fix(#1459): user-owned consent store gates third-party capability activation; env/cwd in disclosure; loader validator parity (#1473)

This commit is contained in:
Tom Boucher
2026-06-20 01:59:03 -04:00
committed by GitHub
parent 81d4b6d573
commit e7855bc217
30 changed files with 5125 additions and 685 deletions

View File

@@ -100,12 +100,13 @@ Third-party capabilities may ship the **same artifacts** first-party ships (full
Hard rules (MUST):
1. **Install never executes capability code.** Staging is copy-only; no `postinstall`-equivalent. (npm `--ignore-scripts` lesson.)
2. **Executable surfaces are disclosed and consented at install.** `hooks`, `mcpServers`, and command modules activate on the *next tool call* — there is no "first use" gate for a hook — so consent must be at install, naming every executable surface. Declining aborts cleanly.
2. **Executable surfaces are disclosed and consented at install.** `hooks`, `mcpServers`, and command modules activate on the *next tool call* — there is no "first use" gate for a hook — so consent must be at install, naming every executable surface. The disclosure includes each MCP server's **`env` and `cwd`** (#1459), because an environment variable (e.g. `NODE_OPTIONS=--require evil.js`) can change *what* a command does without touching the command or argv; the disclosure signature folds env/cwd in as stable sorted JSON so any add/change forces re-consent. Declining aborts cleanly.
3. **Integrity is verified before extraction** when an `integrity`/SHA is available; mismatch aborts. (npm registry-signature lesson.)
4. **Auto-update is OFF by default** for third-party; enabling it still **re-prompts when the executable set changes** between versions. (VS Code stolen-PAT + silent-auto-update lesson.)
5. **Modules are `require()`'d only from the capability's own install root** — parent-directory traversal in declared paths is rejected.
6. **`gsd-*` (and `gsd-core-*`, `anthropic-*`) ids/prefixes are reserved** — third-party cannot impersonate first-party.
7. **`strictKnownRegistries`** (managed/project config) can lock installs to an allowlist; `[]` means no external installs.
8. **The consent signal for a project-scope capability is a user-owned consent store, NOT the in-repo ledger** (#1459). The store lives at `${GSD_HOME||homedir()}/.gsd/consent.json` — outside any repository — keyed by `(realpath(projectRoot), id)` and bound to the bundle integrity + disclosure signature. Before activating a project-scope overlay (its declarative loop surfaces **and** its command dispatch) the loader requires a matching record on **this machine**; without it the capability is discovered-but-inactive. This **retracts the prior limitation** that a project-scope ledger living inside the repository was itself the consent — a forged/cloned project ledger could otherwise activate executable + declarative surfaces with no user decision. Global-scope installs (under the user's own home) need no per-project record. `gsd capability trust list`/`revoke` audit and revoke project consents.
Stated honestly: **there is no sandbox.** Node-level sandboxing is impractical and would defeat full parity. Consent + integrity + reversibility are the barrier. (Obsidian's honest acknowledgment.) **Rationale:** a one-time trust prompt does not make running arbitrary code safe; separating *artifact parity* from *trust posture* is what makes full parity defensible.