Audit metadata
- Priority: P1
- Estimated effort: M
- Implementation risk: HIGH
- Category: security
- Evidence baseline:
07be5be7ce69bea0c3118744ab90d148b010fce0 (origin/main on 2026-07-17)
Dependencies
- P1 proposal 021 — Enforce least-privilege connector permissions and action approval. Included in the current P1 issue wave.
- Audited P2 proposal 028 — Make the standalone agent runtime independently installable and verified. Not filed in this P1-only wave.
Description and impact
An enabled plugin retains tool approval when files change without a version
bump, and version reconciliation can preserve the old allowlist automatically.
Stdio MCP processes also inherit the complete Local Studio environment, which
can expose unrelated credentials to plugin code. Approval must bind to the exact
artifact and child processes must receive only platform essentials plus explicit
connector configuration.
Current state
services/agent-runtime/src/plugin-runtime.ts:113-145 records plugin origin as
ID, version, and binding only.
plugin-runtime.ts:426-458 detects only version mismatch and re-enables a
replacement with the previous allowTools after a probe.
services/agent-runtime/src/connector-contract.ts:5-10 has no artifact digest
in ConnectorOriginSchema.
services/agent-runtime/src/mcp-client.ts:73-78 spawns stdio servers with
{ ...process.env, ...(target.env ?? {}) }.
frontend/scripts/plugin-runtime.test.ts:166-210 currently expects automatic
approval migration on version change.
Verification commands
| Purpose |
Command |
Expected on success |
| Runtime tests |
npm --prefix services/agent-runtime test |
digest, approval, and environment cases pass |
| Runtime check |
npm --prefix services/agent-runtime run check |
exit 0 |
| Repository gate |
npm run check |
exit 0 |
| Integration |
npm run test:integration |
all pass |
| Desktop package |
npm --prefix frontend run desktop:dist |
production desktop bundle created |
Scope
In scope:
services/agent-runtime/src/plugin-artifact-digest.ts (create)
services/agent-runtime/src/plugin-artifact-digest.test.ts (create)
services/agent-runtime/src/plugin-discovery.ts
services/agent-runtime/src/plugin-runtime.ts
services/agent-runtime/src/connector-contract.ts
services/agent-runtime/src/mcp-client.ts
- Focused plugin/runtime tests covering approval migration and child spawn env
Out of scope:
- Plugin download/installation; audit proposal 034 designs that lifecycle.
- OS sandboxing, containers, or rewriting MCP protocol transport.
- Passing arbitrary parent variables for compatibility.
- Changing account-adapter connectors unless they use the same stdio path.
Solution design
Compute SHA-256 over a canonical sorted walk containing normalized relative
path, entry type, relevant mode bits, symlink target, and every regular-file
byte. Reject escaping/dangling/cyclic symlinks and special files; ignore mtimes.
Add artifactDigest to plugin bundles and plugin connector origins. Existing
origins without a digest and any digest/version/binding/config change become
disabled or approval_required; never copy allowTools automatically. Explicit
reconnect probes the new connector and records a new approval. Build stdio child
environments from a platform-specific allowlist (PATH, temp/home/locale and
Windows runtime essentials only) plus target.env; unknown parent variables are
absent.
Implementation plan
Step 1: Add deterministic artifact identity
Create a pure Effect-backed digest module with bounded traversal and canonical
encoding. Include all bundle files, including manifests and executables. Keep
errors path-relative and free of file contents.
Verify: digest tests cover stable reorder/mtime, content/mode/path changes,
empty bundle, nested files, symlink escape/cycle, and special files; all pass.
Step 2: Persist the digest in connector origin
Extend the Effect schema with an optional digest for backward decoding, require
it for newly resolved plugin connectors, and propagate the discovered digest.
Treat missing legacy digest as unapproved on next reconciliation.
Verify: schema tests decode legacy origins but runtime tests show they cannot
remain enabled without explicit reconnect.
Step 3: Fail closed on every plugin identity change
Replace automatic allowlist preservation with invalidation when digest, version,
binding, command, arguments, cwd, endpoint, headers, or declared environment
changes. Close pooled connections. Expose an actionable approval-required reason
without filesystem secrets.
Verify: tests mutate one identity field at a time and assert enabled: false,
no tool call, and explicit reconnect requirement.
Step 4: Build a minimal stdio environment
Extract a typed helper that copies only documented platform essentials from
process.env and overlays explicit connector env keys. Filter undefined values;
never retain an undeclared variable. Inject the env source/spawn function in
tests.
Verify: a sentinel parent secret is absent, explicit connector values are
present, and required POSIX/Windows bootstrap keys are covered by table tests.
Step 5: Run all gates and verify the packaged desktop
Run focused runtime, repository, and integration validation. The agent runtime
is bundled into the desktop app, so build desktop:dist, then perform the exact
canonical reinstall from AGENTS.md: remove /Applications/Local Studio.app,
copy frontend/dist-desktop/mac-arm64/Local Studio.app with ditto, remove the
legacy $HOME/Applications/local-studio-mac.app, terminate/re-register/relaunch
Local Studio, and request /api/desktop-health on its persisted embedded port.
Verify: npm --prefix services/agent-runtime run check && npm run check && npm run test:integration && npm --prefix frontend run desktop:dist && git diff --check → all exit 0; the reinstalled canonical app returns HTTP 200 with ok: true from /api/desktop-health.
Test plan
- Same version with one modified file, mode, manifest, command, or env.
- Version change with identical/different bytes; both require approval because
approval origin changed.
- Legacy origin without digest.
- Contained and escaping symlinks, cycles, special files, deterministic ordering.
- Parent env includes a sentinel secret, cloud credentials, and API keys; none
reach spawn unless explicitly in target.env.
- Reconnect records the new digest only after successful probe and user action.
Acceptance criteria
Risks and stop conditions
Stop and report if:
- A supported bundle requires escaping symlinks or mutable files outside its root.
- Existing connector persistence cannot represent approval-required state safely.
- A required runtime depends on an undeclared parent variable; request an
explicit allowlist decision rather than restoring full inheritance.
- Digesting a legitimate bundle is unbounded without a product size policy.
- The canonical desktop reinstall or
/api/desktop-health verification fails.
Maintenance considerations
Artifact canonicalization is a security contract: version it before changing its
encoding. Reviewers should scrutinize legacy-origin handling and the negative env
test. audit proposal 034's installer must verify the same digest before activation.
Generated from Local Studio 2.0 main-branch audit proposal 039.
Audit metadata
07be5be7ce69bea0c3118744ab90d148b010fce0(origin/mainon 2026-07-17)Dependencies
Description and impact
An enabled plugin retains tool approval when files change without a version
bump, and version reconciliation can preserve the old allowlist automatically.
Stdio MCP processes also inherit the complete Local Studio environment, which
can expose unrelated credentials to plugin code. Approval must bind to the exact
artifact and child processes must receive only platform essentials plus explicit
connector configuration.
Current state
services/agent-runtime/src/plugin-runtime.ts:113-145records plugin origin asID, version, and binding only.
plugin-runtime.ts:426-458detects only version mismatch and re-enables areplacement with the previous
allowToolsafter a probe.services/agent-runtime/src/connector-contract.ts:5-10has no artifact digestin
ConnectorOriginSchema.services/agent-runtime/src/mcp-client.ts:73-78spawns stdio servers with{ ...process.env, ...(target.env ?? {}) }.frontend/scripts/plugin-runtime.test.ts:166-210currently expects automaticapproval migration on version change.
Verification commands
npm --prefix services/agent-runtime testnpm --prefix services/agent-runtime run checknpm run checknpm run test:integrationnpm --prefix frontend run desktop:distScope
In scope:
services/agent-runtime/src/plugin-artifact-digest.ts(create)services/agent-runtime/src/plugin-artifact-digest.test.ts(create)services/agent-runtime/src/plugin-discovery.tsservices/agent-runtime/src/plugin-runtime.tsservices/agent-runtime/src/connector-contract.tsservices/agent-runtime/src/mcp-client.tsOut of scope:
Solution design
Compute SHA-256 over a canonical sorted walk containing normalized relative
path, entry type, relevant mode bits, symlink target, and every regular-file
byte. Reject escaping/dangling/cyclic symlinks and special files; ignore mtimes.
Add
artifactDigestto plugin bundles and plugin connector origins. Existingorigins without a digest and any digest/version/binding/config change become
disabled or
approval_required; never copyallowToolsautomatically. Explicitreconnect probes the new connector and records a new approval. Build stdio child
environments from a platform-specific allowlist (
PATH, temp/home/locale andWindows runtime essentials only) plus
target.env; unknown parent variables areabsent.
Implementation plan
Step 1: Add deterministic artifact identity
Create a pure Effect-backed digest module with bounded traversal and canonical
encoding. Include all bundle files, including manifests and executables. Keep
errors path-relative and free of file contents.
Verify: digest tests cover stable reorder/mtime, content/mode/path changes,
empty bundle, nested files, symlink escape/cycle, and special files; all pass.
Step 2: Persist the digest in connector origin
Extend the Effect schema with an optional digest for backward decoding, require
it for newly resolved plugin connectors, and propagate the discovered digest.
Treat missing legacy digest as unapproved on next reconciliation.
Verify: schema tests decode legacy origins but runtime tests show they cannot
remain enabled without explicit reconnect.
Step 3: Fail closed on every plugin identity change
Replace automatic allowlist preservation with invalidation when digest, version,
binding, command, arguments, cwd, endpoint, headers, or declared environment
changes. Close pooled connections. Expose an actionable approval-required reason
without filesystem secrets.
Verify: tests mutate one identity field at a time and assert
enabled: false,no tool call, and explicit reconnect requirement.
Step 4: Build a minimal stdio environment
Extract a typed helper that copies only documented platform essentials from
process.envand overlays explicit connector env keys. Filter undefined values;never retain an undeclared variable. Inject the env source/spawn function in
tests.
Verify: a sentinel parent secret is absent, explicit connector values are
present, and required POSIX/Windows bootstrap keys are covered by table tests.
Step 5: Run all gates and verify the packaged desktop
Run focused runtime, repository, and integration validation. The agent runtime
is bundled into the desktop app, so build
desktop:dist, then perform the exactcanonical reinstall from
AGENTS.md: remove/Applications/Local Studio.app,copy
frontend/dist-desktop/mac-arm64/Local Studio.appwithditto, remove thelegacy
$HOME/Applications/local-studio-mac.app, terminate/re-register/relaunchLocal Studio, and request
/api/desktop-healthon its persisted embedded port.Verify:
npm --prefix services/agent-runtime run check && npm run check && npm run test:integration && npm --prefix frontend run desktop:dist && git diff --check→ all exit 0; the reinstalled canonical app returns HTTP 200 withok: truefrom/api/desktop-health.Test plan
approval origin changed.
reach spawn unless explicitly in
target.env.Acceptance criteria
allowToolsforward.Risks and stop conditions
Stop and report if:
explicit allowlist decision rather than restoring full inheritance.
/api/desktop-healthverification fails.Maintenance considerations
Artifact canonicalization is a security contract: version it before changing its
encoding. Reviewers should scrutinize legacy-origin handling and the negative env
test. audit proposal 034's installer must verify the same digest before activation.
Generated from Local Studio 2.0 main-branch audit proposal 039.