Skip to content

feat(pr-verify): give the capture's driven session Bash - #13746

Merged
kyle-sexton merged 5 commits into
mainfrom
feat/13711-capture-driven-bash
Nov 6, 2026
Merged

kyle-sexton merged 5 commits into
mainfrom
feat/13711-capture-driven-bash

Conversation

@kyle-sexton

Copy link
Copy Markdown
Member

Closes #13711

Summary

The PR capture's driven session gets Bash, so a plugin hook that fires on Bash tool events is now driven, not only recorded. The launch shape is the one runner probe 42248541058 (job capture (no-scrub, null, 0)) ran and passed: no --restricted, no CLAUDE_CODE_SUBPROCESS_ENV_SCRUB, permissions.defaultMode: "default" and allow: ["Bash"]. Probe evidence and decision: issuecomment-6689445063.

Fix

  • driver.mjs: a new --in-sandbox flag launches with IN_SANDBOX_FLAGS (--strict-mcp-config only) and skips the scrub line. Launches without the flag keep --restricted and the scrub, since local attended drives rely on them (features/skills/invoke/README.md:37).
  • broker.mjs: takes --in-sandbox at start and adds it to every launch, ready and log request. A request cannot set it, because the strict parse refuses it. sibling-init.sh starts the broker with it.
  • pr-verify-capture.yml: the driven settings drop the scrub and allow Bash. The outer key-holding leg keeps CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: "1" and --restricted.
  • Docs updated wherever they said the driven session has no Bash: the control skill hub, the hooks and skill-quality feature READMEs, and the harness-ops control-claude-code SKILL.md.
  • Why dropping the scrub is safe here: with it on, every driven Bash call failed (bwrap: Creating new namespace failed: nesting depth or /proc/sys/user/max_*_namespaces exceeded (ENOSPC), run 42247376450). With it off, Bash sees only the sandbox's dummy key (sibling-init.sh:104), while the real key stays with the key proxy. The probe's driven Bash showed sentinel_bash=0 and curl/node egress refused.

Verification

  • workflow.test.mjs 77/77. Driver and broker node --test 104/104, driver.test.sh 79/79, broker.test.sh 25/25, markdownlint clean (worker run). A new test checks that the driven settings carry no scrub and allow Bash, that the outer leg keeps the scrub, and that sibling-init.sh passes --in-sandbox.
  • The probe changed the installed driver with sed. This PR reaches the same argv and env through the flag, which matches by construction but has not been re-measured. No live capture can run on the new code yet: the capture installs the driver from .base (main), and the PR_VERIFY key is out of promotional credit (perf(pr-verify): set the capture key proxy's token-cap from a full measured capture #13724).

Related

🤖 Generated with Claude Code

kyle-sexton and others added 3 commits November 6, 2026 11:17
The broker now takes --in-sandbox, which sibling-init.sh passes, and adds
it to every driver request. Under it, the driver launches Claude Code
without --restricted, so Bash is in the default tools, and sets no
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB. The scrub's nested PID-namespace bwrap
cannot start inside the sibling sandbox (ENOSPC). The driven managed
settings drop the scrub and allow Bash with defaultMode "default". This
is the launch shape of probe run 42248541058 (no-scrub variant). Local
drives keep --restricted and the scrub, and the outer leg, which holds
the real key, keeps its scrub.

Refs: #13711

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…andbox launch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton kyle-sexton added the do-not-merge Hard merge gate: do not merge while applied. label Nov 6, 2026
The driver dropped --restricted, and the scrub of a kept CI key, for any
launch that passed --in-sandbox, so a local drive could start Claude Code
with Bash beside a real credential. buildLaunch now refuses the flag
unless the launch keeps an unattended ANTHROPIC_API_KEY starting with
ccd-dummy-, the key the launch broker hands its driver child. A pin test
keeps the driver's prefix equal to the broker's KEY_PREFIX.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-06T16:53:21.235879Z
We couldn’t complete this review. Comment @codex review to try again.
b2a80de Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

claude-review reviewed b2a80def69a1d0fe9edc1b1e79eac49c8df17741: read 12 of 12 changed file(s); 0 finding(s), 0 as inline comments.

Code-review lane: no findings. I read every hunk of all 12 changed files in the full diff. I also read the unchanged code the changed lines call: buildLaunch, launchConfig, serve, launch and printTarget in driver.mjs, driverArgs and startConfig in broker.mjs, and the scriptOf, capStep and refuses test helpers.

What I checked

  • The flag reaches each launch path. --in-sandbox gets from the broker's start options to every driver verb. driverArgs appends it after --unattended. In the driver, launchConfig reads it, launch forwards it to the serve holder's argv, and serve rebuilds the launch with it. ready and log reach it through printTarget → runPrint → buildPrint.
  • A request cannot ask for the flag. The broker's strict parse rejects it: the new test expects Unknown option. A brokered client that types --in-sandbox is refused rather than silently honoured.
  • The key guard and the scrub agree. buildLaunch refuses --in-sandbox unless the kept unattended credential is an ANTHROPIC_API_KEY that starts with ccd-dummy- and is longer than the prefix. It drops the scrub only on that path. SANDBOX_KEY_PREFIX is pinned equal to the broker's KEY_PREFIX by a test.
  • The workflow change is internally consistent. The driven managed settings drop CLAUDE_CODE_SUBPROCESS_ENV_SCRUB and add defaultMode: "default" and allow: ["Bash"]. The outer key-holding leg (pr-verify-capture.yml:357-366) keeps the scrub. The new workflow.test.mjs test checks both sides by running the jq filter and reading the settings: block, and checks that sibling-init.sh passes --in-sandbox.
  • Closing issue test(pr-verify): probe Bash for the capture's driven session in the sibling sandbox #13711. The probe criterion is met by the probe run recorded on the issue (author-claimed; I did not re-run it). The "capture enables Bash for the driven session" criterion is met by the settings, broker and driver changes, and the new tests in workflow.test.mjs, broker.test.mjs and driver.test.mjs would fail if it broke.
  • Release note. The changelog fragment's bump: minor fits a new opt-in flag that leaves existing launches unchanged. No plugin.json or marketplace.json is changed, so I made no manifest checks.

Verification. All test results in the PR body (77/77, 104/104, 79/79, 25/25) are author-claimed. I did not run any tests: this lane has no shell. The PR says the launch shape matches the probe by construction but has not been re-measured live.

Out of scope. I scoped security out of this lane because the repository runs a separate security-review lane. Whether the trust boundary is acceptable without the scrub and --restricted is a question for that lane and for the owner decision recorded on #13711.

Spend. The driven session can now run Bash tool calls, which may lengthen capture runs and spend more of the PR_VERIFY key's prepaid API credit within the proxy's existing token cap. This is not a hold.

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

claude-security-review reviewed b2a80def69a1d0fe9edc1b1e79eac49c8df17741: read 12 of 12 changed file(s); 0 finding(s), 0 as inline comments.

Security review: PR #13746 (give the capture's driven session Bash)

No security issues found. I read all 12 changed files in full. This was a full review, not blocking-only: the lane had reviewed no earlier head.

critical-findings: 0

What I checked

1. Who reaches the change. Pull request code reaches it. Its plugin hooks and skills run in the driven session, which now has Bash and no subprocess scrub. The new settings are in pr-verify-capture.yml:479: permissions: {disableBypassPermissionsMode: "disable", defaultMode: "default", allow: ["Bash"]}, and CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is no longer set.

2. Whether it widens what that code can do. It does not. Plugin hooks already ran any code they liked inside the sibling sandbox, since allowManagedHooksOnly is unset. Model-driven Bash runs under the same boundary with the same reach. That boundary is set in pr-verify-capture.yml#L568-L593 and this diff leaves it unchanged:

  • Environment: env -i with only PATH, HOME, TERM, LANG and CCD_RUNTIME_DIR.
  • Isolation: --unshare-net --unshare-pid --unshare-user --disable-userns --cap-drop ALL.
  • Files: the root file system is read-only, $HOME and /run are tmpfs, and only capture-run and the runtime directory are writable.

The only credential inside is the broker's random ccd-dummy- key (sibling-init.sh#L104). The scrub strips credentials from a subprocess's environment, and there is no real one there to strip. The "Check the sibling sandbox" step still fails the job before any pull request code runs if a credential, the sentinel, egress, a user namespace or an unexpected socket is reachable.

3. Whether pull request code can swap in its own driver or init script. It cannot. The driver, the broker and sibling-init.sh are installed root-owned from .base, which is main (pr-verify-capture.yml#L448).

4. The key-holding outer leg. It is unchanged and keeps CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: "1" (line 364) and --restricted. The new workflow test pins both.

5. Who can turn on --in-sandbox.

  • Through a broker request: nobody. driverArgs adds the flag only from the broker's start option (broker.mjs:131). The strict request parse rejects --in-sandbox as an unknown option, and a test covers that.
  • Through a direct driver launch: buildLaunch refuses --in-sandbox unless the launch is unattended and keeps an ANTHROPIC_API_KEY that starts with ccd-dummy- and is longer than that prefix (driver.mjs:340-343). So --restricted and the scrub cannot be dropped around a real key, or with no key. Every other credential is still removed by UNSET_VARS. A test pins KEY_PREFIX to SANDBOX_KEY_PREFIX.

Trust controls and instruction surfaces

  • Trust controls: the changed files include no trusted-trigger check, lane token request, kill switch, token broker signer or merge hold, so no trust control is widened.
  • Instruction text: the skill and README edits (.claude/skills/..., the harness-ops SKILL.md) only narrow "never drop --restricted" to local drives. The capture case they now allow is enforced in code: the driver's dummy-key check and the sandbox check step. A local drive still cannot drop the flag. The protected-class register was not consulted because its file was not read in this run; I judged the edits by their consequences instead.

Spend (not a finding)

The driven model can now run Bash, which may use more prepaid API credit through the key proxy. That spend is still limited by the proxy's per-job token cap and was already accepted under the owner's option A.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 39a20d0 Nov 6, 2026
62 of 75 checks passed
@kyle-sexton
kyle-sexton deleted the feat/13711-capture-driven-bash branch November 6, 2026 17:10
@github-actions github-actions Bot added the merge-path:owner-agent Post-merge accounting record (README.md, Merge paths) label Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-path:owner-agent Post-merge accounting record (README.md, Merge paths)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(pr-verify): probe Bash for the capture's driven session in the sibling sandbox

1 participant