#278

fix(files): contain FileOperationsManager paths inside the sandbox (CWE-22)

Openbharathvemulawants to merge intofrom

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, since Path(base) / "/abs" discards the base).
  • modify_file / delete_file could overwrite or delete arbitrary files — delete_file even shutil.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/True

After 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/True

Also 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.