feat: Poka-yoke — prevent hardcoded home-directory paths (closes #921)
Some checks failed
Contributor Attribution Check / check-attribution (pull_request) Failing after 49s
Docker Build and Publish / build-and-push (pull_request) Has been skipped
Nix / nix (ubuntu-latest) (pull_request) Failing after 6s
Supply Chain Audit / Scan PR for supply chain risks (pull_request) Successful in 25s
Tests / e2e (pull_request) Successful in 4m26s
Tests / test (pull_request) Failing after 59m48s
Nix / nix (macos-latest) (pull_request) Has been cancelled

Three-layer defense against latent /Users/<name>/ and ~/ path defects:

1. Runtime guard (tools/path_guard.py):
   - validate_path() catches /Users/<name>/, /home/<name>/ in tool args
   - Allows current HOME prefix (expanduser output is safe)
   - Wired into write_file_tool() and execute_code()

2. Pre-commit hook (hooks/pre-commit-path-guard.py):
   - Scans staged .py files for hardcoded path patterns
   - Blocks commit with actionable error message
   - # noqa: hardcoded-path-ok escape hatch for legitimate cases

3. CI lint (scripts/lint_hardcoded_paths.py):
   - Scans directory tree for violations
   - --fix flag shows remediation suggestions
   - Skips test dirs, __pycache__, venv

4. 21 tests (tests/test_path_guard.py):
   - Runtime validation (valid/invalid paths, batch, edge cases)
   - Static scanning (clean files, violations, noqa, comments)
   - Directory scanning (tree traversal, skip rules)

Existing violations annotated with # noqa: hardcoded-path-ok where
legitimate (config defaults, display strings, test fixtures, skills).
This commit is contained in:
Alexander Whitestone
2026-04-21 07:37:14 -04:00
parent 05f8c2d188
commit 5dcb90531b
16 changed files with 424 additions and 267 deletions

View File

@@ -916,6 +916,25 @@ def execute_code(
if not code or not code.strip():
return tool_error("No code provided.")
# Poka-yoke: scan code for hardcoded home-directory paths
try:
from tools.path_guard import scan_file_for_violations
import tempfile as _tf
_tmp = _tf.NamedTemporaryFile(mode="w", suffix=".py", delete=False)
_tmp.write(code)
_tmp.close()
_violations = scan_file_for_violations(_tmp.name)
os.unlink(_tmp.name)
if _violations:
_msgs = [f"Line {ln}: {line[:60]} ({pat})" for ln, line, pat, _ in _violations]
return tool_error(
f"Code contains hardcoded home-directory path(s):\n " +
"\n ".join(_msgs) +
"\nUse os.environ['HOME'], os.path.expanduser('~'), or get_hermes_home()."
)
except Exception:
pass # Guard is best-effort; don't block execution on scan failure
# Dispatch: remote backends use file-based RPC, local uses UDS
from tools.terminal_tool import _get_env_config
env_type = _get_env_config()["env_type"]