Follow-up from PR #95's five-round codex autoreview (gpt-5.6-sol high). Two systemic hardening tracks were consciously deferred from that PR with bounded containments landed instead; this issue owns the full class. Threat model throughout: hostile generated-repo code executing during pipeline phases, trying to reach credentials or the control plane.
1. Trusted-git-config contract for host-side git on agent-mutated repos
Landed in #95 (containment): harnessGit() runs all wrapper-owned git writes with a scrubbed env (no vendor creds, no verify secrets, no GH_TOKEN) + hooks disabled two ways (core.hooksPath=<empty mkdtemp>, --no-verify); the review base fetch uses a trusted URL from GITHUB_REPOSITORY with -c protocol.ext.allow=never -c credential.helper= under the scrubbed env; control-plane asserts run after every harness git write section, closing the mutate-then-credentialed-execute window.
Residual class: agent-writable .git/config / .gitattributes can still define clean/smudge filters, gpg.program, merge/diff drivers etc. that EXECUTE (with scrubbed env, so no secret theft, and any .sandcastle mutation is caught by the asserts — but execution itself is still attacker code running host-side). The complete fix is a designed contract: every host-side git invocation on an agent-mutated repo (wrapper, salvage.sh, push step, recap) runs with a trusted config surface — candidates: GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null plus an explicit -c allowlist, --no-replace-objects, and an audit of which repo-config keys git consults per subcommand. This predates #95 in salvage/push and needs one contract, not per-call patches.
2. Temporal credential scoping for the consolidated loop step (/proc environ)
Codex flagged: the single loop step holds every provider credential + verify secrets in its environment for the whole loop lifetime, vs the old per-step temporal scoping. Analysis recorded on the PR: on GitHub-hosted Ubuntu runners, Yama ptrace_scope=1 denies non-descendant /proc/<pid>/environ reads, so gate/phase-spawned repo code cannot read the wrapper's environ; on the local macOS lane the runner user's credential FILES (~/.codex-afk, grok auth) are directly readable by any repo code anyway, so the environ surface adds nothing material there. Hence deferral.
Worth doing properly anyway (defense in depth + lanes we haven't enumerated): temporal scoping via a bootstrap that stashes credentials off-env before any repo code can run — e.g. the workflow step execs the wrapper (no lingering shell parent with secret environ), the wrapper reads creds into heap and re-launches itself (or its phase children) with sanitized env, creds injected per-phase only for that phase's lifetime. Needs care: Bun/Node have no self-execve; a parent-waits design leaves the parent environ readable; evaluate a tiny exec-wrapper (bash exec env -i ...) or per-phase credential files created/deleted around each spawn (matching the existing codex-home pattern).
Watch item (not this issue's scope, recorded for Tier-1)
Codex re-raised gate-before-verify five times on #95: verify-authored commits (small fixes) land after the Convex gate ran for that attempt. The ordering is spec-locked (skills#56 R1, spec §2); residual risk = _generated divergence surfacing at PR CI instead of in-pipeline. The Tier-1 proof (#85) should watch for exactly this; if it bites in practice, the fix is a spec amendment (cheap post-verify gate re-run on passing attempts), not a silent reorder.
Follow-up from PR #95's five-round codex autoreview (gpt-5.6-sol high). Two systemic hardening tracks were consciously deferred from that PR with bounded containments landed instead; this issue owns the full class. Threat model throughout: hostile generated-repo code executing during pipeline phases, trying to reach credentials or the control plane.
1. Trusted-git-config contract for host-side git on agent-mutated repos
Landed in #95 (containment):
harnessGit()runs all wrapper-owned git writes with a scrubbed env (no vendor creds, no verify secrets, no GH_TOKEN) + hooks disabled two ways (core.hooksPath=<empty mkdtemp>,--no-verify); the review base fetch uses a trusted URL fromGITHUB_REPOSITORYwith-c protocol.ext.allow=never -c credential.helper=under the scrubbed env; control-plane asserts run after every harness git write section, closing the mutate-then-credentialed-execute window.Residual class: agent-writable
.git/config/.gitattributescan still define clean/smudge filters,gpg.program, merge/diff drivers etc. that EXECUTE (with scrubbed env, so no secret theft, and any.sandcastlemutation is caught by the asserts — but execution itself is still attacker code running host-side). The complete fix is a designed contract: every host-side git invocation on an agent-mutated repo (wrapper, salvage.sh, push step, recap) runs with a trusted config surface — candidates:GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/nullplus an explicit-callowlist,--no-replace-objects, and an audit of which repo-config keys git consults per subcommand. This predates #95 in salvage/push and needs one contract, not per-call patches.2. Temporal credential scoping for the consolidated loop step (/proc environ)
Codex flagged: the single loop step holds every provider credential + verify secrets in its environment for the whole loop lifetime, vs the old per-step temporal scoping. Analysis recorded on the PR: on GitHub-hosted Ubuntu runners, Yama
ptrace_scope=1denies non-descendant/proc/<pid>/environreads, so gate/phase-spawned repo code cannot read the wrapper's environ; on the local macOS lane the runner user's credential FILES (~/.codex-afk, grok auth) are directly readable by any repo code anyway, so the environ surface adds nothing material there. Hence deferral.Worth doing properly anyway (defense in depth + lanes we haven't enumerated): temporal scoping via a bootstrap that stashes credentials off-env before any repo code can run — e.g. the workflow step
execs the wrapper (no lingering shell parent with secret environ), the wrapper reads creds into heap and re-launches itself (or its phase children) with sanitized env, creds injected per-phase only for that phase's lifetime. Needs care: Bun/Node have no self-execve; a parent-waits design leaves the parent environ readable; evaluate a tiny exec-wrapper (bashexec env -i ...) or per-phase credential files created/deleted around each spawn (matching the existing codex-home pattern).Watch item (not this issue's scope, recorded for Tier-1)
Codex re-raised gate-before-verify five times on #95: verify-authored commits (small fixes) land after the Convex gate ran for that attempt. The ordering is spec-locked (skills#56 R1, spec §2); residual risk =
_generateddivergence surfacing at PR CI instead of in-pipeline. The Tier-1 proof (#85) should watch for exactly this; if it bites in practice, the fix is a spec amendment (cheap post-verify gate re-run on passing attempts), not a silent reorder.