Repository navigation
security(sandbox): deny credential reads across native stages - #370
Conversation
|
Implemented #294 at Credential denials now share canonical path handling across Codex, Claude and preparation. Linux preparation uses srt for both executors; macOS/Windows retain the aligned Codex fallback pending platform validation. Explicit project exceptions are validated and recorded. Owning documentation and regression coverage are included. Verification, agent-reported:
The live native E7 boundary remains Checks read once for
Routing supplied by runtime: profile |
[CCR-20261030-001] · senior_reviewer · anthropic/claude-opus-5-5→? · high · schema:2Initial review — PR #370 (work item #294)Verdict: CHANGES_REQUESTED at Summary and scopeReviewed the full accumulated diff The core of #294 is sound. Findings[REV-001] · high · open · - · blocks:yes — Claude sandbox now forced on every stage, including inactive (non-GitHub) policies, without the bwrap preflight or static-only fallbackPaths:
The guard was not widened with it. The Linux bwrap/socat functional probe and the switch to
Evidence: Expected: either keep the earlier emission condition and add credential [REV-002] · medium · open · - · blocks:no — Issue-review stages lose gh credentials by default; Claude case undocumented and #279 comment now stalePaths:
Code inspection only; not reproduced against a live Security triageThe deterministic rule did not fire: Result: no confirmed vulnerability. The denials narrow access and widen nothing for GitHub stages, and the opt-out is a closed list validated in Commands executed
Unverified
CI (head
|
| Check | State |
|---|---|
| Skills, manifests, and Bash installer | fail (5m28s) |
| Windows test shard (windows-2) | fail (8m24s) |
| Windows test shard (windows-1) | pending |
| Windows test shard (windows-3) | pending |
| Windows test shard (windows-4) | pending |
Two required checks already fail on this head. The cycle's CI gate decides on them; they are not counted as review findings.
Residual risks
- Stores outside security(sandbox): deny credential reads in every stage and preparation sandbox; one preparation sandbox tool #294's list stay readable to sandboxed commands:
~/.git-credentials,~/.netrc,~/.npmrc,~/.docker/config.json,~/.config/bitbucket/*. - On WSL2, a key reachable through a link inside
~/.ssh(for example/mnt/c/Users/*/.ssh) is still readable at its target path, because only the~/.sshdirectory itself is masked. - Publishing stages keep
ghcredentials, so a writable token remains reachable from verification code (accepted, tracked in Make provider capabilities first-class and close 0.6.0 workflow gaps #238).
Project review verdict: CHANGES_REQUESTED — REV-001 blocks approval.
[CCR-20261030-001] · senior_reviewer · anthropic/claude-opus-5-5→? · high · schema:2[CCT-20261030-001] · deep_coder · openai/gpt-6.1-sol→? · high · triaged:50e5b985066f7986e585a9fcd7918c521499925f · schema:2BLOCKED — both review findings are resolved; the separate supplied CI failure cannot be diagnosed because this stage's sandbox blocks its log endpoints. Fix commit: [REV-001] · high · resolved · valid · blocks:yes — Claude sandbox now forced on every stage, including inactive (non-GitHub) policies, without the bwrap preflight or static-only fallbackRestored the previous Claude sandbox scope: active GitHub policies requiring confined review verification, registry domains, publication commands or Docker. Inactive Bitbucket/Plane/Jira policies retain their earlier tooling and no longer receive sandbox configuration refusals.
Regression coverage verifies inactive providers, ordinary issue review, Docker-only activation, inherited [REV-002] · medium · resolved · valid · blocks:no — Issue-review stages lose gh credentials by default; Claude case undocumented and #279 comment now staleCorrected the #279 comment to distinguish network access from credential access. Issue-review configuration now documents both executors: Codex denies stored gh credentials in this non-publishing role; ordinary Claude issue review retains earlier tooling, while sandboxed Claude issue review also needs Updated the owning pages Verification (agent-reported)
Fixtures use synthetic credential homes and their own Git identity. Local installed tools, user configuration and OS differ from CI. Unit/fake-process checks do not establish kernel confinement; preparation evidence is separate from locally executed tests. Live E7 remains Post-change review and securityDelegated Reviewed activation, inherited overrides, credential scope, inactive providers and static-only propagation. Actual OS isolation and installed Claude settings merging remain unverified. Inactive Claude policies intentionally retain their earlier tooling and credential access, as documented. Separate supplied CI failure — unresolved diagnosisThe supplied failure is on While that SHA was still the current PR head, these read-only attempts ran (each with the repository's
Checks of the pushed head — one read, parallel policyHead
The downstream PowerShell installer had not yet been reported. No remote-CI wait or in-stage CI fix round ran. These new checks do not establish the supplied old-head failure's cause. |
Linux dependency preparation now runs srt for both executors, so the preparation tests resolved the host's srt and failed on a runner without it. Patch the resolver with a fake srt by default; the managed-rg test keeps the real resolver because it supplies its own bundle. The emulated sandbox in the confinement-probe test compared canonical paths with unresolved table roots, so a temporary root reached through a symlink or a Windows 8.3 short name denied the clone and TMPDIR writes. Compare against resolved roots and cover a linked temporary root. Refs #294
[CCR-20261030-001] · senior_reviewer · anthropic/claude-opus-5-5→? · high · schema:2[CCT-20261030-001] · deep_coder · openai/gpt-6.1-sol→? · high · triaged:50e5b985066f7986e585a9fcd7918c521499925f · schema:2[CCT-20261030-002] · deep_coder · anthropic/claude-opus-5-5→? · high · triaged:b06504281eae89664a8305727ab6e5142ee41349 · schema:2RESOLVED. This attempt resumes the earlier resolve round, which was BLOCKED. Both review findings stay resolved and were re-verified at the new head. The CI failures on [REV-001] · high · resolved · valid · blocks:yes — Claude sandbox now forced on every stage, including inactive (non-GitHub) policies, without the bwrap preflight or static-only fallbackFixed in [REV-002] · medium · resolved · valid · blocks:no — Issue-review stages lose gh credentials by default; Claude case undocumented and #279 comment now staleFixed in CI failures on
|
| Command | Result |
|---|---|
python3 -m unittest discover -s tests |
exit 0; Ran 2108 tests in 213.734s, OK (skipped=1); the skip is the host-only live E7 matrix |
python3 scripts/validate-package.py |
exit 0; VALID: 16 skills (7 cycle, 9 supporting), shared sections in sync, cross-agent manifests aligned |
python3 -m unittest |
exit 0; Ran 0 tests from the repository root, so not evidence |
python3 -m unittest discover -s tests -p test_credential_reads.py |
before the fix: exit 1 (the new linked-root test fails); after: exit 0, 11 tests, 1 skip |
python3 -m unittest discover -s tests -p test_dependency_setup.py |
with srt simulated absent: exit 1 (14 failures, 4 errors); after the fix: exit 0, 29 tests |
git diff --check |
exit 0 |
What the local run shares with CI: the same commands, synthetic credential homes, and fixtures that set their own Git identity. What it does not share: the operating system (WSL2 Linux here, not the self-hosted runner or Windows), the installed tools, and the temporary path layout.
The live E7 boundary is not_verified. Nested Codex/srt is unsupported inside this native stage, so real OS confinement was not exercised. macOS and Windows confinement remain unverified. Verification conclusion: not_verified, boundary required. The local results above still stand.
Post-change review and security
The delta base is the trusted review receipt 50e5b985066f7986e585a9fcd7918c521499925f, and it is an ancestor of the head. A delegated cc-code-review covered the complete delta from that base, including this attempt's test changes. It found no P0 or P1. It confirmed two lower-severity items in the earlier, already-pushed delta. They are recorded as residual risks below, not fixed in this round.
No changed path matches the mandatory security minimum, and the PR has no sensitive labels. This round changed tests only. The earlier attempt audited the credential and confinement code paths in b065042.
Residual risks
- P2:
credential_read_policyin the dispatch evidence (scripts/executors.py:1293) lists denied paths andgh_readable: falseeven for Claude stages that get no Bash sandbox, such as ordinary issue review or a review without verification commands. In those stages the denials are not enforced. The same misreport already existed for inactive and static-only policies. A follow-up should report enforcement from the final policy. - P3: for a Claude review that has no verification commands, the sandbox comes into scope only when preparation adds registry domains. An inherited
sandbox.filesystem.allowReadis therefore refused after preparation has run, not before (scripts/executors.py:1433). The refusal is correct; only setup time is wasted. - Stores outside the security(sandbox): deny credential reads in every stage and preparation sandbox; one preparation sandbox tool #294 list stay readable, the WSL2 linked-key case remains, and publishing stages keep gh credentials (Make provider capabilities first-class and close 0.6.0 workflow gaps #238). All three are as stated in the review.
Checks of the pushed head (one read, parallel policy)
Head 533ab04a38e14e2f017ff307303fd595275858d8: 0 passed, 0 failed, 0 pending. No checks were registered yet at the single read straight after the push. No remote-CI wait or in-stage CI fix round ran.
Routing supplied by runtime: profile deep_coder, requested model anthropic/claude-opus-5-5, effort high, resolved model ?.
[CCR-20261030-001] · senior_reviewer · anthropic/claude-opus-5-5→? · high · schema:2[CCT-20261030-001] · deep_coder · openai/gpt-6.1-sol→? · high · triaged:50e5b985066f7986e585a9fcd7918c521499925f · schema:2[CCT-20261030-002] · deep_coder · anthropic/claude-opus-5-5→? · high · triaged:b06504281eae89664a8305727ab6e5142ee41349 · schema:2[CCR-20261030-002] · senior_reviewer · anthropic/claude-opus-5-5→? · high · schema:2Rereview — PR #370 (work item #294)Verdict: APPROVED at ScopeI rereviewed the whole accumulated diff Changes since the initial review (
Previous findings[REV-001] · high · resolved · valid · blocks:yes — Claude sandbox now forced on every stage, including inactive (non-GitHub) policies, without the bwrap preflight or static-only fallbackCorrected, and verified by execution. A pre-existing gap is also closed. When preparation adds registry domains, the sandbox becomes active late; the final policy is now probed and checked again before emission ( The relevant tests pass in
[REV-002] · medium · resolved · valid · blocks:no — Issue-review stages lose gh credentials by default; Claude case undocumented and #279 comment now staleCorrected. The comment at Ordinary Claude issue review keeps its earlier tooling, which New findings[REV-003] · low · open · - · blocks:no — Dispatch evidence reports credential denials that a Claude stage without a Bash sandbox does not enforcePaths:
Found by code inspection: the note is built unconditionally, while emission is gated by Security triageThe deterministic rule did not fire. Result: no confirmed vulnerability. Compared with Verification (executed in this rereview)
Unverified:
CI (head
|
| Check | State |
|---|---|
| Skills, manifests, and Bash installer | pass |
| Windows test shard (windows-1) | pass |
| Windows test shard (windows-2) | pass |
| Windows test shard (windows-3) | pass |
| Windows test shard (windows-4) | pass |
| PowerShell installer | pass |
Six passed, none failed, none pending. These are the same jobs that failed on 50e5b98 and b065042, so the test-only commit 533ab04 resolved the CI failures that had no logs.
Residual risks
- Stores outside security(sandbox): deny credential reads in every stage and preparation sandbox; one preparation sandbox tool #294's list stay readable to sandboxed commands:
~/.git-credentials,~/.netrc,~/.npmrc,~/.docker/config.json,~/.config/bitbucket/*. - On WSL2, a key reached through a link inside
~/.sshstays readable at its target path. - Publishing stages keep gh credentials (Make provider capabilities first-class and close 0.6.0 workflow gaps #238).
- The late inherited-
allowReadrefusal happens after preparation (the resolver's P3). Correct, but setup time is wasted.
Project review verdict: APPROVED. No open finding blocks approval, and every check of the head passed.
Implements #294.
Native stage commands now deny SSH, AWS, Codex and Claude credential-store reads. Publishing stages retain gh access; other stages and dependency preparation deny it. Existing symlink targets resolve before masking and absent paths are omitted. Explicit
code_cycle.user_choices.credential_readsexceptions are validated, carried from trusted source configuration, and recorded in stage/preparation evidence.Linux/WSL2 dependency preparation uses installed
srtfor both executors, including user-local prerequisite recovery. macOS/Windows retain the Codex preparation fallback with matching denials until their srt path is validated. Sandbox startup failures retain the existing unverified/static recovery behavior and never retry preparation without confinement. Inherited ClaudeallowReadsettings are refused because they could cancel the new denials.Diagnosis
Classification:
feature_request; decision:implement. The source confirmed the issue's mechanism: Codex stage profiles and Claude settings omitted credential denials, while preparation applied only a partial list in its srt branch. The invariant is that sandboxed commands cannot read credential stores except for explicitly authorized project exceptions and publishing gh access. This shared policy now owns path resolution and denial selection across the boundaries.No reproduction read real credentials. The owner supplied H-03/H-05 boundary evidence on 2026-10-30 and explicitly authorized L-1/L-2. Related-work search covered linked issues, all-state credential/sandbox issue titles, and merged PRs matching those terms. Related #238 and #291 remain separate work; #279, #284, #288 and #289 provide the existing preparation, transport, recovery and permission lifecycle. This change does not implement runtime-owned publication or host execution of checks.
Work units
scripts/{stage_permissions,executors,dependency_setup,native_prerequisites,user_choices}.py; owning docs; regression tests; Windows shard inventoryVerification (agent-reported)
python3 -m unittest discover -s tests— exit 0; 2,102 tests, one host-only live-matrix skip; 296 seconds.python3 scripts/validate-package.py— exit 0; 16 skills valid, shared sections and manifests aligned.python3 -m unittest discover -s tests -p test_credential_reads.py— exit 0; ten tests, one live-matrix skip.git diff --check— exit 0.basedpyright dependency_setup.py executors.py native_prerequisites.py stage_permissions.py user_choices.py(fromscripts/) — exit 1; existing diagnostics, as described below.The focused credential suite exercises ten tests, with the host-only live matrix skipped. The installed Codex confinement probe and srt prerequisite probe now check synthetic credential stores; the opt-in live E7 matrix is documented in
docs/verification.md. It was not launched in this native stage because nested Codex/srt execution is unsupported. No live sandbox pass is claimed. macOS and Windows validation remains pending, so their Codex preparation fallback remains.Scoped BasedPyright completed with existing diagnostics (105 errors; baseline snapshot 106). It is not a clean type-check pass. No unrelated legacy diagnostics or type-check settings were changed. Fixtures set their own Git identity and use synthetic credential homes where exact profile contents matter; unit/fake-process checks do not establish OS isolation. CI can complement the local suite but is separate evidence.
Documentation
This visible behavior was not previously documented. Updated
docs/host-permissions.md,docs/dependency-preparation.md,docs/configuration.mdanddocs/verification.mddescribe the stores, exception key, evidence, platform fallback and live verification procedure. No skill or product entry point was added.Routing supplied by runtime: profile
deep_coder, requested modelopenai/gpt-6.1-sol, effortmedium, resolved model?.Closes #294