Skip to content
Install

docs/security-2026-10-04.md

Security pass: October 3 surfaces

A bounded review of the surfaces added on October 3 and what changed.

Bounded review of app-server, pi and its temporary bridge, threads and explicit snapshots, queue/steering, PR watch, approval previews, saved tool outputs, compaction transcripts, usage resume and feature gating. Baseline: e3ef75d. Threats: authenticated read-only/full peers, hostile repository executable lookup, untrusted model/tool/PR content and secret persistence. This is not a whole-repository security certification.

Confirmed defects and corrections

SurfaceTrigger and impactCorrection
app-serverInternal exceptions or reflected method names could disclose credentials in JSON-RPC errorsGeneric internal failures; redacted expected messages
pi approvalsOversized input was clipped before approval; a previous session grant survived entry into plan modeRefuse oversized arguments without running them; plan denies non-read tools before grants
pi tracesNew credentials appeared in raw RPC lines, including split streaming framesRedact trace lines; omit message-update payloads from traces
threadswait read another session's answer as an unrestricted readSame gate as read/search, explicit mention grants retained
peer model toolsDirect local-only guards did not cover indirect native model callsExclude threads/pr_watch on peer turns, preserve through synthetic continuation and restart; refuse peer pi turns
queue/steerFull peers could inspect pending local prompts and steer them into a host turnQueue inspection/edit/reorder/steer are local-only
PR watchPR title/head/check metadata could be saved or exported with secrets; returned URLs could become next poll arguments; predictable temporary file followed a symlinkRedact before parsing/persistence; canonical GitHub URLs only; exclusive random temporary file
blast radiusRepository PATH could substitute git/find before approvalFixed system executables, controlled PATH, loader variables removed
output filtersNewly discovered credentials reached full saved outputs; split tool deltas escaped per-event detectionRedact before saving/filtering/truncation; bounded line/private-key buffering for live output
output/transcript storageSymlinked output directories redirected private files outside the selected storeRevalidate real private directories per save, exclusive files, atomic transcript writes; cleanup skips links
usage resumeLosing the recorded model permitted resume into a fallbackAsk when the model changes or disappears; peers cannot arm or manually resume host work
feature policyFull peers bypassed tier/features guards via aliases or package/MCP resource commandsHost execution settings and execution-resource mutators are local-only

Regression failures were observed before their corresponding minimal fixes; transport-level regression coverage also exercises local and full-peer command boundaries. New suites use temporary stores, synthetic credentials, scripted providers and fake gh/pi transports. No account, installation, real provider or external network is required.

Retained design choices for review

  • pi remains off by default and permitted only by the full tier or explicit override. Following the lead's isolation decision, untrusted project extension discovery is disabled (or refused when the flag is unavailable), session grants cover exact tool arguments, and pi follows the native OS sandbox setting. Every mode except full requires a bridge handshake before turns and compaction. The bridge does not impose native per-path permission rules or attest arbitrary trusted extensions. With sandbox off, approved tools still have the user's rights. See pi-engine.md; verification uses fake pi and sandbox processes, with no external pi installation.
  • Thread reads use the existing permission modes: full/edits can broadly authorize a write-class action. Decide whether cross-session reads must always require an explicit mention or individual approval even in those modes. This pass aligns wait with read/search; it does not redefine full access.
  • D1 auto-resume stays armed by default with a known reset, no silent fallback, and draft/quota/stale-turn checks. Disabling usage_resume affects new pauses; Cancel disarms an existing schedule. Changing that persistence rule requires a product decision. Peer tool restrictions persist independently of the schedule.
  • Safe mode is a startup ceiling, while local overrides outside safe mode remain intentional. The host already omits MCP, custom tools and extensions and denies pi in safe mode; a repository cannot change that startup option.
  • Detection covers recognized secret formats and registered session values. It cannot prove arbitrary unlabeled text is harmless. General model-text streaming, external engine-owned session files and other legacy export paths are not certified by this bounded pass. A blanket ban on model text or on external-engine persistence needs a separate policy.
  • Storage guards assume the selected data root is trusted. They do not provide isolation from a concurrent same-user process racing filesystem operations; that requires OS-level confinement. Existing disk/retention caps remain intact.

Validation

Run only the changed/new test files plus CLI footprint and TUI automation. Typecheck from the worktree root with ./node_modules/.bin/tsc -p tsconfig.base.json --noEmit. Full suite, live providers and real external tools are excluded. The initial socket checks encountered EPERM; subsequent runs successfully exercised the local transports, full-peer policy, TUI automation and a native peer turn resumed after daemon restart. Final results are recorded in the delivery.

Final targeted test run: 157 passed, zero failed across 14 files, including footprint and TUI automation.

Addendum 2026-10-05: skills, packages and project resources

Follow-up to an external review of skills, packages and custom views. Each finding was treated as a hypothesis and reproduced with a failing regression test (temp directories, fake MCP server, scripted provider, no network) before the fix.

SurfaceTrigger and impactCorrection
Project MCP serversServers from a project's mcp.json or packages were spawned or contacted before the directory was trusted, with the full environmentHeld until /tools trust (needsTrust, no process, connection or discovery); credential-named variables are not inherited by project servers unless their env lists them. See mcp-oauth.md
Custom command inline shell! fragments ran in the daemon before the turn, outside approval, deny rules and sandbox; $1/$ARGUMENTS were pasted raw into shell textFragments run as shell tool calls of the turn (same permission, approval, sandbox and hooks); placeholders become single-quoted words, and unquotable positions are refused. See commands.md
Custom approval and question viewsA view could draw "Reject" over a clickable approving region, and its regions did not follow rows the terminal insertedThe terminal draws the option rows and owns their clicks; these slots have no actions; regions of other slots are rebased after any insertion. See views.md
Package and trust changesThe MCP server set was built once and not rebuilt on package install, enable, disable, remove or trust changesReconciled on each of those events
Daemon extensionsA first daemon(api) that threw after registering left its tools, commands, hooks, auth and provider liveRegistrations are discarded when setup fails; the error stays in /extensions. See extensions.md
Claude agent filesAn allow list of unknown names became "every tool"; permissionMode was dropped silentlyUnknown-only lists grant none, qualifiers never widen, plan and acceptEdits cap the agent, unhonourable fields are listed as warnings. See agents.md

Reviewed and left as is (reported, not changed): the other privileged click regions of custom views (notice queue send and limit chips, slash.menu select, home and sidebar open) act on listed items and grant no permission; their arg is validated against the model. Saved JSON workflow plans from an untrusted project layer are not blocked by trust (scripts are), but starting any workflow needs an approval that lists its tasks and check commands. The status-line script trust check in daemon.ts compares only canonical paths, so it fails closed for a non-canonical stored entry.

Validation: the new and changed tests (mcp, daemon custom commands and command execution, views, extensions, packages), CLI footprint and TUI automation, and ./node_modules/.bin/tsc -p tsconfig.base.json --noEmit.