mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
In containers without unprivileged user namespaces every Codex command fails, yet the review exits 0 and concludes 'no findings' (captured in test/fixtures/codex-sandbox/). The validator now takes execution evidence: 'codex exec --json' command events and the 'codex review' stderr transcript (a command that succeeded proves execution); sandbox failure lines on stderr or in failed command output return unavailable(sandbox_unavailable); exact 'could not run commands / read the diff' phrases are the last fallback. Review text that merely discusses bwrap or namespaces still passes. A free 'codex sandbox ... true' preflight (Linux, 10 s) runs before the paid model probe and every automatic outside review; CODEX_MODE gains sandbox_unavailable, and an inconclusive model probe reports unverified instead of ready (exit code kept for stale callers). Every Codex site (outside-voice resolver, /codex review, challenge, consult) takes its sandbox from _GSTACK_CODEX_SANDBOX and pipes its result through the validator; a static test parses rendered command positions for both. GSTACK_CODEX_NO_SANDBOX=1 (exactly 1, shell env only) switches to danger-full-access with a warning on every use. Sandbox pins in existing tests move from the literal read-only to the selected-sandbox variable; captured autoplan probes are judged against the probe block they executed.
Codex sandbox fixtures (B1)
Captured from codex-cli 0.160.0 (musl x86_64 vendor binary). They pin what
lib/outside-review-result.ts and _gstack_codex_sandbox_preflight classify.
| File | Capture | Exit |
|---|---|---|
sandbox-userns-denied.stderr |
codex sandbox -c 'sandbox_mode="read-only"' true in Docker ubuntu:24.04, default seccomp profile, apt bubblewrap installed |
1 |
sandbox-bwrap-missing.stderr |
same command without bubblewrap installed (Rust panic) |
101 |
exec-json-userns-denied.jsonl |
echo "Run cat a.txt and tell me its content in one word. If the command fails, say exactly what failed." | codex exec - -s read-only -c 'model="gpt-5.4-mini"' -c skills.include_instructions=false --json in the same container (stderr empty) |
0 |
review-userns-denied.stdout / .stderr |
codex review --base master -c 'model="gpt-5.4-mini"' -c 'review_model="gpt-5.4-mini"' -c 'sandbox_mode="read-only"' </dev/null in the same container, on a branch with one changed line |
0 |
review-healthy.stdout / .stderr |
the same review on a host with user namespaces enabled | 0 |
exec-json-healthy.jsonl |
echo 'Run \cat a.txt` and tell me its content in one word.' | codex exec - -s read-only ... --json` on the same host |
0 |
review-mentions-sandbox.txt |
constructed: a real review that discusses bwrap, namespaces, landlock and seccomp (must still pass) | - |
Both failing runs exit 0 and the review's own text concludes "there are no
findings to report", which is why execution evidence (stderr transcript,
--json command events) decides before any review text is trusted.
Reproduce the container captures:
docker run --rm -v <codex vendor bin dir>:/cx:ro -v "$CODEX_HOME":/codexhome -e CODEX_HOME=/codexhome ubuntu:24.04 sh -c \
'apt-get update -qq && apt-get install -y -qq bubblewrap git; PATH=/cx:$PATH codex sandbox -c "sandbox_mode=\"read-only\"" true; echo $?'
Omit bubblewrap from the install for the exit-101 capture. Host paths in the
healthy captures are normalized to /repo, and lines written by the capture
machine's git wrapper are removed.