Skip to content

afk-pipeline: harden host-side git trust + temporal credential scoping for the loop step (deferred from #95 review) #97

Description

@JorgeMenaDev

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions