Skip to content

fix(security): refuse to chmod(parent, 0o700) on / or top-level dirs (#25821)#29670

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-a75fb3fa
May 21, 2026
Merged

fix(security): refuse to chmod(parent, 0o700) on / or top-level dirs (#25821)#29670
teknium1 merged 2 commits into
mainfrom
hermes/hermes-a75fb3fa

Conversation

@teknium1

@teknium1 teknium1 commented May 21, 2026

Copy link
Copy Markdown
Contributor

Salvages @liuhao1024's #25831 — production-confirmed P0 host-bricking bug. If any of the 5 token-storage paths resolves to a top-level dir (e.g. HERMES_HOME=/), os.chmod("/", 0o700) succeeds and strips traversal perms from the root inode, taking out DNS / networking / journald / Docker containers.

Closes #25821.

Summary

New secure_parent_dir(path) helper refuses to chmod when the resolved parent is / or any direct child (/usr, /etc, /var, …) — the depth check (< 3) covers all top-level dirs without needing a hardcoded blocklist. Applied at all 5 known call sites.

Changes

  • hermes_constants.py: secure_parent_dir() helper
  • tools/mcp_oauth.py, hermes_cli/auth.py (3 sites), agent/google_oauth.py: route every os.chmod(parent, 0o700) through the new helper
  • tests/test_hermes_constants.py: 5 unit tests for the helper
  • My follow-up: docstring + whitespace cleanup (no behavior change)

Validation

Before After
HERMES_HOME=/ → chmod / succeeds, bricks host skipped
HERMES_HOME=/etc → chmod /etc succeeds, breaks system skipped
HERMES_HOME=/home/u/.hermes → chmod /home/u/.hermes works works

Targeted tests: 40/40 (hermes_constants), 148/148 (auth + mcp_oauth + google_oauth). E2E with real secure_parent_dir against 8 catastrophic paths (/, /usr/..., /etc/..., /tmp/..., /var/..., /home/...) all correctly skipped; safe nested paths still chmod 0o700.

Other PRs on this issue

Infographic

host-brick-shield

liuhao1024 and others added 2 commits May 20, 2026 22:54
Five call sites do os.chmod(path.parent, 0o700) without checking that
the parent resolves to a safe directory. If HERMES_HOME or another
path env var resolves to /, the chmod strips traversal permission from
the root inode and bricks the entire host.

Add secure_parent_dir() to hermes_constants.py that refuses to chmod
/ or any top-level directory (depth < 2). Replace all 5 call sites
with this helper.

Fixes #25821
- Drop two extra blank lines between display_hermes_home and secure_parent_dir
- Fix docstring saying 'depth < 2' (actual guard is parts < 3)
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-a75fb3fa vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8984 on HEAD, 8984 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4741 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1 teknium1 merged commit 127b56a into main May 21, 2026
17 of 18 checks passed
@teknium1 teknium1 deleted the hermes/hermes-a75fb3fa branch May 21, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: os.chmod(path.parent, 0o700) bricks host when path.parent resolves to /

2 participants