FileOperationsManager built every path as self.base_dir / filename with no validation, so the /api/files/* endpoints allowed path traversal (CWE-22) outside the /tmp/hexstrike_files sandbox, as reported in #135:
create_file("../../x")wrote arbitrary files outside the sandbox (absolute paths escaped too, sincePath(base) / "/abs"discards the base).modify_file/delete_filecould overwrite or delete arbitrary files —delete_fileevenshutil.rmtrees directories.list_files("..")listed arbitrary directories.- A symlink inside the sandbox pointing outside was followed.
Fix: one helper, _resolve_sandbox_path(), used by all four methods. It resolves the candidate path (following .. segments, absolute paths, and symlinks) and raises ValueError unless the result stays under the resolved base_dir. The existing methods already convert exceptions into {"success": false, "error": ...} responses, so callers now get a clean error instead of an escape. __init__ is untouched, so this does not conflict with the open Windows-portability PRs (#193/#195/#228), which only change the default base_dir. (An earlier attempt, #159, was closed unmerged by its author; the issue is still open and the code on master is unchanged.)
Evidence (repo has no test suite; verified with a harness that imports the real hexstrike_server module and drives FileOperationsManager against a scratch sandbox, on master d689933):
Before the fix — all escapes succeeded:
BUG create_file('../escape_create.txt'): ESCAPED sandbox
BUG modify_file('../victim_modify.txt'): ESCAPED sandbox
BUG delete_file('../victim_delete.txt'): ESCAPED sandbox
BUG list_files('..'): ESCAPED sandbox
BUG create_file('<absolute path>'): ESCAPED sandbox
OK controls: create/modify/list/delete inside sandbox -> True/True/True/TrueAfter the fix — all escapes blocked, controls unchanged:
OK create_file('../escape_create.txt'): blocked / contained
OK modify_file('../victim_modify.txt'): blocked / contained
OK delete_file('../victim_delete.txt'): blocked / contained
OK list_files('..'): blocked / contained
OK create_file('<absolute path>'): blocked / contained
OK controls: create/modify/list/delete inside sandbox -> True/True/True/TrueAlso verified after the fix: a create through a symlinked subdirectory pointing outside the sandbox is blocked, and via the Flask test client POST /api/files/create with ../ returns success: false with no file written outside, while a normal in-sandbox create still succeeds. python -m py_compile hexstrike_server.py passes.
Fixes #135.
Prepared with AI assistance; the reproduction harness and before/after output above were run locally against master and this branch.