Skip to content

feat(agent): enforce a drive trust boundary — shell opt-in, egress allowlist, redaction - #29

Merged
tcballard merged 3 commits into
mainfrom
claude/trust-boundary
Jul 2, 2026
Merged

feat(agent): enforce a drive trust boundary — shell opt-in, egress allowlist, redaction#29
tcballard merged 3 commits into
mainfrom
claude/trust-boundary

Conversation

@tcballard

@tcballard tcballard commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Batch 1 of the technical-review fixes: the drive's trust boundary. Everything the model observes during a drive is content from the product under test — untrusted input — yet the same model held an unsandboxed shell (spawnSync(..., { shell: true })) and unrestricted navigate/request URLs, and the transcript shipped to the model provider unredacted.

What

  • Shell off by default. The terminal tools (run_command, expect_output, expect_exit) are withheld from the advertised catalog and refused at dispatch unless the operator opts in with --allow-shell (CLI) or allowShell (scope config). A tool the model never sees is a tool an injected page can't ask for; the dispatch check makes it a guarantee.
  • Egress allowlist. navigate/request may only reach the start URL's origin, the loaded extension's own pages, and hosts passed via repeatable --allow-host / config allowedHosts. Cloud-metadata endpoints, internal services, and exfil targets are refused. Non-special schemes (chrome-extension:) have opaque WHATWG origins, so those compare as protocol//host — one extension's pages stay distinct from another's.
  • Refusals are feedback, not aborts. A refused call surfaces to the model as a failed action naming the reason and the remedying flag, matching the loop's existing failure-feedback contract.
  • Observation redaction. Query strings, bearer credentials, and provider-shaped keys are scrubbed from network lines, console lines, command output, and response snippets before the transcript ships to the provider. The page's visible text and ARIA tree are deliberately untouched, so recorded assertions still match the real page.
  • Prompt hardening. The system prompt states the egress policy and that page content is data, never instructions.

New modules: src/agent/policy.ts (EgressPolicy, callRefusal, urlRefusal, buildPolicy), src/agent/redact.ts (redactUrl, redactText) — both exported from the library surface.

Behavior change

A drive that needs the terminal must now pass --allow-shell; one that reaches a second origin must --allow-host it. Noted in the CHANGELOG under Unreleased.

Corpus (dogfood)

req-agent-trust-boundary + design-agent-trust-boundary, linked to the autonomous-qa-enhancements roadmap with a new initiative; ## Verified Bytests/agent-policy.test.ts, tests/redact.test.ts. Gates green: rac validate (29/29), rac relationships --validate (0 issues, health 100), rac review (no priority findings), self-coverage 14/14.

Verification

  • Unit: 273 passed (29 new across agent-policy, redact, plus threading assertions in qa-command, scoped-qa, scope-config).
  • Real browser (PROOFKEEPER_E2E=1): all 6 drive integration suites pass, including the terminal drive (now opted in with allowShell: true) and the HTTP drive (same-origin request allowed by policy).
  • npm run typecheck / build green.

tcballard added 3 commits July 1, 2026 22:02
…hancements]

Observed page content is untrusted input, and the model held an
unsandboxed shell and unrestricted navigate/request URLs in the same
context. Add the drive-time boundary:

- shell tools are withheld and refused unless the operator opts in
  (--allow-shell / config allowShell)
- navigate/request egress is allowlisted to the start URL's origin, the
  loaded extension's pages, and --allow-host / config allowedHosts
- network, console, command, and response side channels are redacted
  (query strings, bearer tokens, key-shaped values) before the
  transcript ships to the model provider

Refusals feed back to the model as failed actions, so a drive adapts
instead of aborting.

Signed-off-by: Tom Ballard <tom@armytage.co>
…roadmap:autonomous-qa-enhancements]

Pins that an injected instruction cannot reach the shell or a
non-allowlisted origin, that one extension's pages stay distinct from
another's under opaque WHATWG origins, that the trust boundary threads
CLI -> QaOptions -> DriveOptions and config -> scoped drives, and that
credential-shaped values are scrubbed from side channels.

Signed-off-by: Tom Ballard <tom@armytage.co>
…enhancements]

Implements lore-proofkeeper/requirements/req-agent-trust-boundary.md.

Adds the requirement and design artifacts, the roadmap initiative, a
README 'Trust boundary' section, and the changelog entry noting the
behavior change (terminal drives now opt in with --allow-shell).

Signed-off-by: Tom Ballard <tom@armytage.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant