fix(security): guard os.chmod(parent) against / and top-level dirs

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
This commit is contained in:
liuhao1024 2026-05-15 00:28:39 +08:00 committed by Teknium
parent 3bbe980115
commit 4ead464f97
5 changed files with 127 additions and 22 deletions

View file

@ -48,6 +48,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from typing import Any
from urllib.parse import parse_qs, urlparse
from hermes_constants import secure_parent_dir
logger = logging.getLogger(__name__)
@ -175,10 +176,8 @@ def _write_json(path: Path, data: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
# Tighten parent dir to 0o700 so siblings can't traverse to the creds.
# No-op on Windows (POSIX mode bits aren't enforced); ignore failures.
try:
os.chmod(path.parent, 0o700)
except OSError:
pass
# secure_parent_dir refuses to chmod / or top-level dirs (#25821).
secure_parent_dir(path)
# Per-process random suffix avoids collisions between concurrent
# writers and stale leftovers from a prior crashed write.
tmp = path.with_suffix(f".tmp.{os.getpid()}.{secrets.token_hex(4)}")