Follow-up to #41 (PR #48), which narrowed Stage C discovery to tracked paths. Discovery is narrowed; the execution context is not.
Problem
scripts/gates/structure.sh runs each suite as ( cd "$dir" && python3 -m unittest "$mod" ). python3 -m puts the current working directory at the front of sys.path, so every .py file in the suite directory is importable, tracked or not. A name shadowing a stdlib module, or any module the tracked suite imports, is imported before the real one.
Confirmed empirically during the #41 security review: a tracked plug/skills/demo/scripts/test_demo.py containing import json, plus an untracked plug/skills/demo/scripts/json.py whose first module-level statement writes a sentinel. The untracked file executed, the gate printed PASS python suite and exited 0, and no refusal fired, because the file never matched the test_*.py glob.
The tracked-path control constrains which files are named for execution. It does not constrain what those files import.
Why this is worth fixing
Same threat model as #41: the gate is the test binding in .daedalus/config.json, so an implementer subagent writing <plugin>/skills/<skill>/scripts/os.py into a worktree gets it executed on the next gate run, unstaged and unreviewed. This vector is easier than the one #41 closed, since the payload needs no test_ prefix.
Acceptance Criteria
Notes
Candidates: python3 -P (3.11+), python3 -I, PYTHONSAFEPATH=1, an explicit PYTHONPATH with an isolated interpreter, or executing from a clean checkout of the index (git archive / git worktree). The last also closes the related gap that --cached selects paths while the interpreter reads working-tree content, so unstaged edits to a tracked suite still execute.
Follow-up to #41 (PR #48), which narrowed Stage C discovery to tracked paths. Discovery is narrowed; the execution context is not.
Problem
scripts/gates/structure.shruns each suite as( cd "$dir" && python3 -m unittest "$mod" ).python3 -mputs the current working directory at the front ofsys.path, so every.pyfile in the suite directory is importable, tracked or not. A name shadowing a stdlib module, or any module the tracked suite imports, is imported before the real one.Confirmed empirically during the #41 security review: a tracked
plug/skills/demo/scripts/test_demo.pycontainingimport json, plus an untrackedplug/skills/demo/scripts/json.pywhose first module-level statement writes a sentinel. The untracked file executed, the gate printedPASS python suiteand exited 0, and no refusal fired, because the file never matched thetest_*.pyglob.The tracked-path control constrains which files are named for execution. It does not constrain what those files import.
Why this is worth fixing
Same threat model as #41: the gate is the
testbinding in.daedalus/config.json, so an implementer subagent writing<plugin>/skills/<skill>/scripts/os.pyinto a worktree gets it executed on the next gate run, unstaged and unreviewed. This vector is easier than the one #41 closed, since the payload needs notest_prefix.Acceptance Criteria
.pysibling of a tracked suite that shadows a stdlib or imported module does not execute when the gate runs.case_untracked_python_refusedestablished in Stop the structure gate from executing untracked test files #41).Discovery ruleheader paragraph instructure.shdescribes the execution-context control alongside the discovery control.Notes
Candidates:
python3 -P(3.11+),python3 -I,PYTHONSAFEPATH=1, an explicitPYTHONPATHwith an isolated interpreter, or executing from a clean checkout of the index (git archive/git worktree). The last also closes the related gap that--cachedselects paths while the interpreter reads working-tree content, so unstaged edits to a tracked suite still execute.