fix(agent-team): post-build denied-path check writes scratch outside the checkout

Live smoke exposed a self-pollution bug (pre-existing from PR #17): the post-build
denied-path check wrote its own _build_diff_z.bin / _build_status_z.bin into the
working tree, then its own `git status --untracked-files=all` flagged them as
out-of-scope writes — failing any run with a narrow declared_scope (the smoke's
docs/**). Write them to $RUNNER_TEMP instead (read via $_DIFF_Z/$_STATUS_Z), so
the check no longer sees its own temp files. The agent-team pytest artifacts were
already correctly gitignored; only the check's own files tripped it. Test harness
updated to pass the env paths. 1044 tests, ruff clean.
This commit is contained in:
Adam Moussa 2026-06-22 19:18:47 -04:00
parent 5756178d62
commit 2dce5c6b76
2 changed files with 23 additions and 7 deletions

View file

@ -742,8 +742,15 @@ jobs:
# mangling is done anywhere. A path that cannot be cleanly decoded is # mangling is done anywhere. A path that cannot be cleanly decoded is
# treated as a VIOLATION (fail closed). # treated as a VIOLATION (fail closed).
git config core.quotepath false git config core.quotepath false
git diff -z --name-only HEAD > ./_build_diff_z.bin || true # Write the scratch capture files OUTSIDE the checkout ($RUNNER_TEMP) so
git status --porcelain=v1 -z --untracked-files=all > ./_build_status_z.bin || true # the `git status --untracked-files=all` below does not see — and flag —
# the check's OWN temp files as out-of-scope writes (they would otherwise
# appear as untracked and fail a narrow declared_scope).
_DIFF_Z="${RUNNER_TEMP:-/tmp}/_build_diff_z.bin"
_STATUS_Z="${RUNNER_TEMP:-/tmp}/_build_status_z.bin"
export _DIFF_Z _STATUS_Z
git diff -z --name-only HEAD > "$_DIFF_Z" || true
git status --porcelain=v1 -z --untracked-files=all > "$_STATUS_Z" || true
python3 - <<'PY' python3 - <<'PY'
from __future__ import annotations from __future__ import annotations
@ -885,7 +892,7 @@ jobs:
"""`git diff -z --name-only` records: each NUL field is one path.""" """`git diff -z --name-only` records: each NUL field is one path."""
ok: list[str] = [] ok: list[str] = []
bad: list[bytes] = [] bad: list[bytes] = []
for rec in _read_z("./_build_diff_z.bin"): for rec in _read_z(os.environ["_DIFF_Z"]):
dec = _decode(rec) dec = _decode(rec)
(ok if dec is not None else bad).append(dec if dec is not None else rec) (ok if dec is not None else bad).append(dec if dec is not None else rec)
return ok, bad return ok, bad
@ -901,7 +908,7 @@ jobs:
""" """
ok: list[str] = [] ok: list[str] = []
bad: list[bytes] = [] bad: list[bytes] = []
recs = _read_z("./_build_status_z.bin") recs = _read_z(os.environ["_STATUS_Z"])
i = 0 i = 0
while i < len(recs): while i < len(recs):
rec = recs[i] rec = recs[i]

View file

@ -390,9 +390,18 @@ def _run_post_build(
script = tmp_path / "post_build.py" script = tmp_path / "post_build.py"
script.write_text(_extract_post_build_script(), encoding="utf-8") script.write_text(_extract_post_build_script(), encoding="utf-8")
(tmp_path / "_build_diff_z.bin").write_bytes(diff_z) diff_z_path = tmp_path / "_build_diff_z.bin"
(tmp_path / "_build_status_z.bin").write_bytes(status_z) status_z_path = tmp_path / "_build_status_z.bin"
env = dict(os.environ, DECLARED_SCOPE=scope) diff_z_path.write_bytes(diff_z)
status_z_path.write_bytes(status_z)
# The script now reads its capture files from $_DIFF_Z / $_STATUS_Z (written
# outside the checkout in CI so the check's own temp files are not flagged).
env = dict(
os.environ,
DECLARED_SCOPE=scope,
_DIFF_Z=str(diff_z_path),
_STATUS_Z=str(status_z_path),
)
result = subprocess.run( result = subprocess.run(
[sys.executable, str(script)], [sys.executable, str(script)],
env=env, env=env,