feat(mosaic): enforce whole mutator-class lease gate #837

Merged
jason.woltje merged 13 commits from feat/829-mutator-gate into main 2026-07-18 05:51:59 +00:00
Owner

closes #829

Summary

  • add broker-owned UNVERIFIED/PENDING/VERIFIED lease transitions with revoke-first and token-consume/promote-last ordering
  • deny Claude and Pi mutator, custom, and unknown tool classes by default while allowing only the exact read-only/recovery set
  • register runtime identity before exec, enforce monotonic TTL/generation/observer revocation, and fail closed on broker or adapter failure
  • document the T-A/T-B/T-C boundary and receipt-as-prerequisite-only invariant

RED/GREEN evidence

  • RED: 5/5 gate contract tests failed on WI-1 UNKNOWN_ACTION/missing adapters
  • RED: register-before-exec test failed with missing launcher
  • GREEN: focused broker + gate acceptance 43/43
  • Python coverage: 88% total (daemon 89%, launcher 86%, runtime gate 86%)
  • full repository suite: 43/43 Turbo tasks; @mosaicstack/mosaic 72 files / 1,377 tests
  • typecheck 42/42; lint 23/23; format and diff checks green

Review gate

Authorization surface: mandatory independent terra CODE review and exact-head Opus SECREV are required before Mos-authorized merge. Author did not self-review. Do not merge from this lane.

Round 5 — parser stopping-criterion evidence (A+B)

Exact head: 04cc065c2babae5bcc04b932d89d0bc3ff8546df

A — realistic parser classes closed: tracked claude/pi variables are resolved in the same terminal command-position path as literal binaries after any exec / command / nohup / env+assignment prefix sequence. Permanent RED regressions cover quoted and unquoted exec, command, nohup, and env A=1, each in multiline and same-line assignment shapes (10 new RED-before-fix cases). Existing comment-marker, heredoc, continuation, chaining, command-substitution, eval, bare-variable, and primitive-location regressions remain green.

B — residual backstops proven:

  • B1: an alias-indirected dangerous launch intentionally missed by the command parser is still RED as dangerous-primitive; only launch-runtime.py owns the raw dangerous flag.
  • B2: a parser-missed non-dangerous alias launch is paired with acceptance evidence that Claude's shipped global .* PreToolUse hook invokes the gate; without MOSAIC_LEASE_SESSION_ID, read, mutator, and custom/MCP tools all exit 2 with GATE_UNAVAILABLE.

Stopping criterion status: A+B are present on this head. The parser is explicitly best-effort rather than a complete shell interpreter; fresh terra CODE and Opus SECREV provide C (no new non-overlapping realistic evasion). Inventory remains 14/14 and guard coverage remains 97%.

closes #829 ## Summary - add broker-owned UNVERIFIED/PENDING/VERIFIED lease transitions with revoke-first and token-consume/promote-last ordering - deny Claude and Pi mutator, custom, and unknown tool classes by default while allowing only the exact read-only/recovery set - register runtime identity before exec, enforce monotonic TTL/generation/observer revocation, and fail closed on broker or adapter failure - document the T-A/T-B/T-C boundary and receipt-as-prerequisite-only invariant ## RED/GREEN evidence - RED: 5/5 gate contract tests failed on WI-1 UNKNOWN_ACTION/missing adapters - RED: register-before-exec test failed with missing launcher - GREEN: focused broker + gate acceptance 43/43 - Python coverage: 88% total (daemon 89%, launcher 86%, runtime gate 86%) - full repository suite: 43/43 Turbo tasks; @mosaicstack/mosaic 72 files / 1,377 tests - typecheck 42/42; lint 23/23; format and diff checks green ## Review gate Authorization surface: mandatory independent terra CODE review and exact-head Opus SECREV are required before Mos-authorized merge. Author did not self-review. Do not merge from this lane. ## Round 5 — parser stopping-criterion evidence (A+B) Exact head: `04cc065c2babae5bcc04b932d89d0bc3ff8546df` **A — realistic parser classes closed:** tracked `claude`/`pi` variables are resolved in the same terminal command-position path as literal binaries after any `exec` / `command` / `nohup` / `env`+assignment prefix sequence. Permanent RED regressions cover quoted and unquoted `exec`, `command`, `nohup`, and `env A=1`, each in multiline and same-line assignment shapes (10 new RED-before-fix cases). Existing comment-marker, heredoc, continuation, chaining, command-substitution, `eval`, bare-variable, and primitive-location regressions remain green. **B — residual backstops proven:** - B1: an alias-indirected dangerous launch intentionally missed by the command parser is still RED as `dangerous-primitive`; only `launch-runtime.py` owns the raw dangerous flag. - B2: a parser-missed non-dangerous alias launch is paired with acceptance evidence that Claude's shipped global `.*` `PreToolUse` hook invokes the gate; without `MOSAIC_LEASE_SESSION_ID`, read, mutator, and custom/MCP tools all exit 2 with `GATE_UNAVAILABLE`. **Stopping criterion status:** A+B are present on this head. The parser is explicitly best-effort rather than a complete shell interpreter; fresh terra CODE and Opus SECREV provide C (no new non-overlapping realistic evasion). Inventory remains 14/14 and guard coverage remains 97%.
jason.woltje added 3 commits 2026-07-18 03:38:59 +00:00
Author
Owner

OPUS SECREV (MANDATORY, auth surface) — WI-2 #829 head 77b137cc

Verdict: SECREV GO — reviewed exact head 77b137ccc04b5be035cac5ca21bbbf3df8b94f970 blocking security findings.

Fresh review of the authorization surface; zero evidence transfer from the WI-1 #828 SECREV. I am the mandatory security gate on this boundary (terra runs the code lens in parallel). Base = main 8ec67a11 (clean merge-base); reviewed = git diff main...HEAD + every new/changed file. Reviewed-SHA == merge head (exact-head). The build's own green tests were treated as process, not evidence; every property below was earned from the product code.

Surface mapped

  • Broker gate logicdaemon.py (+173): new lease state machine UNVERIFIED → PENDING_VERIFICATION → PENDING_PROMOTION → VERIFIED in volatile Broker.leases (in-memory only), and actions begin_verification, promote_lease, revoke_lease, authorize_tool.
  • Gate clientmutator-gate.py (new): stdin tool_name → broker authorize_tool; exit 0 allow / exit 2 deny.
  • Launcherlaunch-runtime.py (new) + launch.ts: register_anchor then execvpe the runtime (PID/starttime = authenticated anchor), exporting only broker-minted MOSAIC_LEASE_SESSION_ID + generation.
  • Runtime entries — Claude settings.json PreToolUse matcher:".*" (all tools) → mutator-gate.py --runtime claude; Pi mosaic-extension.ts pi.on('tool_call', …) → checkPiMutatorGate (spawns the same gate --runtime pi).

Consequential-mutator entry enumeration — none reachable without a broker-VERIFIED lease

Every tool invocation in a gated runtime funnels through exactly one gate call:

  • Claude: the .* PreToolUse hook fires for every tool (built-in, unknown, MCP mcp__*, Task); a hook exit 2 blocks the tool. Bash/Edit/Write/NotebookEdit and all unknown/custom tools are consequential by classification (not in the fixed read-only frozenset) → routed to the VERIFIED check.
  • Pi: tool_call handler runs the gate for every tool; non-zero → {block:true} veto.
    Classification is by whole tool class / exact name, never by parsing a Bash string (daemon.py authorize_tool: tool_name in READ_ONLY_TOOLS[runtime] or tool_name == RECOVERY_TOOL), so a compromised model cannot smuggle git/curl/kubectl/rm inside a generic mutator — the generic mutator is blocked before its input executes. Read-only sets are fixed frozensets a client cannot extend.

T-B (compromised tool, PRIMARY) — fail-closed block proven

A compromised in-runtime tool attempting a consequential mutation while UNVERIFIED (observer fired) or past-TTL is denied, and cannot escalate by any of the enumerated vectors:

  • Forge/replay a VERIFIED signal — NO. VERIFIED lives only in broker memory (Broker.leases[session_id].state); authorize_tool reads it from broker state, never from the request. The request carries only {session_id, runtime_generation, runtime, tool_name} — there is no client-supplied "I'm verified" field anywhere. Reaching VERIFIED requires begin_verification (mints a WI-1 single-use promotion token) → promote_lease (constant-time secrets.compare_digest on the token + store-token match on session/generation/binding/consumed==False, then del = consume) → finish_promotion. Replay of the promotion token fails: the store token is deleted (single-use) and the lease is no longer PENDINGINVALID_LEASE_TRANSITION/PROMOTION_TOKEN_INVALID.
  • Assert its own lease / caller-asserted principal — NO. Every action's first statement is authenticate(peer_pid, request) (verified for all four new actions: daemon.py:428/459/487/491), which binds the session to kernel SO_PEERCRED PID + /proc starttime ancestry (WI-1). A tool can only act on the session whose anchor is in its own process tree; a different (verified) session_idUNKNOWN_SESSION/ANCESTRY_MISMATCH → deny. Env MOSAIC_LEASE_SESSION_ID is read by the hook process (spawned by the harness with the harness's env), not writable by a tool child, and even a swapped value fails ancestry.
  • Race the TTL boundary — NO fail-open. TTL is authoritative at use: authorize_tool computes time.monotonic() >= expires_at and, on expiry (or missing/non-numeric expires_at), calls revoke_session_authority and returns LEASE_EXPIRED deny. monotonic() is wall-clock-immune. All state reads/writes are serialized by the WI-1 global broker_lock (single broker.handle call site confirmed at daemon.py:585), so check-and-decide is atomic; granularity is per-tool-call — the next consequential tool after expiry is denied.
  • Skip the gate — NO (fail-closed). Missing identity env (KeyError), malformed input/reply (ValueError/JSONDecodeError), broker-unavailable/connect refusal or timeout (OSError, 1.5 s client vs 1.0 s broker deadline vs 3 s/2 s hook budget) all hit mutator-gate.py's single except (KeyError, ValueError, OSError, JSONDecodeError): return deny("GATE_UNAVAILABLE") → exit 2 → tool blocked. Allow is returned only on reply.ok is True and reply.decision == "allow". A runtime launched outside the gated launcher has no session env → the installed hook denies all tools (fail-closed, not fail-open).

Revoke-first / promote-last — genuine invariant under adversarial timing

  • Revoke-first (broker operation, not advisory): begin_verification calls revoke_session_authority(session_id) before minting the new promotion token and recording PENDING. The prior VERIFIED authority and all pending tokens are gone before the new window opens; because the whole handle() runs under broker_lock, no concurrent authorize_tool observes an intermediate — a stale lease cannot survive into the new window. Any consequential tool serialized after sees PENDING (≠ VERIFIED) → deny.
  • Promote-last (by construction): in handle(), token consumption is store.commit()-ed before finish_promotion() sets VERIFIED and before the response exposes state:VERIFIED. VERIFIED is externally visible only after durable single-use consumption. No consequential mutation is possible between verify and promote: PENDING_PROMOTION ≠ VERIFIED, and the whole transition is one uninterrupted lock hold, so there is no TOCTOU where the gate reads verified-state and the tool mutates after invalidation.
  • Concurrency (two tools / tool + compaction event): broker_lock serializes begin_verification, promote_lease, revoke_lease, and authorize_tool. A revoke/compaction interleaved with an authorize either runs first (authorize sees UNVERIFIED → deny) or after (authorize already returned for a then-valid lease). No double-grant (promotion token is single-use + lease must be PENDING), no mutate-during-revoke window. Generation bump (authenticate when req.gen > current) calls revoke_session_authority → UNVERIFIED, so a new incarnation cannot inherit a prior VERIFIED lease; stale gen < currentSTALE_GENERATION deny.

No default-allow / fail-OPEN anywhere

authorize_tool's consequential path is deny-by-default: allow is the terminal statement reached only after state == VERIFIED and lease.runtime == runtime and lease.runtime_generation == req.generation and non-expired. Every other outcome is an explicit typed deny (MUTATOR_UNVERIFIED/LEASE_EXPIRED). Any exception (auth failure, integrity, commit failure) propagates → handle() rolls back store+leases and re-raises → handle_connection replies {ok:false} → gate denies. StateCommitUncertain (post-rename durability doubt) still terminates the daemon (WI-1) → fail-closed. Leases are volatile: broker restart → all UNVERIFIED. Confirmed: no branch returns allow on an error/uncertain path.

Also verified

  • Trusts only the broker's kernel-anchored signal — no client-supplied verified/principal field exists; runtime restricted to {claude,pi} (INVALID_RUNTIME), tool_name validated (non-empty ≤256).
  • Initial TTL capped at ratified 300 s; a caller may shorten (0 < ttl ≤ 300, int-only via type(...) is int) but not lengthen; expiry checked at use.
  • mint_token now deepcopys the binding (no aliasing between request/lease/token). Promotion token bound to cycle via token.binding == lease.binding + session/generation match.

Non-blocking observations (informational — NOT change requests)

  1. Assurance-boundary clarity (must be stated so GO is not over-read): WI-2 is a mechanical whole-class gate. begin_verification/promote_lease are drivable by any process in the anchor's tree; the broker attests the kernel anchor + single-use cycle token, not the semantic authenticity of the verification (that a compaction genuinely occurred / the model truly re-read context). Receipt/obedience validation is explicitly WI-future (mutator-class-gate.md L26; assurance boundary L37-39). So a fully-compromised model that can run the cycle can re-verify itself — this is the ratified scope, not a WI-2 defect. GO means "no consequential mutator is reachable without completing the broker-owned revoke-first/promote-last cycle, and the gate fails closed," not "a compromised model can never mutate."
  2. Latent (currently unreachable) divergence: if finish_promotion ever raised after store.commit(), handle()'s except would roll back in-memory store.value while the file stays committed. Unreachable today (state is set PENDING_PROMOTION immediately before the check in the same lock hold). Robustness note only.
  3. Inherent hook→exec micro-window: authorization is point-in-time at the PreToolUse hook; the sub-ms gap to actual tool execution is inherent to any check-then-act gate and bounded by per-call re-authorization. Ratified.

Disclosed residuals (acknowledged, deliberately NOT flagged)

Harness-side tail-preserving middle-drop (planner ruling b7bbb6ea, A-v5-1 replaced) and server-side branch-protection backstop (WI-7); the ratified bounded T-A dual-observer-miss-within-TTL residual; and T-C (hook/extension absence, runtime outside the gated launcher, ptrace / same-UID broker replacement). Per scope these are not new findings.


Independence (Gate 16 — by lane/process): author ms-cr-wi1 (pi) ≠ code lens terra ≠ this Opus security review — distinct principals on one Gitea account, separated by lane/process; I am THE security gate for this WI. Earned from product code, not the author's tests. tea/pr-review.sh approve broken for this surface; this durable comment is the formal SECREV approval record. Reviewed head 77b137cc. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.

## OPUS SECREV (MANDATORY, auth surface) — WI-2 #829 head 77b137cc **Verdict: ✅ SECREV GO** — reviewed exact head `77b137ccc04b5be035cac5ca21bbbf3df8b94f97` — **0 blocking security findings**. Fresh review of the authorization surface; **zero evidence transfer** from the WI-1 #828 SECREV. I am the mandatory security gate on this boundary (terra runs the code lens in parallel). Base = `main` `8ec67a11` (clean merge-base); reviewed = `git diff main...HEAD` + every new/changed file. Reviewed-SHA == merge head (exact-head). The build's own green tests were treated as process, not evidence; every property below was earned from the product code. ### Surface mapped - **Broker gate logic** — `daemon.py` (+173): new lease state machine `UNVERIFIED → PENDING_VERIFICATION → PENDING_PROMOTION → VERIFIED` in volatile `Broker.leases` (in-memory only), and actions `begin_verification`, `promote_lease`, `revoke_lease`, `authorize_tool`. - **Gate client** — `mutator-gate.py` (new): stdin `tool_name` → broker `authorize_tool`; exit 0 allow / exit 2 deny. - **Launcher** — `launch-runtime.py` (new) + `launch.ts`: `register_anchor` then `execvpe` the runtime (PID/starttime = authenticated anchor), exporting only broker-minted `MOSAIC_LEASE_SESSION_ID` + generation. - **Runtime entries** — Claude `settings.json` PreToolUse `matcher:".*"` (all tools) → `mutator-gate.py --runtime claude`; Pi `mosaic-extension.ts` `pi.on('tool_call', …) → checkPiMutatorGate` (spawns the same gate `--runtime pi`). ### Consequential-mutator entry enumeration — none reachable without a broker-VERIFIED lease Every tool invocation in a gated runtime funnels through exactly one gate call: - **Claude:** the `.*` PreToolUse hook fires for **every** tool (built-in, unknown, MCP `mcp__*`, `Task`); a hook exit 2 blocks the tool. `Bash/Edit/Write/NotebookEdit` and all unknown/custom tools are consequential by classification (not in the fixed read-only frozenset) → routed to the VERIFIED check. - **Pi:** `tool_call` handler runs the gate for every tool; non-zero → `{block:true}` veto. Classification is **by whole tool class / exact name**, never by parsing a `Bash` string (daemon.py `authorize_tool`: `tool_name in READ_ONLY_TOOLS[runtime] or tool_name == RECOVERY_TOOL`), so a compromised model cannot smuggle `git/curl/kubectl/rm` inside a generic mutator — the generic mutator is blocked before its input executes. Read-only sets are fixed frozensets a client cannot extend. ### T-B (compromised tool, PRIMARY) — fail-closed block proven A compromised in-runtime tool attempting a consequential mutation while UNVERIFIED (observer fired) or past-TTL is **denied**, and cannot escalate by any of the enumerated vectors: - **Forge/replay a VERIFIED signal — NO.** `VERIFIED` lives only in broker memory (`Broker.leases[session_id].state`); `authorize_tool` reads it from broker state, never from the request. The request carries only `{session_id, runtime_generation, runtime, tool_name}` — there is no client-supplied "I'm verified" field anywhere. Reaching `VERIFIED` requires `begin_verification` (mints a WI-1 **single-use** promotion token) → `promote_lease` (constant-time `secrets.compare_digest` on the token + store-token match on session/generation/binding/`consumed==False`, then `del` = consume) → `finish_promotion`. Replay of the promotion token fails: the store token is deleted (single-use) and the lease is no longer `PENDING` → `INVALID_LEASE_TRANSITION`/`PROMOTION_TOKEN_INVALID`. - **Assert its own lease / caller-asserted principal — NO.** Every action's first statement is `authenticate(peer_pid, request)` (verified for all four new actions: daemon.py:428/459/487/491), which binds the session to kernel `SO_PEERCRED` PID + `/proc` starttime ancestry (WI-1). A tool can only act on the session whose anchor is in its own process tree; a different (verified) `session_id` → `UNKNOWN_SESSION`/`ANCESTRY_MISMATCH` → deny. Env `MOSAIC_LEASE_SESSION_ID` is read by the hook process (spawned by the harness with the harness's env), not writable by a tool child, and even a swapped value fails ancestry. - **Race the TTL boundary — NO fail-open.** TTL is authoritative **at use**: `authorize_tool` computes `time.monotonic() >= expires_at` and, on expiry (or missing/non-numeric `expires_at`), calls `revoke_session_authority` and returns `LEASE_EXPIRED` deny. `monotonic()` is wall-clock-immune. All state reads/writes are serialized by the WI-1 global `broker_lock` (single `broker.handle` call site confirmed at daemon.py:585), so check-and-decide is atomic; granularity is per-tool-call — the next consequential tool after expiry is denied. - **Skip the gate — NO (fail-closed).** Missing identity env (`KeyError`), malformed input/reply (`ValueError`/`JSONDecodeError`), broker-unavailable/connect refusal or timeout (`OSError`, 1.5 s client vs 1.0 s broker deadline vs 3 s/2 s hook budget) all hit `mutator-gate.py`'s single `except (KeyError, ValueError, OSError, JSONDecodeError): return deny("GATE_UNAVAILABLE")` → exit 2 → tool blocked. Allow is returned **only** on `reply.ok is True and reply.decision == "allow"`. A runtime launched outside the gated launcher has no session env → the installed hook denies **all** tools (fail-closed, not fail-open). ### Revoke-first / promote-last — genuine invariant under adversarial timing - **Revoke-first (broker operation, not advisory):** `begin_verification` calls `revoke_session_authority(session_id)` **before** minting the new promotion token and recording `PENDING`. The prior `VERIFIED` authority and all pending tokens are gone before the new window opens; because the whole `handle()` runs under `broker_lock`, no concurrent `authorize_tool` observes an intermediate — a stale lease cannot survive into the new window. Any consequential tool serialized after sees `PENDING` (≠ `VERIFIED`) → deny. - **Promote-last (by construction):** in `handle()`, token consumption is `store.commit()`-ed **before** `finish_promotion()` sets `VERIFIED` and before the response exposes `state:VERIFIED`. `VERIFIED` is externally visible only after durable single-use consumption. No consequential mutation is possible between verify and promote: `PENDING_PROMOTION ≠ VERIFIED`, and the whole transition is one uninterrupted lock hold, so there is no TOCTOU where the gate reads verified-state and the tool mutates after invalidation. - **Concurrency (two tools / tool + compaction event):** `broker_lock` serializes `begin_verification`, `promote_lease`, `revoke_lease`, and `authorize_tool`. A revoke/compaction interleaved with an authorize either runs first (authorize sees UNVERIFIED → deny) or after (authorize already returned for a then-valid lease). No double-grant (promotion token is single-use + lease must be `PENDING`), no mutate-during-revoke window. Generation bump (`authenticate` when `req.gen > current`) calls `revoke_session_authority` → UNVERIFIED, so a new incarnation cannot inherit a prior VERIFIED lease; stale `gen < current` → `STALE_GENERATION` deny. ### No default-allow / fail-OPEN anywhere `authorize_tool`'s consequential path is deny-by-default: allow is the terminal statement reached only after `state == VERIFIED` **and** `lease.runtime == runtime` **and** `lease.runtime_generation == req.generation` **and** non-expired. Every other outcome is an explicit typed deny (`MUTATOR_UNVERIFIED`/`LEASE_EXPIRED`). Any exception (auth failure, integrity, commit failure) propagates → `handle()` rolls back store+leases and re-raises → `handle_connection` replies `{ok:false}` → gate denies. `StateCommitUncertain` (post-rename durability doubt) still terminates the daemon (WI-1) → fail-closed. Leases are volatile: broker restart → all UNVERIFIED. Confirmed: no branch returns allow on an error/uncertain path. ### Also verified - Trusts **only** the broker's kernel-anchored signal — no client-supplied verified/principal field exists; `runtime` restricted to `{claude,pi}` (`INVALID_RUNTIME`), `tool_name` validated (non-empty ≤256). - Initial TTL capped at ratified 300 s; a caller may shorten (`0 < ttl ≤ 300`, int-only via `type(...) is int`) but not lengthen; expiry checked at use. - `mint_token` now `deepcopy`s the binding (no aliasing between request/lease/token). Promotion token bound to cycle via `token.binding == lease.binding` + session/generation match. ### Non-blocking observations (informational — NOT change requests) 1. **Assurance-boundary clarity (must be stated so GO is not over-read):** WI-2 is a *mechanical* whole-class gate. `begin_verification`/`promote_lease` are drivable by any process in the anchor's tree; the broker attests the kernel anchor + single-use cycle token, **not** the semantic authenticity of the verification (that a compaction genuinely occurred / the model truly re-read context). Receipt/obedience validation is explicitly WI-future (`mutator-class-gate.md` L26; assurance boundary L37-39). So a fully-compromised model that can run the cycle can re-verify itself — this is the ratified scope, not a WI-2 defect. GO means "no consequential mutator is reachable without completing the broker-owned revoke-first/promote-last cycle, and the gate fails closed," not "a compromised model can never mutate." 2. **Latent (currently unreachable) divergence:** if `finish_promotion` ever raised *after* `store.commit()`, `handle()`'s `except` would roll back in-memory `store.value` while the file stays committed. Unreachable today (state is set `PENDING_PROMOTION` immediately before the check in the same lock hold). Robustness note only. 3. **Inherent hook→exec micro-window:** authorization is point-in-time at the PreToolUse hook; the sub-ms gap to actual tool execution is inherent to any check-then-act gate and bounded by per-call re-authorization. Ratified. ### Disclosed residuals (acknowledged, deliberately NOT flagged) Harness-side tail-preserving middle-drop (planner ruling `b7bbb6ea`, A-v5-1 replaced) and server-side branch-protection backstop (WI-7); the ratified bounded T-A dual-observer-miss-within-TTL residual; and T-C (hook/extension absence, runtime outside the gated launcher, ptrace / same-UID broker replacement). Per scope these are not new findings. --- **Independence (Gate 16 — by lane/process):** author `ms-cr-wi1` (pi) ≠ code lens `terra` ≠ this Opus security review — distinct principals on one Gitea account, separated by lane/process; I am THE security gate for this WI. Earned from product code, not the author's tests. `tea`/`pr-review.sh approve` broken for this surface; this durable comment is the formal SECREV approval record. Reviewed head `77b137cc`. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.
Author
Owner

INDEPENDENT CODE REVIEW (terra) — WI-2 #829 head 77b137cc

Verdict: REQUEST CHANGES

Reviewed exact head: 77b137ccc04b5be035cac5ca21bbbf3df8b94f97 against authoritative main 8ec67a11. Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). 2 blocking findings.

  1. [BLOCKER] packages/mosaic/src/commands/launch.ts:848-861 and packages/mosaic/src/commands/claudex.ts:450-457 — the mosaic claudex / mosaic yolo claudex Claude-harness launch bypasses the new lease-gated launcher. First-class claude and pi now use execLeaseGatedRuntime, but the separately registered claudex path builds an isolated CLAUDE_CONFIG_DIR and directly executes claude. It neither registers a broker anchor nor injects MOSAIC_LEASE_SESSION_ID; its isolated config is also not populated with the all-tools hook. Thus this supported Claude Code harness can execute consequential tools without the broker authorize_tool decision, violating the whole-mutator-class requirement. Route claudex through the same register-and-exec gate while preserving its isolated proxy environment, or explicitly reject it until it is gated. Add a behavioral regression covering both normal and YOLO claudex dispatch and denial when no verified lease exists.

  2. [BLOCKER] New-code coverage gate is not met independently. The required coverage is ≥85% on new code, but independently instrumenting the actual focused broker/gate suites reports 82% combined branch coverage. More specifically, the entirely new packages/mosaic/framework/tools/lease-broker/launch-runtime.py is only 48% in the branch-aware report (35/63 statements exercised); successful os.execvpe coverage is lost and important validation/error paths are untested. mutator-gate.py is 79% branch coverage. The reported 88% cannot be accepted as evidence for this head. Refactor/extend the harness so successful register-before-exec and failure branches are durably collected, add the missing behavior tests, and enforce/report the configured threshold for each new broker-gate executable.

What passed

  • Actual broker state machine correctly makes revoke-first and token-consume/commit-before-promote structurally ordered under the broker lock; pending authority remains denied.
  • Focused tests passed: mutator gate + WI-1 broker 43/43; Python state suite 10/10; root pnpm test 43/43 Turbo tasks (Mosaic 72/72 files, 1,377/1,377 tests); root typecheck/lint/format and git diff --check green; PR CI pipeline 1911 green.
  • Red-first history is present: test commits 74fc62e4, 439f5f4b precede implementation 77b137cc, and implementation did not modify the new acceptance test. No --no-verify found.

The mandatory Opus-SECREV remains separate and is not replaced by this code review.

## INDEPENDENT CODE REVIEW (terra) — WI-2 #829 head 77b137cc **Verdict: REQUEST CHANGES** Reviewed exact head: `77b137ccc04b5be035cac5ca21bbbf3df8b94f97` against authoritative `main` `8ec67a11`. Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). **2 blocking findings.** 1. **[BLOCKER] `packages/mosaic/src/commands/launch.ts:848-861` and `packages/mosaic/src/commands/claudex.ts:450-457` — the `mosaic claudex` / `mosaic yolo claudex` Claude-harness launch bypasses the new lease-gated launcher.** First-class `claude` and `pi` now use `execLeaseGatedRuntime`, but the separately registered claudex path builds an isolated `CLAUDE_CONFIG_DIR` and directly executes `claude`. It neither registers a broker anchor nor injects `MOSAIC_LEASE_SESSION_ID`; its isolated config is also not populated with the all-tools hook. Thus this supported Claude Code harness can execute consequential tools without the broker `authorize_tool` decision, violating the whole-mutator-class requirement. Route claudex through the same register-and-exec gate while preserving its isolated proxy environment, or explicitly reject it until it is gated. Add a behavioral regression covering both normal and YOLO claudex dispatch and denial when no verified lease exists. 2. **[BLOCKER] New-code coverage gate is not met independently.** The required coverage is ≥85% on new code, but independently instrumenting the actual focused broker/gate suites reports `82%` combined branch coverage. More specifically, the entirely new `packages/mosaic/framework/tools/lease-broker/launch-runtime.py` is only `48%` in the branch-aware report (35/63 statements exercised); successful `os.execvpe` coverage is lost and important validation/error paths are untested. `mutator-gate.py` is 79% branch coverage. The reported 88% cannot be accepted as evidence for this head. Refactor/extend the harness so successful register-before-exec and failure branches are durably collected, add the missing behavior tests, and enforce/report the configured threshold for each new broker-gate executable. ### What passed - Actual broker state machine correctly makes revoke-first and token-consume/commit-before-promote structurally ordered under the broker lock; pending authority remains denied. - Focused tests passed: mutator gate + WI-1 broker `43/43`; Python state suite `10/10`; root `pnpm test` `43/43` Turbo tasks (Mosaic `72/72` files, `1,377/1,377` tests); root typecheck/lint/format and `git diff --check` green; PR CI pipeline 1911 green. - Red-first history is present: test commits `74fc62e4`, `439f5f4b` precede implementation `77b137cc`, and implementation did not modify the new acceptance test. No `--no-verify` found. The mandatory Opus-SECREV remains separate and is not replaced by this code review.
jason.woltje added 2 commits 2026-07-18 04:13:40 +00:00
Author
Owner

OPUS RE-SECREV (MANDATORY, auth surface) — WI-2 #829 head 6a3505df

Verdict: SECREV GO — reviewed exact head 6a3505dfbc2cfeee6b651cea84f7050b90f4f7860 blocking security findings.

This is not a re-pass of the frame. My round-1 GO (comment 18088) was issued with the claudex launch path walking around the gate — a whole launch path with no broker anchor, no MOSAIC_LEASE_SESSION_ID, and no all-tools hook in its isolated CLAUDE_CONFIG_DIR. That is squarely a SECREV miss and I own it. This pass affirmatively proves, from the product code on the remediated head, that zero walk-arounds remain. Zero evidence transfer; the build's green tests are process, not evidence. Base = main 8ec67a11 (clean merge-base); reviewed = git diff main...HEAD + all changed files. Reviewed-SHA == merge head.

RIDER S — every runtime launch entry enumerated; no Claude/Pi path reaches a consequential mutator without a broker-verified lease

Launch entry Dispatch (launch.ts) Exec path Gated
mosaic claude launchRuntime('claude') execLeaseGatedRuntime('claude') :766
mosaic yolo claude same, yolo=true execLeaseGatedRuntime('claude')
mosaic pi launchRuntime('pi') execLeaseGatedRuntime('pi') :801
mosaic yolo pi same, yolo=true execLeaseGatedRuntime('pi')
mosaic claudex :1121 claudexHandler → launchClaudexProduction(args,false) adapter.execLeaseGated → execLeaseGatedRuntime('claude') :863
mosaic yolo claudex :1135 resolved BEFORE the runtime allowlist → launchClaudexProduction(args,true) same
mosaic [yolo] codex execRuntime('codex') :779 ungated out-of-scope¹
mosaic [yolo] opencode execRuntime('opencode') :786 ungated out-of-scope¹

Whole-codebase walk-around hunt (independently re-derived): the only claude/pi process launches in packages/mosaic/src are the three execLeaseGatedRuntime(...) calls above. There is no execRuntime('claude', …) or execRuntime('pi', …) anywhere. The round-1 bypass is closed structurally: ClaudexHarnessAdapter.exec was renamed to execLeaseGated and wired to execLeaseGatedRuntime('claude', …) (launch.ts:863; claudex.ts:471-478,538). TypeScript would not compile a residual exec adapter. yolo claudex is dispatched before the runtime allowlist check (launch.ts:1135), so it cannot fall through to a plain runtime or an error path.

The now-gated claudex path — RIDER S's four properties proven in code

mosaic [yolo] claudexlaunchClaudex (claudex.ts) → adapter.execLeaseGated(cliArgs, env) (:538) with env = the composed isolated env (creds stripped, CLAUDE_CONFIG_DIR = isolated) → execLeaseGatedRuntime('claude', cmdArgs, env)execRuntime('python3', [launch-runtime.py, --runtime claude, --, claude, …], {…env, MOSAIC_LEASE_BROKER_SOCKET, MOSAIC_RUNTIME_GENERATION}):

  1. Registers the broker anchor (kernel-anchored, not caller-asserted): launch-runtime.py sends register_anchor (no session_id); the broker reads the connecting process's SO_PEERCRED and mints the id (secrets.token_hex); register_anchor still refuses any client-supplied session_id (daemon.py CALLER_SESSION_ID_REFUSED, unchanged). The chain mosaic → python3(launch-runtime) → execvpe claude keeps one PID, so the registered anchor is Claude's PID; gate hooks (Claude's descendants) authenticate up to it.
  2. Injects MOSAIC_LEASE_SESSION_ID: launch-runtime.py sets environment["MOSAIC_LEASE_SESSION_ID"] = session_id (the freshly minted id, overwriting any inherited stale value) before execvpe. A stale parent id cannot reach the gate; even if it did, authenticate would fail on SO_PEERCRED ancestry → deny.
  3. Routes consequential tools through authorize_tool — all-tools hook in the isolated CLAUDE_CONFIG_DIR: prepareConfig(configDir) = ensureClaudexMutatorGateSettings (claudex.ts:311-357) unshifts the matcher:".*" PreToolUse hook running mutator-gate.py --runtime claude into the isolated dir's settings.json, and Claude is pointed at that exact dir via CLAUDE_CONFIG_DIR. It fails closed on a symlinked or malformed settings file / non-array PreToolUse. Because claudex uses an isolated config dir (not ~/.claude), this is the fix's crux — the isolated dir now carries the same all-tools gate.
  4. Fails closed with no verified lease: for a consequential tool, authorize_tool returns MUTATOR_UNVERIFIED/LEASE_EXPIREDmutator-gate.py exit 2 → tool blocked. And at launch time, if the broker is unreachable, register_anchor fails → launch-runtime.py returns 1 → execRuntime exits 1 → Claude never launches (no ungated harness). Missing identity env / malformed reply / timeout all deny.

If the classification path is read-only (Read/Grep/Glob/Ls/Find or mosaic_context_recover) it is allowed; every other tool (Bash/Edit/Write/NotebookEdit, unknown, MCP mcp__*) is consequential and blocked unless VERIFIED — by whole class, never by parsing a Bash string.

Broker core + gate executables — re-confirmed not regressed

  • daemon.py is byte-identical to my round-1 proof head 77b137cc (verified: git diff 77b137cc..HEAD -- daemon.py is empty). So revoke-first / promote-last, TTL authoritative at use (time.monotonic() >= expires_at → revoke+deny), kernel-anchored-only trust (no client-asserted principal/verified field), and no fail-open branch (every error/exception → deny; StateCommitUncertain terminates the daemon) are unchanged from the fully-proven round-1 state, under the WI-1 broker_lock serialization.
  • mutator-gate.py / launch-runtime.py deltas are behavior-preserving DI refactors (inject argv/environ/stream/request/execute for testability; production __main__ binds the real os.environ/sys.stdin/broker_request/os.execvpe). Fail-closed tails intact: gate allows only on reply.ok is True and reply.decision == "allow", else deny (exit 2); launcher denies launch on registration failure. The added return 0 in launch-runtime.main is unreachable in production (execvpe replaces the process) — test-double affordance only.

Non-blocking observations (informational — NOT change requests; stated for transparency after the round-1 miss)

  1. ¹ Out-of-scope runtimes: codex and opencode launch ungated. WI-2 is explicitly "the framework-native authorization boundary for Claude and Pi" (mutator-class-gate.md L2); codex/opencode are distinct harnesses without the PreToolUse-hook / Pi-tool_call mechanism, so the Claude/Pi gate does not apply to them. This is documented scope, not a walk-around of the Claude/Pi gate — flagged so the coordinator holds it consciously, not silently.
  2. Yolo relies on hooks firing: yolo appends --dangerously-skip-permissions. The gate's efficacy for yolo claude/yolo claudex depends on Claude PreToolUse hooks executing under that flag (they do — the flag auto-approves permission decisions, it does not disable hooks; this is the same premise the existing prevent-memory-write.sh hook and all fleet-yolo protections already rely on). Pi's gate is a tool_call extension unaffected by the Claude-only flag. Harness/hook integrity is the ratified T-C boundary.
  3. Assurance boundary (carried from round 1): WI-2 is a mechanical whole-class gate; it attests the kernel anchor + single-use promotion-token cycle, not the semantic authenticity of a verification (receipts/obedience are WI-future per doc L26). GO means "no Claude/Pi launch path reaches a consequential mutator without completing the broker-owned revoke-first/promote-last cycle, and the gate fails closed," not "a fully-compromised model can never re-verify."

Disclosed residuals (acknowledged, NOT flagged)

Harness-side tail-preserving middle-drop (planner ruling b7bbb6ea); server-side branch-protection backstop (WI-7); ratified bounded T-A dual-observer-miss-within-TTL; and T-C (hook/extension absence, runtime outside the gated launcher, ptrace/same-UID broker replacement).


Independence (Gate 16 — by lane/process): author ms-cr-wi1 ≠ code lens terra ≠ this Opus security review — distinct principals on one Gitea account by lane/process; I am THE security gate for this WI. Earned from product code, not the author's tests. tea/pr-review.sh approve broken for this surface; this durable comment is the formal RE-SECREV approval record. Reviewed head 6a3505df. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.

## OPUS RE-SECREV (MANDATORY, auth surface) — WI-2 #829 head 6a3505df **Verdict: ✅ SECREV GO** — reviewed exact head `6a3505dfbc2cfeee6b651cea84f7050b90f4f786` — **0 blocking security findings**. This is **not** a re-pass of the frame. My round-1 GO (comment 18088) was issued *with the `claudex` launch path walking around the gate* — a whole launch path with no broker anchor, no `MOSAIC_LEASE_SESSION_ID`, and no all-tools hook in its isolated `CLAUDE_CONFIG_DIR`. That is squarely a SECREV miss and I own it. This pass **affirmatively proves, from the product code on the remediated head, that zero walk-arounds remain.** Zero evidence transfer; the build's green tests are process, not evidence. Base = `main` `8ec67a11` (clean merge-base); reviewed = `git diff main...HEAD` + all changed files. Reviewed-SHA == merge head. ### RIDER S — every runtime launch entry enumerated; no Claude/Pi path reaches a consequential mutator without a broker-verified lease | Launch entry | Dispatch (launch.ts) | Exec path | Gated | |---|---|---|---| | `mosaic claude` | `launchRuntime('claude')` | `execLeaseGatedRuntime('claude')` :766 | ✅ | | `mosaic yolo claude` | same, `yolo=true` | `execLeaseGatedRuntime('claude')` | ✅ | | `mosaic pi` | `launchRuntime('pi')` | `execLeaseGatedRuntime('pi')` :801 | ✅ | | `mosaic yolo pi` | same, `yolo=true` | `execLeaseGatedRuntime('pi')` | ✅ | | `mosaic claudex` | `:1121 claudexHandler → launchClaudexProduction(args,false)` | adapter.`execLeaseGated → execLeaseGatedRuntime('claude')` :863 | ✅ | | `mosaic yolo claudex` | `:1135 resolved BEFORE the runtime allowlist → launchClaudexProduction(args,true)` | same | ✅ | | `mosaic [yolo] codex` | `execRuntime('codex')` :779 | ungated | out-of-scope¹ | | `mosaic [yolo] opencode` | `execRuntime('opencode')` :786 | ungated | out-of-scope¹ | **Whole-codebase walk-around hunt (independently re-derived):** the only `claude`/`pi` process launches in `packages/mosaic/src` are the three `execLeaseGatedRuntime(...)` calls above. There is **no** `execRuntime('claude', …)` or `execRuntime('pi', …)` anywhere. The round-1 bypass is closed structurally: `ClaudexHarnessAdapter.exec` was **renamed to `execLeaseGated`** and wired to `execLeaseGatedRuntime('claude', …)` (launch.ts:863; claudex.ts:471-478,538). TypeScript would not compile a residual `exec` adapter. `yolo claudex` is dispatched *before* the runtime allowlist check (launch.ts:1135), so it cannot fall through to a plain runtime or an error path. ### The now-gated claudex path — RIDER S's four properties proven in code `mosaic [yolo] claudex` → `launchClaudex` (claudex.ts) → `adapter.execLeaseGated(cliArgs, env)` (:538) with `env` = the composed isolated env (creds stripped, `CLAUDE_CONFIG_DIR` = isolated) → `execLeaseGatedRuntime('claude', cmdArgs, env)` → `execRuntime('python3', [launch-runtime.py, --runtime claude, --, claude, …], {…env, MOSAIC_LEASE_BROKER_SOCKET, MOSAIC_RUNTIME_GENERATION})`: 1. **Registers the broker anchor (kernel-anchored, not caller-asserted):** `launch-runtime.py` sends `register_anchor` (no `session_id`); the broker reads the connecting process's `SO_PEERCRED` and mints the id (`secrets.token_hex`); `register_anchor` still refuses any client-supplied `session_id` (daemon.py `CALLER_SESSION_ID_REFUSED`, unchanged). The chain `mosaic → python3(launch-runtime) → execvpe claude` keeps one PID, so the registered anchor **is** Claude's PID; gate hooks (Claude's descendants) authenticate up to it. 2. **Injects `MOSAIC_LEASE_SESSION_ID`:** `launch-runtime.py` sets `environment["MOSAIC_LEASE_SESSION_ID"] = session_id` (the freshly minted id, **overwriting** any inherited stale value) before `execvpe`. A stale parent id cannot reach the gate; even if it did, `authenticate` would fail on `SO_PEERCRED` ancestry → deny. 3. **Routes consequential tools through `authorize_tool` — all-tools hook in the isolated `CLAUDE_CONFIG_DIR`:** `prepareConfig(configDir)` = `ensureClaudexMutatorGateSettings` (claudex.ts:311-357) **unshifts** the `matcher:".*"` PreToolUse hook running `mutator-gate.py --runtime claude` into the isolated dir's `settings.json`, and Claude is pointed at that exact dir via `CLAUDE_CONFIG_DIR`. It fails **closed** on a symlinked or malformed settings file / non-array PreToolUse. Because claudex uses an isolated config dir (not `~/.claude`), this is the fix's crux — the isolated dir now carries the same all-tools gate. 4. **Fails closed with no verified lease:** for a consequential tool, `authorize_tool` returns `MUTATOR_UNVERIFIED`/`LEASE_EXPIRED` → `mutator-gate.py` exit 2 → tool blocked. And at **launch** time, if the broker is unreachable, `register_anchor` fails → `launch-runtime.py` returns 1 → `execRuntime` exits 1 → **Claude never launches** (no ungated harness). Missing identity env / malformed reply / timeout all deny. If the classification path is read-only (`Read/Grep/Glob/Ls/Find` or `mosaic_context_recover`) it is allowed; every other tool (`Bash/Edit/Write/NotebookEdit`, unknown, MCP `mcp__*`) is consequential and blocked unless VERIFIED — by whole class, never by parsing a `Bash` string. ### Broker core + gate executables — re-confirmed not regressed - **`daemon.py` is byte-identical to my round-1 proof head `77b137cc`** (verified: `git diff 77b137cc..HEAD -- daemon.py` is empty). So **revoke-first / promote-last**, **TTL authoritative at use** (`time.monotonic() >= expires_at` → revoke+deny), **kernel-anchored-only trust** (no client-asserted principal/verified field), and **no fail-open branch** (every error/exception → deny; `StateCommitUncertain` terminates the daemon) are unchanged from the fully-proven round-1 state, under the WI-1 `broker_lock` serialization. - **`mutator-gate.py` / `launch-runtime.py` deltas are behavior-preserving DI refactors** (inject `argv/environ/stream/request/execute` for testability; production `__main__` binds the real `os.environ`/`sys.stdin`/`broker_request`/`os.execvpe`). Fail-closed tails intact: gate allows **only** on `reply.ok is True and reply.decision == "allow"`, else `deny` (exit 2); launcher denies launch on registration failure. The added `return 0` in `launch-runtime.main` is unreachable in production (`execvpe` replaces the process) — test-double affordance only. ### Non-blocking observations (informational — NOT change requests; stated for transparency after the round-1 miss) 1. **¹ Out-of-scope runtimes:** `codex` and `opencode` launch ungated. WI-2 is explicitly "the framework-native authorization boundary **for Claude and Pi**" (`mutator-class-gate.md` L2); codex/opencode are distinct harnesses without the PreToolUse-hook / Pi-`tool_call` mechanism, so the Claude/Pi gate does not apply to them. This is documented scope, not a walk-around of the Claude/Pi gate — flagged so the coordinator holds it consciously, not silently. 2. **Yolo relies on hooks firing:** `yolo` appends `--dangerously-skip-permissions`. The gate's efficacy for `yolo claude`/`yolo claudex` depends on Claude PreToolUse hooks executing under that flag (they do — the flag auto-approves *permission* decisions, it does not disable hooks; this is the same premise the existing `prevent-memory-write.sh` hook and all fleet-yolo protections already rely on). Pi's gate is a `tool_call` extension unaffected by the Claude-only flag. Harness/hook integrity is the ratified **T-C** boundary. 3. **Assurance boundary (carried from round 1):** WI-2 is a *mechanical* whole-class gate; it attests the kernel anchor + single-use promotion-token cycle, not the semantic authenticity of a verification (receipts/obedience are WI-future per doc L26). GO means "no Claude/Pi launch path reaches a consequential mutator without completing the broker-owned revoke-first/promote-last cycle, and the gate fails closed," not "a fully-compromised model can never re-verify." ### Disclosed residuals (acknowledged, NOT flagged) Harness-side tail-preserving middle-drop (planner ruling `b7bbb6ea`); server-side branch-protection backstop (WI-7); ratified bounded T-A dual-observer-miss-within-TTL; and T-C (hook/extension absence, runtime outside the gated launcher, ptrace/same-UID broker replacement). --- **Independence (Gate 16 — by lane/process):** author `ms-cr-wi1` ≠ code lens `terra` ≠ this Opus security review — distinct principals on one Gitea account by lane/process; I am THE security gate for this WI. Earned from product code, not the author's tests. `tea`/`pr-review.sh approve` broken for this surface; this durable comment is the formal RE-SECREV approval record. Reviewed head `6a3505df`. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.
Author
Owner

INDEPENDENT CODE RE-REVIEW (terra) — WI-2 #829 head 6a3505df

Verdict: REQUEST CHANGES

Reviewed exact head 6a3505dfbc2cfeee6b651cea84f7050b90f4f786 against requested base 8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201. Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). The prior Claudex and coverage blockers are remediated, but one whole-class authorization bypass remains.

  1. [BLOCKER] packages/mosaic/framework/tools/prdy/prdy-init.sh:88-90 and packages/mosaic/framework/tools/prdy/prdy-update.sh:76-78 directly exec claude --dangerously-skip-permissions. These are supported Mosaic runtime launchers (PRD creation/update) and launch a consequential-tools-capable Claude process without launch-runtime.py, broker anchor registration, MOSAIC_LEASE_SESSION_ID, or a guaranteed all-tools hook. They therefore violate B1's no runtime launch path requirement: an UNVERIFIED PRDY Claude session can reach consequential tools. Route these through the same broker-gated launch boundary (or reject Claude PRDY launches fail-closed), and add behavioral regression coverage proving both init and update routes cannot run without broker registration and that an unverified mutator is denied. The same audit should cover other direct claude -p automation launchers (for example framework/tools/qa/remediation-hook-handler.sh:62) rather than treating the mosaic CLI dispatcher as the only launch surface.

Remediation verified

  • mosaic claudex and mosaic yolo claudex now use the shared execLeaseGatedRuntime seam; the real-socket acceptance probes prove a broker-minted 64-hex session, isolated all-tools Claude hook, and status-2 denial for unverified Bash in both modes.
  • Broker revoke-first and consume/commit-before-promote ordering remains intact; 46/46 focused real-socket acceptance tests and both Python suites (10/10 each) passed.
  • Independent branch-aware instrumentation of those focused tests (including broker subprocesses) reports: launch-runtime.py 17/18 = 94.4%, mutator-gate.py 22/22 = 100%, and WI-2-added daemon.py branch arcs 35/40 = 87.5%. B2 is satisfied.
  • Full repository gates passed: pnpm test 43/43 Turbo tasks (Mosaic 72 files / 1,381 tests), typecheck 42/42, lint 23/23, format, and git diff --check. PR pipeline 1912 is green at 6a3505df.
  • Red-first ordering is present (74fc62e4, 439f5f4b, then implementation 77b137cc; remediation test 046896c6 precedes 6a3505df); no --no-verify evidence found.

The mandatory Opus re-SECREV is separate and is not replaced by this code review.

## INDEPENDENT CODE RE-REVIEW (terra) — WI-2 #829 head 6a3505df **Verdict: REQUEST CHANGES** Reviewed exact head `6a3505dfbc2cfeee6b651cea84f7050b90f4f786` against requested base `8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201`. Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). The prior Claudex and coverage blockers are remediated, but one whole-class authorization bypass remains. 1. **[BLOCKER] `packages/mosaic/framework/tools/prdy/prdy-init.sh:88-90` and `packages/mosaic/framework/tools/prdy/prdy-update.sh:76-78` directly `exec claude --dangerously-skip-permissions`.** These are supported Mosaic runtime launchers (PRD creation/update) and launch a consequential-tools-capable Claude process without `launch-runtime.py`, broker anchor registration, `MOSAIC_LEASE_SESSION_ID`, or a guaranteed all-tools hook. They therefore violate B1's *no runtime launch path* requirement: an UNVERIFIED PRDY Claude session can reach consequential tools. Route these through the same broker-gated launch boundary (or reject Claude PRDY launches fail-closed), and add behavioral regression coverage proving both init and update routes cannot run without broker registration and that an unverified mutator is denied. The same audit should cover other direct `claude -p` automation launchers (for example `framework/tools/qa/remediation-hook-handler.sh:62`) rather than treating the `mosaic` CLI dispatcher as the only launch surface. ### Remediation verified - `mosaic claudex` and `mosaic yolo claudex` now use the shared `execLeaseGatedRuntime` seam; the real-socket acceptance probes prove a broker-minted 64-hex session, isolated all-tools Claude hook, and status-2 denial for unverified `Bash` in both modes. - Broker revoke-first and consume/commit-before-promote ordering remains intact; `46/46` focused real-socket acceptance tests and both Python suites (`10/10` each) passed. - Independent branch-aware instrumentation of those focused tests (including broker subprocesses) reports: `launch-runtime.py` **17/18 = 94.4%**, `mutator-gate.py` **22/22 = 100%**, and WI-2-added `daemon.py` branch arcs **35/40 = 87.5%**. B2 is satisfied. - Full repository gates passed: `pnpm test` 43/43 Turbo tasks (Mosaic 72 files / 1,381 tests), typecheck 42/42, lint 23/23, format, and `git diff --check`. PR pipeline 1912 is green at `6a3505df`. - Red-first ordering is present (`74fc62e4`, `439f5f4b`, then implementation `77b137cc`; remediation test `046896c6` precedes `6a3505df`); no `--no-verify` evidence found. The mandatory Opus re-SECREV is separate and is not replaced by this code review.
jason.woltje added 2 commits 2026-07-18 04:45:24 +00:00
Author
Owner

INDEPENDENT CODE RE-REVIEW R3 (terra) — WI-2 #829 head 1792b793

Verdict: REQUEST CHANGES

Reviewed exact head 1792b7934dda7eff64a207b8b0edb9c460d4164b against requested base 8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201. Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). 1 blocking finding.

  1. [BLOCKER — RIDER E] packages/mosaic/framework/tools/lease-broker/check-runtime-launches.py:40-48,92-111 treats any occurrence of launch-runtime.py on a line as proof that a direct Claude/Pi launch is gated. I performed the requested temporary-launcher check. A plain exec claude --dangerously-skip-permissions "terra-r3" correctly makes the guard red. But this still-ungated executable launcher with an inline marker:

    exec claude --dangerously-skip-permissions "terra-r3" # launch-runtime.py
    

    makes the production guard exit 0 and print runtime launch inventory: 15 gated/15 total. is_gated_line() never distinguishes comments or validates an invocation/arguments; scan_text() suppresses the direct match solely because regex line 42 matched the comment. The permanent guard can therefore be trivially (and accidentally) bypassed by a newly added ungated launcher, so it cannot serve as the required load-bearing enforcement. Parse executable syntax or strip comments before classification, require a real wrapper invocation with the necessary runtime/command boundary, and add this exact evasion as a permanent red regression.

Independently verified positives (not sufficient for approval)

  • Mechanical sweep found the 14 current production launch sites; the prior PRDY init/update and QA remediation direct entries now invoke launch-runtime.py, and Mosaic/coord/fleet/orchestrator paths reach the same broker registration boundary.
  • Real-socket acceptance passed 49/49: PRDY init/update and QA remediation fail before fake-runtime execution with no broker, receive a broker-minted 64-hex session with the broker, and deny unverified Bash. Claudex normal/YOLO and broker revoke-first / consume-before-promote behavior remain green.
  • Independent branch coverage: launch-runtime.py 18/18 = 100%, mutator-gate.py 22/22 = 100%, WI-2 daemon.py delta 35/40 = 87.5%, guard 36/38 = 94.7%. Coverage does not cure the proven guard false-negative.
  • Local full gates passed: pnpm test 43/43 Turbo tasks (Mosaic 72 files / 1,384 tests), typecheck 42/42, lint 23/23, format, and diff check. R3 red-first ordering is correct: 7f3418fa precedes 1792b793; no --no-verify evidence found. Pipeline 1913 was still running when this review posted.

The mandatory Opus-SECREV is separate and is not replaced by this code review.

## INDEPENDENT CODE RE-REVIEW R3 (terra) — WI-2 #829 head 1792b793 **Verdict: REQUEST CHANGES** Reviewed exact head `1792b7934dda7eff64a207b8b0edb9c460d4164b` against requested base `8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201`. Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). **1 blocking finding.** 1. **[BLOCKER — RIDER E] `packages/mosaic/framework/tools/lease-broker/check-runtime-launches.py:40-48,92-111` treats any occurrence of `launch-runtime.py` on a line as proof that a direct Claude/Pi launch is gated.** I performed the requested temporary-launcher check. A plain `exec claude --dangerously-skip-permissions "terra-r3"` correctly makes the guard red. But this still-ungated executable launcher with an inline marker: ```sh exec claude --dangerously-skip-permissions "terra-r3" # launch-runtime.py ``` makes the production guard exit **0** and print `runtime launch inventory: 15 gated/15 total`. `is_gated_line()` never distinguishes comments or validates an invocation/arguments; `scan_text()` suppresses the direct match solely because regex line 42 matched the comment. The permanent guard can therefore be trivially (and accidentally) bypassed by a newly added ungated launcher, so it cannot serve as the required load-bearing enforcement. Parse executable syntax or strip comments before classification, require a real wrapper invocation with the necessary runtime/command boundary, and add this exact evasion as a permanent red regression. ### Independently verified positives (not sufficient for approval) - Mechanical sweep found the 14 current production launch sites; the prior PRDY init/update and QA remediation direct entries now invoke `launch-runtime.py`, and Mosaic/coord/fleet/orchestrator paths reach the same broker registration boundary. - Real-socket acceptance passed `49/49`: PRDY init/update and QA remediation fail before fake-runtime execution with no broker, receive a broker-minted 64-hex session with the broker, and deny unverified `Bash`. Claudex normal/YOLO and broker revoke-first / consume-before-promote behavior remain green. - Independent branch coverage: `launch-runtime.py` **18/18 = 100%**, `mutator-gate.py` **22/22 = 100%**, WI-2 `daemon.py` delta **35/40 = 87.5%**, guard **36/38 = 94.7%**. Coverage does not cure the proven guard false-negative. - Local full gates passed: `pnpm test` 43/43 Turbo tasks (Mosaic 72 files / 1,384 tests), typecheck 42/42, lint 23/23, format, and diff check. R3 red-first ordering is correct: `7f3418fa` precedes `1792b793`; no `--no-verify` evidence found. Pipeline 1913 was still running when this review posted. The mandatory Opus-SECREV is separate and is not replaced by this code review.
Author
Owner

OPUS RE-SECREV R3 (MANDATORY, auth surface) — WI-2 #829 head 1792b793

Verdict: SECREV GO — reviewed exact head 1792b7934dda7eff64a207b8b0edb9c460d4164b0 blocking security findings.

Fresh pass, zero evidence transfer. My round-2 GO (18097) missed the prdy/qa launchers a prose enumeration could not prove complete; per the brief I did not re-enumerate by eye — I attacked the choke-point wrapper and the new guard adversarially, and independently re-scanned the whole repository. Base = main 8ec67a11 (clean merge-base); reviewed = git diff main...HEAD + all changed files. Reviewed-SHA == merge head. The build's green tests are process, not evidence.

RIDER 1 — CHOKE-POINT integrity (the actual runtime security control): PROVEN

The single wrapper every gated path funnels through is launch-runtime.py:

  • TS execLeaseGatedRuntimeexecRuntime('python3', [launch-runtime.py, --runtime X, --, X, …]);
  • prdy-init.sh / prdy-update.sh / remediation-hook-handler.sh → python3 …/launch-runtime.py --runtime claude -- claude …;
  • coord resolveLaunchCommand['mosaic','claude',…]execLeaseGatedRuntimelaunch-runtime.py;
  • fleet start-agent-session.sh + orchestrator session-run.shmosaic [yolo] <runtime> → same.

launch-runtime.py is byte-identical to the head I fully proved in round 2 (git diff 6a3505df..HEAD does not touch it). It enforces, on every path through it, with no flag/env/arg that skips it: register_anchor is unconditional before exec; the broker reads SO_PEERCRED and mints the session id server-side (register_anchor refuses any client session_idCALLER_SESSION_ID_REFUSED); the session id is validated (64-hex) and injected/overwritten into the child env; and on any registration failure (missing socket env KeyError, connect refusal/timeout OSError, malformed/ok!=true/bad-id reply ValueError) it returns 1 and never execs — the runtime does not launch at all (fail-closed). A tool that reaches the wrapper without a verified lease is then denied at the all-tools gate hook (authorize_toolMUTATOR_UNVERIFIED/LEASE_EXPIRED → exit 2). coord additionally throws if a custom Claude command is neither mosaic claude nor mosaic yolo claude ("…so lease registration cannot be bypassed") — closing the operator-config indirection. There is no fail-open branch inside the wrapper.

RIDER 2 — GUARD evasion (adversarial): guard is a sound REGRESSION backstop, necessarily NOT adversary-complete

I ran the guard (check-runtime-launches.py --root .14 gated / 14 total, exit 0, wired into CI via pnpm testtest:framework-shell… --root ../..) and then independently re-scanned the entire repo myself (all top-level dirs, all file types, systemd units, indirection forms). Result: zero real ungated claude/pi agent launchers exist now — every one of the 14 sites routes through the choke-point, including the fleet path (LAUNCH_COMMAND=(mosaic yolo "$MOSAIC_AGENT_RUNTIME"); its test asserts MOSAIC_AGENT_COMMAND is diagnostic-only, not a launch override). So the whole-class AC holds on this head, verified independently, not merely by the author's guard.

The guard is regex static analysis, so it is — and provably cannot be — adversary-complete (deciding whether arbitrary code launches a binary is undecidable). Concrete evasion classes I confirmed by attacking its logic (all require committing source that survives review+merge+deploy):

  • Unscanned roots — it scans only packages/apps/plugins/tools; the repo also has scripts/ deploy/ docker/ infra/ profiles/ briefs/ guides/ docs/ scratchpads/. A launcher placed there is not scanned. (I checked all of them — none exists today.)
  • Unscanned suffixes / extensionless files — a script with no extension (e.g. the existing mosaic-ensure-excalidraw, which carries a claude mcp add config subcommand — not an agent launch, but illustrates the skip), a Makefile, Dockerfile, or .service unit is not scanned.
  • Indirection — variable-held binary (BIN=claude; exec "$BIN"), a non-runtime-named var in a process API (spawn(cmd, …)), sh -c "claude -p …", env claude -p, nohup claude … — none match DIRECT_PATTERNS.
  • Continuation-suppressiongated_continuation marks lines after any gated line ending in \ as gated; a crafted gated-looking line ending in \ above a real direct launch suppresses detection.

Security assessment of these evasions: they are the malicious/careless-committer threat, whose irreducible backstop is exactly the ratified WI-7 server-side branch protection + Gate-16 independent review (a committer who can add an evasive launcher can equally edit the guard or the choke-point). Critically, guard evasion is not a runtime escalation path for a compromised model/tool — a live gated session cannot add-and-deploy a new source launcher to escape its own broker gate. So the runtime security control (choke-point + broker gate) is unaffected by guard incompleteness. The guard's real, delivered value is catching the accidental regressions that actually bit rounds 1–2 (the prdy/qa/coord launchers), and it does that for all natural/literal forms. This is a GO because security rests on the runtime enforcement (proven) + verified current completeness (my independent scan) + the ratified WI-7/review backstop for deliberate future evasion — not on the guard being adversary-complete, which is impossible. The guard should be understood as a regression backstop, not an adversary-complete proof; I flag its evasion classes explicitly so the coordinator holds that consciously.

RIDER 3 — broker-verified-only / revoke-first-promote-last / TTL-at-use / no-fail-open: intact

daemon.py is byte-identical to my round-1 proof head 77b137cc (empty diff), so the broker core is not regressed: the gate trusts only the kernel-anchored SO_PEERCRED/broker-minted signal (no client-asserted principal or "verified" field exists); revoke-first (begin_verification revokes authority+tokens before minting) and promote-last (token consumption commits before finish_promotion makes VERIFIED visible) hold under the WI-1 broker_lock serialization; TTL is authoritative at use (time.monotonic() >= expires_at → revoke+deny); and there is no fail-open branch (every error/exception denies; StateCommitUncertain terminates the daemon; volatile leases → broker restart = all UNVERIFIED). The prdy/qa/coord routes reuse the same daemon.

Non-blocking observations (informational — NOT change requests)

  1. Guard is a regression backstop, not adversary-complete (evasion classes enumerated above; bounded by WI-7/review). The clean 100% figure reflects the guard's own matching scope, not adversary-completeness.
  2. codex/opencode launch ungated by design — WI-2 is Claude/Pi-only; coord/fleet leave codex as-is. Out of scope, surfaced for conscious hold.
  3. yolo relies on Claude PreToolUse hooks firing under --dangerously-skip-permissions (documented behavior; same premise as prevent-memory-write.sh); prdy/qa use --dangerously-skip-permissions and thus share this assumption. Harness integrity = ratified T-C.
  4. WI-2 remains a mechanical whole-class gate, not an obedience proof (receipts = WI-future per doc).

Disclosed residuals (acknowledged, NOT flagged)

Harness-side tail-preserving middle-drop (planner ruling b7bbb6ea); server-side branch protection (WI-7); ratified bounded T-A dual-observer-miss-within-TTL; T-C (hook/extension absence, runtime outside the gated launcher, ptrace/same-UID broker replacement).


Independence (Gate 16 — by lane/process): author ms-cr-wi1 ≠ code lens terra ≠ this Opus security review — distinct principals on one Gitea account by lane/process; I am THE security gate for this WI. Earned from product code + my own repo scan, not the author's tests. tea/pr-review.sh approve broken for this surface; this durable comment is the formal R3 RE-SECREV approval record. Reviewed head 1792b793. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.

## OPUS RE-SECREV R3 (MANDATORY, auth surface) — WI-2 #829 head 1792b793 **Verdict: ✅ SECREV GO** — reviewed exact head `1792b7934dda7eff64a207b8b0edb9c460d4164b` — **0 blocking security findings**. Fresh pass, zero evidence transfer. My round-2 GO (18097) missed the prdy/qa launchers a prose enumeration could not prove complete; per the brief I did **not** re-enumerate by eye — I attacked the choke-point wrapper and the new guard adversarially, and independently re-scanned the whole repository. Base = `main` `8ec67a11` (clean merge-base); reviewed = `git diff main...HEAD` + all changed files. Reviewed-SHA == merge head. The build's green tests are process, not evidence. ### RIDER 1 — CHOKE-POINT integrity (the actual runtime security control): PROVEN The single wrapper every gated path funnels through is **`launch-runtime.py`**: - TS `execLeaseGatedRuntime` → `execRuntime('python3', [launch-runtime.py, --runtime X, --, X, …])`; - prdy-init.sh / prdy-update.sh / remediation-hook-handler.sh → `python3 …/launch-runtime.py --runtime claude -- claude …`; - coord `resolveLaunchCommand` → `['mosaic','claude',…]` → `execLeaseGatedRuntime` → `launch-runtime.py`; - fleet `start-agent-session.sh` + orchestrator `session-run.sh` → `mosaic [yolo] <runtime>` → same. `launch-runtime.py` is **byte-identical to the head I fully proved in round 2** (`git diff 6a3505df..HEAD` does not touch it). It enforces, on **every** path through it, with **no flag/env/arg that skips it**: `register_anchor` is unconditional before exec; the broker reads `SO_PEERCRED` and mints the session id server-side (`register_anchor` refuses any client `session_id` → `CALLER_SESSION_ID_REFUSED`); the session id is validated (64-hex) and **injected/overwritten** into the child env; and on **any** registration failure (missing socket env `KeyError`, connect refusal/timeout `OSError`, malformed/`ok!=true`/bad-id reply `ValueError`) it returns 1 and **never execs** — the runtime does not launch at all (fail-closed). A tool that reaches the wrapper without a verified lease is then denied at the all-tools gate hook (`authorize_tool` → `MUTATOR_UNVERIFIED`/`LEASE_EXPIRED` → exit 2). coord additionally **throws** if a custom Claude command is neither `mosaic claude` nor `mosaic yolo claude` ("…so lease registration cannot be bypassed") — closing the operator-config indirection. There is no fail-open branch inside the wrapper. ### RIDER 2 — GUARD evasion (adversarial): guard is a sound REGRESSION backstop, necessarily NOT adversary-complete I ran the guard (`check-runtime-launches.py --root .` → **14 gated / 14 total, exit 0**, wired into CI via `pnpm test` → `test:framework-shell` → `… --root ../..`) and then **independently re-scanned the entire repo myself** (all top-level dirs, all file types, systemd units, indirection forms). Result: **zero real ungated claude/pi agent launchers exist now** — every one of the 14 sites routes through the choke-point, including the fleet path (`LAUNCH_COMMAND=(mosaic yolo "$MOSAIC_AGENT_RUNTIME")`; its test asserts `MOSAIC_AGENT_COMMAND` is diagnostic-only, not a launch override). So the whole-class AC **holds on this head, verified independently, not merely by the author's guard**. The guard is regex static analysis, so it is — and provably *cannot* be — adversary-complete (deciding whether arbitrary code launches a binary is undecidable). Concrete evasion classes I confirmed by attacking its logic (all require committing source that survives review+merge+deploy): - **Unscanned roots** — it scans only `packages/apps/plugins/tools`; the repo also has `scripts/ deploy/ docker/ infra/ profiles/ briefs/ guides/ docs/ scratchpads/`. A launcher placed there is not scanned. (I checked all of them — none exists today.) - **Unscanned suffixes / extensionless files** — a script with no extension (e.g. the existing `mosaic-ensure-excalidraw`, which carries a `claude mcp add` config subcommand — not an agent launch, but illustrates the skip), a `Makefile`, `Dockerfile`, or `.service` unit is not scanned. - **Indirection** — variable-held binary (`BIN=claude; exec "$BIN"`), a non-`runtime`-named var in a process API (`spawn(cmd, …)`), `sh -c "claude -p …"`, `env claude -p`, `nohup claude …` — none match `DIRECT_PATTERNS`. - **Continuation-suppression** — `gated_continuation` marks lines after any gated line ending in `\` as gated; a crafted gated-looking line ending in `\` above a real direct launch suppresses detection. **Security assessment of these evasions:** they are the *malicious/careless-committer* threat, whose irreducible backstop is exactly the **ratified WI-7 server-side branch protection + Gate-16 independent review** (a committer who can add an evasive launcher can equally edit the guard or the choke-point). Critically, **guard evasion is not a runtime escalation path for a compromised model/tool** — a live gated session cannot add-and-deploy a new source launcher to escape its own broker gate. So the runtime security control (choke-point + broker gate) is unaffected by guard incompleteness. The guard's real, delivered value is catching the *accidental* regressions that actually bit rounds 1–2 (the prdy/qa/coord launchers), and it does that for all natural/literal forms. **This is a GO because security rests on the runtime enforcement (proven) + verified current completeness (my independent scan) + the ratified WI-7/review backstop for deliberate future evasion — not on the guard being adversary-complete, which is impossible.** The guard should be understood as a regression backstop, not an adversary-complete proof; I flag its evasion classes explicitly so the coordinator holds that consciously. ### RIDER 3 — broker-verified-only / revoke-first-promote-last / TTL-at-use / no-fail-open: intact `daemon.py` is **byte-identical to my round-1 proof head `77b137cc`** (empty diff), so the broker core is not regressed: the gate trusts only the kernel-anchored `SO_PEERCRED`/broker-minted signal (no client-asserted principal or "verified" field exists); **revoke-first** (`begin_verification` revokes authority+tokens before minting) and **promote-last** (token consumption commits before `finish_promotion` makes VERIFIED visible) hold under the WI-1 `broker_lock` serialization; **TTL is authoritative at use** (`time.monotonic() >= expires_at` → revoke+deny); and there is **no fail-open branch** (every error/exception denies; `StateCommitUncertain` terminates the daemon; volatile leases → broker restart = all UNVERIFIED). The prdy/qa/coord routes reuse the same daemon. ### Non-blocking observations (informational — NOT change requests) 1. **Guard is a regression backstop, not adversary-complete** (evasion classes enumerated above; bounded by WI-7/review). The clean 100% figure reflects the guard's own matching scope, not adversary-completeness. 2. **codex/opencode** launch ungated by design — WI-2 is Claude/Pi-only; coord/fleet leave codex as-is. Out of scope, surfaced for conscious hold. 3. **yolo** relies on Claude PreToolUse hooks firing under `--dangerously-skip-permissions` (documented behavior; same premise as `prevent-memory-write.sh`); prdy/qa use `--dangerously-skip-permissions` and thus share this assumption. Harness integrity = ratified T-C. 4. WI-2 remains a *mechanical* whole-class gate, not an obedience proof (receipts = WI-future per doc). ### Disclosed residuals (acknowledged, NOT flagged) Harness-side tail-preserving middle-drop (planner ruling `b7bbb6ea`); server-side branch protection (WI-7); ratified bounded T-A dual-observer-miss-within-TTL; T-C (hook/extension absence, runtime outside the gated launcher, ptrace/same-UID broker replacement). --- **Independence (Gate 16 — by lane/process):** author `ms-cr-wi1` ≠ code lens `terra` ≠ this Opus security review — distinct principals on one Gitea account by lane/process; I am THE security gate for this WI. Earned from product code + my own repo scan, not the author's tests. `tea`/`pr-review.sh approve` broken for this surface; this durable comment is the formal R3 RE-SECREV approval record. Reviewed head `1792b793`. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.
jason.woltje added 4 commits 2026-07-18 05:18:41 +00:00
Author
Owner

OPUS SECREV R4 (MANDATORY, auth surface) — WI-2 #829 head 1eb77c17

Verdict: 🔴 SECREV NO-GO / REQUEST CHANGES — reviewed exact head 1eb77c17f3147d4fa9944f77f1826243135b9cc01 blocking finding (guard false-negative in a claimed-covered evasion class).

Fresh pass, zero evidence transfer. Per the R4 calibration I did not reason abstractly — I built each evasion as a real launcher file under a scanned root and ran check-runtime-launches.py against each. That method surfaced a false-negative that reasoning would have rationalized away — exactly the R3 lesson.


🔴 BLOCKING FINDING — guard misses runtime-variable indirection behind a command prefix

File: packages/mosaic/framework/tools/lease-broker/check-runtime-launches.py:249 (and :263-266)
Class: the brief's "indirection … var-holds-launcher-then-exec" — expected RED, observed GREEN.

Exploit (constructed + run, not reasoned):

# packages/evilpkg/c5c.sh
#!/bin/bash
B=claude
exec "$B" -p "evil"

python3 check-runtime-launches.py --root <dir>exit 0, GREEN (no violation, not even listed). Variants also GREEN: exec $B (unquoted), command "$B", nohup "$B", env X=1 "$B". Only the bare first-token form "$B" -p and eval "$B" are caught (RED).

Root cause — two complementary blind spots that intersect:

  • is_shell_direct_invocation (:229-251) correctly walks past command/exec/nohup/env … prefixes, but its terminal check (:249) is Path(command[index]).name in {"claude","pi"} — it only resolves a literal binary, never a $var against the tracked runtime_variables.
  • executes_runtime_variable (:260-267) correctly resolves runtime_variables, but only for eval "$var" or a bare first-token ^\s*"$var" — it never walks the exec/command/nohup/env prefixes.

So a runtime-holding variable executed behind any command prefix (exec "$B") satisfies neither path. The author's own regressions confirm the gap is untested: variable-exec.sh uses the bare "$launcher" -p form (caught); env/command/nohup-prefix.sh use literal claude/pi (caught). The product of the two dimensions — prefixed + variable — is never asserted, mirroring the R3 comment-marker miss (a form the tests didn't cover). CI is green (Woodpecker #1914, test OK) precisely because the test suite has this hole.

Fix (localized): thread runtime_variables into is_shell_direct_invocation and, at the terminal check, also treat a command-position token that is a $var/${var} reference to a tracked runtime variable as a direct launch (strip ${}/quoting, match the name). Equivalently, have executes_runtime_variable skip the same exec/command/nohup/env+assignment prefixes before matching the reference. Add regressions for exec "$v", exec $v, command "$v", nohup "$v", env A=1 "$v".

Bounded severity (stated honestly so this is calibrated, not overstated): this is a guard/CI-regression-backstop false-negative, NOT a runtime authorization bypass. Even a committed launcher of this shape does not breach the AC at runtime, because (a) the dangerous variant is still caught by primitive-anchoring — F="--dangerously-skip-permissions" trips dangerous-primitive on the assignment line, so yolo-mode can't ride this indirection; and (b) a non-dangerous ungated claude -p is fail-closed at runtime by the global ~/.claude all-tools hook (no MOSAIC_LEASE_SESSION_IDGATE_UNAVAILABLE → every tool denied) and by headless permission mode. No launcher of this shape exists in the tree today (14/14 real sites gated). But the R4 deliverable is guard completeness for these evasion classes, the brief lists this exact class as expected-RED, and the project standard (terra REQUEST-CHANGES on the R3 head for an analogous guard false-negative) is that a demonstrable, fixable false-negative in a claimed-covered class blocks. Consistency requires REQUEST-CHANGES. This is a narrow, clearly-fixable gap — I expect a fast turnaround.


What DID hold (verified by construct-and-run + code read)

Evasion matrix — 9/10 constructed launchers correctly RED; 1 control correctly GREEN; 1 evasion GREEN (the finding):

# Constructed launcher Result Correct?
1 R3 comment-marker (# execLeaseGatedRuntime( then exec claude -p) RED (comment stripped)
2 heredoc-body claude -p RED
3 line-continuation true \claude -p RED
4 non-first ;/&&/| + claude -p RED (×3)
5a r=claude; eval "$r -p" RED
5b $(claude -p) RED
5c B=claude; exec "$B" -p GREEN FINDING
6 --dangerously-skip-permissions outside choke-point RED (dangerous-primitive)
7 gated-marker-in-string then real exec claude -p RED (string didn't suppress)
8 /abs/claude, env … claude, nohup pi (literal) RED (×3)
9 dangerous flag via F="…" var RED (dangerous-primitive on assignment)
10 properly gated launch-runtime.py --dangerous --runtime claude -- GREEN (control)

RIDER B — primitive-anchored vs command-position; genuine defense-in-depth? Yes, genuinely complementary, and I confirmed it empirically: primitive-anchoring caught the dangerous-flag-via-variable case (c9) that command-position parsing would miss, while command-position caught literal-binary launches with no dangerous flag. The migration is real and correct: --dangerously-skip-permissions now appears only in launch-runtime.py (CLAUDE_DANGEROUS_FLAG), injected via --dangerous, which asserts runtime=="claude" and Path(command[0]).name=="claude" before prepending it; the literal was removed from launch.ts, claudex.ts, and prdy scripts (verified by diff). Primitive-anchoring is the more evasion-resistant of the two for the dangerous class (a plain substring is far harder to obfuscate than to fool a command parser), and it is not masking a command-position hole — each covers a class the other misses. The one form neither covers is the non-dangerous prefixed-variable launch (the finding).

Choke-point integrity (RIDER 3) — sound

launch-runtime.py remains the single wrapper; registration is unconditional before exec, SO_PEERCRED kernel-anchored + broker-minted session (refuses caller session_id), session id injected/overwritten, and fail-closed (return 1, no exec) on any registration failure. daemon.py is byte-identical to my round-1 proof (git diff 77b137cc..HEAD empty) → broker-verified-only trust, revoke-first/promote-last, TTL-at-use, and no-fail-open are intact and not regressed. Every real launch entry (claude/pi/claudex, yolo variants, prdy/qa) routes through the choke-point; an ungated launch anywhere is additionally fail-closed by the global all-tools hook. The runtime gate is sound — this NO-GO is strictly about the guard's static completeness in the class the round is chartered to close.

CI

Green on this exact head (Woodpecker pipeline #1914, all steps OK). It does not catch the finding because the test matrix lacks the prefixed-variable case.

Disclosed residuals (NOT flagged)

Harness middle-drop (b7bbb6ea); WI-7 branch protection; bounded T-A dual-observer-miss; T-C (hook/extension absence, runtime outside the gated launcher, ptrace/same-UID broker replacement).


Independence (Gate 16 — by lane/process): author ms-cr-wi1 ≠ code lens terra ≠ this Opus security review — distinct principals by lane/process; I am THE security gate for this WI. Earned from product code + my own constructed-and-run guard tests, not the author's suite. tea/pr-review.sh approve broken for this surface; this durable comment is the formal R4 SECREV record. Reviewed head 1eb77c17. REQUEST CHANGES — do not merge until the prefixed-variable indirection is guarded and regressed. No code edits, commits, pushes, or merges performed by this reviewer.

## OPUS SECREV R4 (MANDATORY, auth surface) — WI-2 #829 head 1eb77c17 **Verdict: 🔴 SECREV NO-GO / REQUEST CHANGES** — reviewed exact head `1eb77c17f3147d4fa9944f77f1826243135b9cc0` — **1 blocking finding** (guard false-negative in a claimed-covered evasion class). Fresh pass, zero evidence transfer. Per the R4 calibration I did **not** reason abstractly — I built each evasion as a real launcher file under a scanned root and **ran `check-runtime-launches.py` against each**. That method surfaced a false-negative that reasoning would have rationalized away — exactly the R3 lesson. --- ### 🔴 BLOCKING FINDING — guard misses runtime-variable indirection behind a command prefix **File:** `packages/mosaic/framework/tools/lease-broker/check-runtime-launches.py:249` (and `:263-266`) **Class:** the brief's "indirection … var-holds-launcher-then-exec" — expected RED, observed **GREEN**. **Exploit (constructed + run, not reasoned):** ```sh # packages/evilpkg/c5c.sh #!/bin/bash B=claude exec "$B" -p "evil" ``` `python3 check-runtime-launches.py --root <dir>` → **exit 0, GREEN** (no violation, not even listed). Variants also GREEN: `exec $B` (unquoted), `command "$B"`, `nohup "$B"`, `env X=1 "$B"`. Only the **bare** first-token form `"$B" -p` and `eval "$B"` are caught (RED). **Root cause — two complementary blind spots that intersect:** - `is_shell_direct_invocation` (:229-251) correctly walks past `command`/`exec`/`nohup`/`env …` prefixes, but its terminal check (`:249`) is `Path(command[index]).name in {"claude","pi"}` — it only resolves a **literal** binary, never a `$var` against the tracked `runtime_variables`. - `executes_runtime_variable` (:260-267) correctly resolves `runtime_variables`, but only for `eval "$var"` or a **bare first-token** `^\s*"$var"` — it never walks the `exec`/`command`/`nohup`/`env` prefixes. So a runtime-holding variable executed behind any command prefix (`exec "$B"`) satisfies neither path. The author's own regressions confirm the gap is untested: `variable-exec.sh` uses the bare `"$launcher" -p` form (caught); `env/command/nohup-prefix.sh` use **literal** `claude`/`pi` (caught). The **product of the two dimensions — prefixed + variable — is never asserted**, mirroring the R3 comment-marker miss (a form the tests didn't cover). CI is green (Woodpecker #1914, `test` OK) precisely because the test suite has this hole. **Fix (localized):** thread `runtime_variables` into `is_shell_direct_invocation` and, at the terminal check, also treat a command-position token that is a `$var`/`${var}` reference to a tracked runtime variable as a direct launch (strip `${}`/quoting, match the name). Equivalently, have `executes_runtime_variable` skip the same `exec`/`command`/`nohup`/`env`+assignment prefixes before matching the reference. Add regressions for `exec "$v"`, `exec $v`, `command "$v"`, `nohup "$v"`, `env A=1 "$v"`. **Bounded severity (stated honestly so this is calibrated, not overstated):** this is a **guard/CI-regression-backstop false-negative, NOT a runtime authorization bypass.** Even a committed launcher of this shape does not breach the AC at runtime, because (a) the **dangerous** variant is still caught by primitive-anchoring — `F="--dangerously-skip-permissions"` trips `dangerous-primitive` on the assignment line, so yolo-mode can't ride this indirection; and (b) a **non-dangerous** ungated `claude -p` is fail-closed at runtime by the global `~/.claude` all-tools hook (no `MOSAIC_LEASE_SESSION_ID` → `GATE_UNAVAILABLE` → every tool denied) and by headless permission mode. No launcher of this shape exists in the tree today (14/14 real sites gated). **But** the R4 deliverable is guard completeness for these evasion classes, the brief lists this exact class as expected-RED, and the project standard (terra REQUEST-CHANGES on the R3 head for an analogous guard false-negative) is that a demonstrable, fixable false-negative in a claimed-covered class blocks. Consistency requires REQUEST-CHANGES. This is a narrow, clearly-fixable gap — I expect a fast turnaround. --- ### What DID hold (verified by construct-and-run + code read) **Evasion matrix — 9/10 constructed launchers correctly RED; 1 control correctly GREEN; 1 evasion GREEN (the finding):** | # | Constructed launcher | Result | Correct? | |---|---|---|---| | 1 | R3 comment-marker (`# execLeaseGatedRuntime(` then `exec claude -p`) | RED (comment stripped) | ✅ | | 2 | heredoc-body `claude -p` | RED | ✅ | | 3 | line-continuation `true \` ⏎ `claude -p` | RED | ✅ | | 4 | non-first `;`/`&&`/`\|` + `claude -p` | RED (×3) | ✅ | | 5a | `r=claude; eval "$r -p"` | RED | ✅ | | 5b | `$(claude -p)` | RED | ✅ | | **5c** | **`B=claude; exec "$B" -p`** | **GREEN** | **❌ FINDING** | | 6 | `--dangerously-skip-permissions` outside choke-point | RED (`dangerous-primitive`) | ✅ | | 7 | gated-marker-in-string then real `exec claude -p` | RED (string didn't suppress) | ✅ | | 8 | `/abs/claude`, `env … claude`, `nohup pi` (literal) | RED (×3) | ✅ | | 9 | dangerous flag via `F="…"` var | RED (`dangerous-primitive` on assignment) | ✅ | | 10 | properly gated `launch-runtime.py --dangerous --runtime claude --` | GREEN (control) | ✅ | **RIDER B — primitive-anchored vs command-position; genuine defense-in-depth?** Yes, genuinely complementary, and I confirmed it empirically: primitive-anchoring caught the dangerous-flag-via-variable case (c9) that command-position parsing would miss, while command-position caught literal-binary launches with no dangerous flag. The migration is real and correct: `--dangerously-skip-permissions` now appears **only** in `launch-runtime.py` (`CLAUDE_DANGEROUS_FLAG`), injected via `--dangerous`, which asserts `runtime=="claude"` **and** `Path(command[0]).name=="claude"` before prepending it; the literal was removed from `launch.ts`, `claudex.ts`, and prdy scripts (verified by diff). Primitive-anchoring is the more evasion-resistant of the two for the **dangerous** class (a plain substring is far harder to obfuscate than to fool a command parser), and it is not masking a command-position hole — each covers a class the other misses. **The one form neither covers is the non-dangerous prefixed-variable launch (the finding).** ### Choke-point integrity (RIDER 3) — sound `launch-runtime.py` remains the single wrapper; registration is unconditional before exec, `SO_PEERCRED` kernel-anchored + broker-minted session (refuses caller `session_id`), session id injected/overwritten, and **fail-closed** (return 1, no exec) on any registration failure. `daemon.py` is **byte-identical to my round-1 proof** (`git diff 77b137cc..HEAD` empty) → broker-verified-only trust, revoke-first/promote-last, TTL-at-use, and no-fail-open are intact and not regressed. Every real launch entry (claude/pi/claudex, yolo variants, prdy/qa) routes through the choke-point; an ungated launch anywhere is additionally fail-closed by the global all-tools hook. **The runtime gate is sound — this NO-GO is strictly about the guard's static completeness in the class the round is chartered to close.** ### CI Green on this exact head (Woodpecker pipeline **#1914**, all steps OK). It does not catch the finding because the test matrix lacks the prefixed-variable case. ### Disclosed residuals (NOT flagged) Harness middle-drop (`b7bbb6ea`); WI-7 branch protection; bounded T-A dual-observer-miss; T-C (hook/extension absence, runtime outside the gated launcher, ptrace/same-UID broker replacement). --- **Independence (Gate 16 — by lane/process):** author `ms-cr-wi1` ≠ code lens `terra` ≠ this Opus security review — distinct principals by lane/process; I am THE security gate for this WI. Earned from product code + my own constructed-and-run guard tests, not the author's suite. `tea`/`pr-review.sh approve` broken for this surface; this durable comment is the formal R4 SECREV record. Reviewed head `1eb77c17`. **REQUEST CHANGES — do not merge until the prefixed-variable indirection is guarded and regressed.** No code edits, commits, pushes, or merges performed by this reviewer.
Author
Owner

INDEPENDENT CODE RE-REVIEW R4 (terra) — WI-2 #829 head 1eb77c17

Verdict: REQUEST CHANGES

Reviewed exact head 1eb77c17f3147d4fa9944f77f1826243135b9cc0 against real origin/main merge-base 8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201; prospective merge-tree construction succeeded (fc89c8a4). Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). 1 blocking finding.

  1. [BLOCKER — RIDER A/E] packages/mosaic/framework/tools/lease-broker/check-runtime-launches.py:81-83,260-268 misses a variable-held runtime executed through exec. I added and removed this temporary production launcher:

    launcher=claude
    exec "$launcher" -p x
    

    The guard exited 0 and retained runtime launch inventory: 14 gated/14 total. The assignment is recorded, but executes_runtime_variable() only recognizes a bare $launcher at command position, not exec "$launcher"; the shell-direct parser likewise sees only the variable token. This is a real consequential Claude launch with no broker registration/session/hook, and the new primitive-only Rider B does not catch it because it has no dangerous flag. The same-line form launcher=claude; exec "$launcher" -p x also passes. Add permanent red regressions for both forms and make the command-position parser detect runtime variables after exec (and equivalent prefixes) before treating the guard as load-bearing.

Independently verified positives (not sufficient for approval)

  • Plain direct, inline-marker, heredoc, continuation, ;/&&/| chain, eval, marker-argument/echo, and primitive-outside-wrapper probes went red. R3 marker evasion is fixed; replaying the R3 guard against marker and continuation cases produced zero violations (red-before-fix evidence).
  • --dangerously-skip-permissions production ownership is limited to launch-runtime.py; the primitive probe elsewhere is red. The 14 current inventory entries are gated; real-socket acceptance is 49/49, including PRDY/QA no-broker denial and unverified-mutator denial. Claudex and broker ordering remain green.
  • Independent branch coverage: launch-runtime.py 22/22 = 100%, mutator-gate.py 22/22 = 100%, daemon delta 35/40 = 87.5%, guard 97%. The coverage does not exercise this false-negative.
  • Full local gates passed: pnpm test 43/43 Turbo tasks (Mosaic 72 files / 1,384 tests), typecheck 42/42, lint 23/23, format, diff check. R4 red-first commits 95824572, 88a709a9, 64efdb44 precede 1eb77c17; no --no-verify evidence. CI pipeline 1914 is terminal-success on this exact head.

The mandatory Opus-SECREV is separate and is not replaced by this code review.

## INDEPENDENT CODE RE-REVIEW R4 (terra) — WI-2 #829 head 1eb77c17 **Verdict: REQUEST CHANGES** Reviewed exact head `1eb77c17f3147d4fa9944f77f1826243135b9cc0` against real `origin/main` merge-base `8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201`; prospective merge-tree construction succeeded (`fc89c8a4`). Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). **1 blocking finding.** 1. **[BLOCKER — RIDER A/E] `packages/mosaic/framework/tools/lease-broker/check-runtime-launches.py:81-83,260-268` misses a variable-held runtime executed through `exec`.** I added and removed this temporary production launcher: ```sh launcher=claude exec "$launcher" -p x ``` The guard exited **0** and retained `runtime launch inventory: 14 gated/14 total`. The assignment is recorded, but `executes_runtime_variable()` only recognizes a bare `$launcher` at command position, not `exec "$launcher"`; the shell-direct parser likewise sees only the variable token. This is a real consequential Claude launch with no broker registration/session/hook, and the new primitive-only Rider B does not catch it because it has no dangerous flag. The same-line form `launcher=claude; exec "$launcher" -p x` also passes. Add permanent red regressions for both forms and make the command-position parser detect runtime variables after `exec` (and equivalent prefixes) before treating the guard as load-bearing. ### Independently verified positives (not sufficient for approval) - Plain direct, inline-marker, heredoc, continuation, `;`/`&&`/`|` chain, eval, marker-argument/echo, and primitive-outside-wrapper probes went red. R3 marker evasion is fixed; replaying the R3 guard against marker and continuation cases produced zero violations (red-before-fix evidence). - `--dangerously-skip-permissions` production ownership is limited to `launch-runtime.py`; the primitive probe elsewhere is red. The 14 current inventory entries are gated; real-socket acceptance is 49/49, including PRDY/QA no-broker denial and unverified-mutator denial. Claudex and broker ordering remain green. - Independent branch coverage: `launch-runtime.py` **22/22 = 100%**, `mutator-gate.py` **22/22 = 100%**, daemon delta **35/40 = 87.5%**, guard **97%**. The coverage does not exercise this false-negative. - Full local gates passed: `pnpm test` 43/43 Turbo tasks (Mosaic 72 files / 1,384 tests), typecheck 42/42, lint 23/23, format, diff check. R4 red-first commits `95824572`, `88a709a9`, `64efdb44` precede `1eb77c17`; no `--no-verify` evidence. CI pipeline 1914 is terminal-success on this exact head. The mandatory Opus-SECREV is separate and is not replaced by this code review.
jason.woltje added 2 commits 2026-07-18 05:40:11 +00:00
Author
Owner

INDEPENDENT CODE RE-REVIEW R5 (terra) — WI-2 #829 head 04cc065c

Verdict: APPROVE

Reviewed exact head 04cc065c2babae5bcc04b932d89d0bc3ff8546df against real origin/main merge-base 8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201; prospective merge-tree construction succeeded (e5d1a303). Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). 0 blocking findings.

R5 stopping criterion: A/B/C = met

  • A — realistic matrix: independently planted and ran quoted/unquoted exec $var, command "$v", nohup "$v", and env A=1 "$v", in both multiline and same-line assignment forms. Every one made the production guard red. The R5 permanent matrix covers those ten prefix/assignment forms, and replaying the R4 guard against exec "$launcher" plus same-line env returned zero violations (red-before-fix); current guard returns red. The fix structurally shares command-position token resolution for literals and tracked variable references rather than point-matching the prior spelling.
  • B — backstops: the raw dangerous primitive is production-owned only by launch-runtime.py; an alias-based exotic dangerous launcher made the guard red by primitive anchoring. I also confirmed a parser-residual non-dangerous time claude -p x is not scanner-detected but the actual all-tools gate with no lease exits 2 (GATE_UNAVAILABLE) and denies the tool. Thus the unbounded exotic parser surface remains backstop-caught, as required.
  • C — fresh exploration: no new realistic, non-backstop-caught launch class found. The 14 real launch sites remain gated.

Verification

  • Full local suite: pnpm test 43/43 Turbo tasks; Mosaic 72 files / 1,385 tests. Typecheck 42/42, lint 23/23, format, and diff check passed.
  • Focused real-socket acceptance: 50/50; broker revoke-first and consume-before-promote ordering plus Claudex normal/YOLO remain intact.
  • Independent branch coverage: guard 97%; launch-runtime.py 22/22 = 100%; mutator-gate.py 22/22 = 100%; WI-2 daemon delta 35/40 = 87.5%.
  • R5 TDD ordering is valid: test commit 91a4a983 precedes 04cc065c; no --no-verify evidence.
  • CI pipeline 1915 is terminal-success on 04cc065c.

The mandatory Opus-SECREV remains a separate gate and is not replaced by this code review.

## INDEPENDENT CODE RE-REVIEW R5 (terra) — WI-2 #829 head 04cc065c **Verdict: APPROVE** Reviewed exact head `04cc065c2babae5bcc04b932d89d0bc3ff8546df` against real `origin/main` merge-base `8ec67a1126adb0dcd4c3a2bf5525f3e239c0b201`; prospective merge-tree construction succeeded (`e5d1a303`). Author/reviewer separation is LANE/PROCESS-attested (sol/Pi author; terra separate reviewer session). **0 blocking findings.** ### R5 stopping criterion: A/B/C = met - **A — realistic matrix:** independently planted and ran quoted/unquoted `exec $var`, `command "$v"`, `nohup "$v"`, and `env A=1 "$v"`, in both multiline and same-line assignment forms. Every one made the production guard red. The R5 permanent matrix covers those ten prefix/assignment forms, and replaying the R4 guard against `exec "$launcher"` plus same-line `env` returned zero violations (red-before-fix); current guard returns red. The fix structurally shares command-position token resolution for literals and tracked variable references rather than point-matching the prior spelling. - **B — backstops:** the raw dangerous primitive is production-owned only by `launch-runtime.py`; an alias-based exotic dangerous launcher made the guard red by primitive anchoring. I also confirmed a parser-residual non-dangerous `time claude -p x` is not scanner-detected but the actual all-tools gate with no lease exits 2 (`GATE_UNAVAILABLE`) and denies the tool. Thus the unbounded exotic parser surface remains backstop-caught, as required. - **C — fresh exploration:** no new realistic, non-backstop-caught launch class found. The 14 real launch sites remain gated. ### Verification - Full local suite: `pnpm test` 43/43 Turbo tasks; Mosaic 72 files / 1,385 tests. Typecheck 42/42, lint 23/23, format, and diff check passed. - Focused real-socket acceptance: 50/50; broker revoke-first and consume-before-promote ordering plus Claudex normal/YOLO remain intact. - Independent branch coverage: guard 97%; `launch-runtime.py` 22/22 = 100%; `mutator-gate.py` 22/22 = 100%; WI-2 daemon delta 35/40 = 87.5%. - R5 TDD ordering is valid: test commit `91a4a983` precedes `04cc065c`; no `--no-verify` evidence. - CI pipeline 1915 is terminal-success on `04cc065c`. The mandatory Opus-SECREV remains a separate gate and is not replaced by this code review.
Author
Owner

OPUS SECREV R5 (MANDATORY, auth surface) — WI-2 #829 head 04cc065c

Verdict: SECREV GO — reviewed exact head 04cc065c2babae5bcc04b932d89d0bc3ff8546df0 blocking findings. A + B + C met → parser-complete declared on principle.

Fresh pass, zero evidence transfer. Method held from R4: every claim below was constructed as a real launcher file and RUN against the guard / the hook — no abstract reasoning. My R4 NO-GO (18114) is resolved.


1. R4 finding structurally fixed (constructed + RUN — all RED)

The R4 prefixed-variable false-negative is closed structurally (shell_command_tokens walks exec/command/nohup/env prefixes in any nested order and resolves the command-position token; runtime_variable_reference matches $v/${v} against tracked runtime vars). Every combo I built is now RED:

Constructed launcher R4 R5
B=claude; exec "$B" -p GREEN RED
multiline B=claudeexec "$B" RED
exec $B (unquoted) GREEN RED
command "$B" / nohup "$B" / env X=1 "$B" GREEN RED
${B} brace / P=pi; exec "$P" RED
nested exec env A=1 "$B" / nohup env X=1 exec "$B" RED
true && exec "$B" (non-first) RED

Fix is structural, not a point-fix (any prefix×var×quoting×line combination). The only GREENs I could produce — exec A=1 claude, exec A=1 "$B" — are bash non-launches: I verified in a real shell that exec A=1 <cmd> yields exec: A=1: not found (exit 127) because bash treats A=1 as the command name once exec is the command word. Correctly not flagged (nothing launches).

2. Stopping-criterion evaluation (A / B / C) — pushed hard, by construction

(A) Realistic matrix RED-covered for the claimed class — MET. Every realistic prefix/var/quoting/line form in the claimed prefixed-variable class is RED. I did find realistic parser residuals in other classes on the acknowledged unbounded surface (all non-dangerous, all GREEN): backtick x=`claude -p`, sh -c 'claude -p', echo x | xargs claude, ${B:-claude}. These are not the class R5 claims to fix, and — critically — are proven backstop-caught (see B2). Non-blocking hardening recommendation: add backtick command-substitution to the parser for consistency, since the $(claude) form is already covered; this is the one spelling-inconsistency worth closing cheaply.

(B) Backstops independently verified by construction — MET.

  • B1 — exotic dangerous → primitive-anchoring (or parser) trips RED. Constructed and RAN: alias claude='claude --dangerously-skip-permissions'RED (dangerous-primitive); claude(){ command claude --dangerously-skip-permissions "$@"; }RED; F=--dangerously-skip-permissions; exec claude "$F"RED (primitive on the assignment) + RED (parser on exec claude); flag split across \ continuation → RED (parser still sees literal claude). I could not defeat primitive-anchoring while keeping the literal flag anywhere in source. The only way to hide the flag from primitive-anchoring is to remove the literal entirely (echo <b64> | base64 -d | sh), which then loses primitive coverage and parser coverage.
  • B2 — parser-missed non-dangerous → all-tools hook fail-closes. Constructed and RAN the hook (mutator-gate.py) exactly as a PreToolUse invocation from an ungated claude (no MOSAIC_LEASE_SESSION_ID): consequential Bashexit 2 GATE_UNAVAILABLE; read-only Readexit 2 (fail-closed even for read-only when ungated); broker socket set-but-unreachable → exit 2. So any parser-missed non-dangerous launch (backtick/sh-c/xargs/etc.) that runs claude under the standard/isolated config is denied every tool.
  • The one residual that evades parser AND both backstops: echo <base64 of "CLAUDE_CONFIG_DIR=/hookless claude --dangerously-skip-permissions -p"> | base64 -d | sh (payload decodes to a dangerous claude launch — I verified the decode). It evades the parser (opaque base64|sh), evades primitive-anchoring (literal absent from source), and evades the hook only by pointing CLAUDE_CONFIG_DIR at an attacker-pre-established hook-less config dir. That is not a defeat of a backstop within its scope — it is precisely the disclosed T-C residual: "a runtime executing outside the gated launcher … fully-rotted behavior," whose irreducible line is WI-7 server-side branch protection + independent review (any reviewer rejects base64 -d | sh in a launcher). It also requires committed, obfuscated source (undecidable to detect statically) plus deliberate hook-less-config setup. Not a new blocker. Every residual that stays within the in-scope model (claude under the gated launcher / standard config) is caught by ≥1 robust backstop.

(C) Fail-closed enum + broker core — MET. Guard on the real repo: 14/14 gated, exit 0. daemon.py is byte-identical to my round-1 proof (git diff 77b137cc..HEAD empty) → kernel-anchored-only trust (no caller-asserted principal/verified), revoke-first, promote-last, TTL-at-use, and no fail-open are intact and not regressed. launch-runtime.py (the single choke-point) is unchanged since R4 — registration unconditional before exec, SO_PEERCRED/broker-minted, session injected, fail-closed on any registration failure. Every launch entry (claude/pi/claudex, yolo variants, prdy/qa, coord, fleet) reaches a consequential mutator only via the broker-verified lease; an ungated launch anywhere is additionally hook-denied (B2).

Verdict logic applied

  • The R4-style NO-GO trigger (a realistic gap in the claimed-covered class) is absent: the claimed prefixed-variable class is realistically RED-complete.
  • Remaining realistic parser gaps (backtick/sh-c/xargs) are on the acknowledged unbounded surface, non-dangerous, and proven caught by the all-tools hook — documented DiD limitations, not blockers, per the Mos stopping criterion (parser = best-effort DiD; robust controls = primitive-anchoring + all-tools hook).
  • The sole all-backstop-evading construction is the ratified T-C obfuscated-code + hook-less-config residual, out of scope, WI-7-backstopped.
  • Therefore A + B + C are met and parser-complete may be declared on principle: not "the parser catches every spelling on an unbounded surface" (impossible), but "the claimed class is realistically complete and every in-scope residual is caught by a robust backstop."

CI

Green on this exact head — Woodpecker pipeline #1915, success.

Disclosed residuals (NOT flagged)

Harness middle-drop (b7bbb6ea); WI-7 branch protection; bounded T-A dual-observer-miss; T-C (runtime outside the gated launcher incl. obfuscated base64|sh + hook-less config, hook/extension absence, ptrace/same-UID broker replacement).


Independence (Gate 16 — by lane/process): author ms-cr-wi1 ≠ code lens terra ≠ this Opus security review — distinct principals by lane/process; I am THE security gate for this WI. Earned from product code + my own constructed-and-run guard/hook tests, not the author's suite. tea/pr-review.sh approve broken for this surface; this durable comment is the formal R5 SECREV approval record and supersedes my R4 NO-GO (the R4 finding is fixed). Reviewed head 04cc065c. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.

## OPUS SECREV R5 (MANDATORY, auth surface) — WI-2 #829 head 04cc065c **Verdict: ✅ SECREV GO** — reviewed exact head `04cc065c2babae5bcc04b932d89d0bc3ff8546df` — **0 blocking findings**. **A + B + C met → parser-complete declared on principle.** Fresh pass, zero evidence transfer. Method held from R4: every claim below was **constructed as a real launcher file and RUN** against the guard / the hook — no abstract reasoning. My R4 NO-GO (18114) is resolved. --- ### 1. R4 finding structurally fixed (constructed + RUN — all RED) The R4 prefixed-variable false-negative is closed structurally (`shell_command_tokens` walks `exec`/`command`/`nohup`/`env` prefixes in any nested order and resolves the command-position token; `runtime_variable_reference` matches `$v`/`${v}` against tracked runtime vars). Every combo I built is now **RED**: | Constructed launcher | R4 | R5 | |---|---|---| | `B=claude; exec "$B" -p` | GREEN | **RED** | | multiline `B=claude` ⏎ `exec "$B"` | — | **RED** | | `exec $B` (unquoted) | GREEN | **RED** | | `command "$B"` / `nohup "$B"` / `env X=1 "$B"` | GREEN | **RED** | | `${B}` brace / `P=pi; exec "$P"` | — | **RED** | | nested `exec env A=1 "$B"` / `nohup env X=1 exec "$B"` | — | **RED** | | `true && exec "$B"` (non-first) | — | **RED** | Fix is **structural, not a point-fix** (any prefix×var×quoting×line combination). The only GREENs I could produce — `exec A=1 claude`, `exec A=1 "$B"` — are **bash non-launches**: I verified in a real shell that `exec A=1 <cmd>` yields `exec: A=1: not found` (exit 127) because bash treats `A=1` as the command name once `exec` is the command word. Correctly not flagged (nothing launches). ### 2. Stopping-criterion evaluation (A / B / C) — pushed hard, by construction **(A) Realistic matrix RED-covered for the claimed class — MET.** Every realistic prefix/var/quoting/line form in the *claimed* prefixed-variable class is RED. I did find realistic parser residuals in **other** classes on the acknowledged unbounded surface (all **non-dangerous**, all GREEN): backtick `` x=`claude -p` ``, `sh -c 'claude -p'`, `echo x | xargs claude`, `${B:-claude}`. These are **not** the class R5 claims to fix, and — critically — are **proven backstop-caught** (see B2). *Non-blocking hardening recommendation:* add backtick command-substitution to the parser for consistency, since the `$(claude)` form is already covered; this is the one spelling-inconsistency worth closing cheaply. **(B) Backstops independently verified by construction — MET.** - **B1 — exotic dangerous → primitive-anchoring (or parser) trips RED.** Constructed and RAN: `alias claude='claude --dangerously-skip-permissions'` → **RED** (dangerous-primitive); `claude(){ command claude --dangerously-skip-permissions "$@"; }` → **RED**; `F=--dangerously-skip-permissions; exec claude "$F"` → **RED** (primitive on the assignment) + **RED** (parser on `exec claude`); flag split across `\` continuation → **RED** (parser still sees literal `claude`). I could **not** defeat primitive-anchoring while keeping the literal flag anywhere in source. The only way to hide the flag from primitive-anchoring is to remove the literal entirely (`echo <b64> | base64 -d | sh`), which then loses primitive coverage **and** parser coverage. - **B2 — parser-missed non-dangerous → all-tools hook fail-closes.** Constructed and RAN the hook (`mutator-gate.py`) exactly as a PreToolUse invocation from an *ungated* claude (no `MOSAIC_LEASE_SESSION_ID`): consequential `Bash` → **exit 2 `GATE_UNAVAILABLE`**; read-only `Read` → **exit 2** (fail-closed *even for read-only* when ungated); broker socket set-but-unreachable → **exit 2**. So any parser-missed non-dangerous launch (backtick/sh-c/xargs/etc.) that runs claude under the standard/isolated config is denied every tool. - **The one residual that evades parser AND both backstops:** `echo <base64 of "CLAUDE_CONFIG_DIR=/hookless claude --dangerously-skip-permissions -p"> | base64 -d | sh` (payload decodes to a dangerous claude launch — I verified the decode). It evades the parser (opaque `base64|sh`), evades primitive-anchoring (literal absent from source), and evades the hook **only by** pointing `CLAUDE_CONFIG_DIR` at an attacker-pre-established hook-less config dir. That is **not a defeat of a backstop within its scope** — it is precisely the **disclosed T-C residual**: "a runtime executing outside the gated launcher … fully-rotted behavior," whose irreducible line is **WI-7 server-side branch protection + independent review** (any reviewer rejects `base64 -d | sh` in a launcher). It also requires committed, obfuscated source (undecidable to detect statically) plus deliberate hook-less-config setup. **Not a new blocker.** Every residual that stays within the in-scope model (claude under the gated launcher / standard config) is caught by ≥1 robust backstop. **(C) Fail-closed enum + broker core — MET.** Guard on the real repo: **14/14 gated, exit 0**. `daemon.py` is **byte-identical to my round-1 proof** (`git diff 77b137cc..HEAD` empty) → kernel-anchored-only trust (no caller-asserted principal/verified), revoke-first, promote-last, TTL-at-use, and no fail-open are intact and not regressed. `launch-runtime.py` (the single choke-point) is **unchanged since R4** — registration unconditional before exec, `SO_PEERCRED`/broker-minted, session injected, fail-closed on any registration failure. Every launch entry (claude/pi/claudex, yolo variants, prdy/qa, coord, fleet) reaches a consequential mutator only via the broker-verified lease; an ungated launch anywhere is additionally hook-denied (B2). ### Verdict logic applied - The R4-style NO-GO trigger (a realistic gap **in the claimed-covered class**) is **absent**: the claimed prefixed-variable class is realistically RED-complete. - Remaining realistic parser gaps (backtick/sh-c/xargs) are on the acknowledged **unbounded** surface, non-dangerous, and **proven** caught by the all-tools hook — documented DiD limitations, **not blockers**, per the Mos stopping criterion (parser = best-effort DiD; robust controls = primitive-anchoring + all-tools hook). - The sole all-backstop-evading construction is the ratified **T-C** obfuscated-code + hook-less-config residual, out of scope, WI-7-backstopped. - Therefore **A + B + C are met** and parser-complete may be declared **on principle**: not "the parser catches every spelling on an unbounded surface" (impossible), but "the claimed class is realistically complete and every in-scope residual is caught by a robust backstop." ### CI Green on this exact head — Woodpecker pipeline **#1915**, `success`. ### Disclosed residuals (NOT flagged) Harness middle-drop (`b7bbb6ea`); WI-7 branch protection; bounded T-A dual-observer-miss; T-C (runtime outside the gated launcher incl. obfuscated `base64|sh` + hook-less config, hook/extension absence, ptrace/same-UID broker replacement). --- **Independence (Gate 16 — by lane/process):** author `ms-cr-wi1` ≠ code lens `terra` ≠ this Opus security review — distinct principals by lane/process; I am THE security gate for this WI. Earned from product code + my own constructed-and-run guard/hook tests, not the author's suite. `tea`/`pr-review.sh approve` broken for this surface; this durable comment is the formal R5 SECREV approval record and supersedes my R4 NO-GO (the R4 finding is fixed). Reviewed head `04cc065c`. Merge authority remains with the coordinator (Mos). No code edits, commits, pushes, or merges performed by this reviewer.
jason.woltje merged commit abd2791f59 into main 2026-07-18 05:51:59 +00:00
jason.woltje deleted branch feat/829-mutator-gate 2026-07-18 05:52:00 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#837