Path-injection alert disposition¶
Status: issue #264 remediation evidence
Scope: open CodeQL py/path-injection findings present on main in July 2026
Policy¶
ZapTrace separates two path trust boundaries:
- Network and agent surfaces must resolve caller-selected paths through the configured workspace policy or avoid request-derived filesystem paths entirely.
- Low-level SDK APIs may accept an explicit trusted filesystem root when selecting an output or input location is part of the documented library contract.
A CodeQL alert is dismissed only when the sink belongs to the second category or the reported line is the containment sanitizer itself, and regression tests demonstrate the boundary.
Alert-to-control traceability¶
| Alerts | File / sink | Disposition | Control and test evidence |
|---|---|---|---|
| 40, 42, 43, 44, 48, 49, 50 | zaptrace/api/storage.py |
Code fix | REST artifact paths now use server-generated opaque object directories and fixed payload.txt / manifest.json names. Session IDs, filenames, kinds, and delete selectors are compared only as metadata. Existing pre-opaque manifests remain accessible through fixed-root enumeration; new writes use only opaque directories. Covered by tests/test_api_hardening.py, tests/test_object_authorization.py, and tests/test_path_injection_regressions.py. |
| 39 | zaptrace/core/session_store.py |
Code fix | Persistent session and design directories are server-generated opaque identifiers. Session IDs and design names are stored in manifests and matched during fixed-root enumeration. Version filenames are server-generated. Existing pre-opaque session layouts are discovered by fixed-root manifest enumeration without joining the requested session ID into a path. Covered by tests/test_session_store.py and tests/test_path_injection_regressions.py. |
| 4, 5 | zaptrace/agent/tool_impls/runtime.py |
False positive: sanitizer implementation | _validate_path resolves the candidate to a canonical absolute path, requires it to be relative to the resolved workspace root, and returns the canonical path rather than the original selector. Traversal, absolute escape, prefix-sibling, symlink escape, and symlink-swap behavior are covered by tests/test_path_traversal.py, tests/test_fuzz_untrusted_inputs.py, and tests/test_path_injection_regressions.py. |
| 9 | zaptrace/core/parser.py |
False positive: trusted SDK input path | parse_file is a low-level SDK function for a caller-selected trusted path. REST/MCP/agent entry points call _validate_path(..., must_exist=True) before invoking it. The trust boundary is documented in the function docstring and tested through agent path-containment tests. |
| 23, 24, 25, 28, 29 | zaptrace/export/kicad.py |
False positive: trusted SDK output root | The public exporter intentionally accepts a caller-selected trusted output directory. Generated filenames are reduced to a restricted stem, candidates are resolved and checked relative to the resolved output root, and the canonical path is returned. Agent-facing exports validate the output directory against the workspace first. Traversal, suffix rewriting, and symlink-swap behavior are covered by tests/test_path_injection_regressions.py and existing KiCad export tests. |
CI enforcement¶
GitHub CodeQL analysis runs on pull requests and the branch code-scanning check fails when a pull request introduces a new high-severity alert. Security workflow analysis, the PR alert check, and release-gate summary must all pass before merge.
Residual risk¶
- Trusted SDK callers remain responsible for selecting an appropriate parser input path or exporter output root.
- Filesystem containment does not defend against a privileged local attacker who can modify the configured storage root or process environment.
- Persistent storage remains process/file based and is not a multi-process transactional database.