* test(#3515): add failing-first unconfined-mcp notice suite * fix(#3515): disclose the intentional mcp unconfined posture * chore(#3515): backfill changeset pr number --------- Co-authored-by: sim <sim@local>
3.3 KiB
Fix PR
Using the wrong template? — Enhancement: use enhancement.md — Feature: use feature.md
Linked Issue
Required. This PR will be auto-closed if no valid issue link is found.
Fixes #3515
The linked issue must have the
confirmed-buglabel. If it doesn't, ask a maintainer to confirm the bug before continuing.
What was broken
The capability consent prompt never stated the hooks-vs-MCP confinement asymmetry: hook commands are confined to the capability bundle (ADR-1244 D5 rule 5), but an MCP server's command/args/env/cwd are written verbatim and may point anywhere on the machine. The asymmetry is intentional (per the maintainer decision on the epic — most real MCP servers legitimately resolve to global/npx installs, so confinement would break them), but unstated, so the consent was not informed about it.
What this fix does
The document+disclose arm of the decision (no confinement machinery): the consent disclosure's MCP section now renders one explicit notice for every spawned (stdio) server — "intentionally NOT confined to the bundle: a server's command, args, env, and cwd are written verbatim and may point anywhere on this machine — unlike hooks, which are confined to the capability bundle root". Remote-only (http/sse) servers render no notice (nothing local is spawned — the claim stays exact), decided by one shared isRemoteMcpServer predicate also used for the per-server rendering so the two cannot drift. The lifecycle's MCP write path documents the intentional asymmetry in code, cross-referencing the notice and the existing re-consent binding (disclosureSignature already folds command/args/env/cwd + full rawConfig, #1459 — any change forces re-consent).
Root cause
The D5 hook confinement (rule 5) postdates the MCP write path; the asymmetry was deliberate but was never carried into the human disclosure, so the prompt showed MCP servers without saying their posture differs from the hooks listed right above them.
Testing
How I verified the fix
- Failing-first:
gsd-testat the tests-only commit — verdict below. - GREEN: full
gsd-testmatrix at the final HEAD — verdict below. - A GOLDEN signature test locks the consent signature's exact bytes for a spawned-server manifest — the notice provably introduces no new disclosure state, so no already-consented install can be spuriously re-prompted.
Regression test added?
- Yes — added a test that would have caught this bug
Platforms tested
- macOS
- Windows (including backslash path handling)
- Linux
Runtimes tested
- Claude Code
- Gemini CLI
- OpenCode
- Other: ___
- N/A (not runtime-specific — trust-gate prompt renderer)
Checklist
- Issue linked above with
Fixes #3515— PR will be auto-closed if missing - Linked issue has the
confirmed-buglabel - Fix is scoped to the reported bug — no unrelated changes included
- Regression test added (or explained why not)
- All existing tests pass (
npm test) — fullgsd-testmatrix at final HEAD .changeset/fragment added —Securitytype- No unnecessary dependencies added
Breaking changes
None — no behavior change anywhere; one added prompt line (spawned MCP servers only), comments, and documentation.