T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/runner.ts:268
- Finding
- Plan-Level Hooks Bypass Risk Authorization Controls<![CDATA[ ## Vulnerability Details **File Location**: `src/runner.ts:268-290` and `src/schema.ts:579-589` **Vulnerability Type**: Missing authorization enforcement for plan-level setup and teardown hooks **Risk Level**: High ### Complete Code Snippet ```ts if (RISKY.has(tc.risk) && !opts.allowRisk) { return { tc, driver, runnable: false, blockReason: `risk=${tc.risk}: blocked unless --allow-risk is set (and confirmed by a human).`, }; } return { tc, driver, runnable: true }; ``` ```ts // By design, plan hooks are TRUSTED, author-controlled fixtures and run UNGATED: // unlike cases, they are not subject to the RISKY/--allow-risk gate (a hook has // no per-step risk field). Authors must keep destructive ops out of plan // setup/teardown unless they intend them to run unconditionally. const hadRunnable = runnable.length > 0; if (plan.setup && plan.setup.length > 0 && hadRunnable) { log.info(c.cyan("▶ plan setup") + c.dim(` — ${plan.setup.length} step(s)`)); const setupError = await runPlanHook(plan.setup, ctx, "plan setup"); if (setupError) { const reason = redactString(`plan setup failed: ${setupError}`); log.err(reason); for (const p of runnable) { results.push(blocked(p.tc, p.driver, reason)); log.warn(`${p.tc.id}: ${reason}`); } runnable = []; } } ``` The schema explicitly documents the same behavior: ```ts "steps run ONCE before any case (in a throwaway context) to seed shared state; if any fails, every runnable case is marked blocked. TRUSTED, author-controlled fixtures: plan hooks run UNGATED — they are NOT subject to the per-case risk/--allow-risk gate, so keep destructive operations out of them unless you intend them to run unconditionally" ``` ### Technical Analysis The authorization control applies only to individual cases whose `risk` value is `destructive`, `paid`, or `prod`. Plan-level `setup` and `teardown` hooks support the same powerful step interpreter, including HTTP requests, br ...[truncated 2047 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Add explicit risk metadata to plan-level `setup` and `teardown` hooks. 2. Apply the same `RISKY` and `allowRisk` checks to hooks before creating a browser or issuing any request. 3. Prefer requiring a separate option such as `--allow-risky-hooks`, so case authorization cannot silently authorize unconditional fixtures. 4. Reject network, `eval`, `load`, and other side-effecting hook steps unless the hook is explicitly authorized. 5. In the MCP interface, expose risky-hook authorization as a distinct, clearly described parameter and rely on the MCP host’s approval mechanism. 6. Consider eliminating plan-level side-effecting hooks in favor of normal cases so every operation has a risk label, result, timeout, and evidence trail. 7. Add regression tests proving that destructive operations in both setup and teardown remain blocked without authorization. ]]>
