T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- control.ts:58
- Finding
- Unscoped Flush Operation Can Terminate Unrelated Execution Sessions## Vulnerability Details **File Location**: `control.ts`, lines 58–76 **Vulnerability Type**: Missing ownership validation for destructive session-management operations **Risk Level**: High ### Vulnerable Code ```ts // /flush: emergency stop export async function flush() { // Mark tasks as cancelled registry.forEach(t => t.state = "cancelled"); registry.clear(); // Kill active exec sessions (best-effort) try { const procs = await process.list({}); for (const p of procs.sessions || []) { await process.kill({ sessionId: p.sessionId }); } } catch (e) { // Swallow errors: flush must always respond } return { ok: true, message: "All tasks cancelled, exec sessions killed, registry cleared", ts: now() }; } ``` ### Technical Analysis The exported `flush()` function obtains the global execution-session list and terminates every session returned by the SDK. It does not verify that a session: - Was created by this skill; - Corresponds to a task in the local registry; - Is owned by the requesting user or agent; or - Is otherwise within the legitimate scope of the emergency-stop operation. The task registry only records task metadata and does not associate task IDs with spawned execution-session IDs. Consequently, `flush()` cannot distinguish skill-owned workers from unrelated workloads. The issue also affects `recover()` at `control.ts` lines 81–88 because that function invokes `flush()` directly. Whether an external actor can exploit this behavior depends on how the host application authorizes access to these exported operations, but the destructive implementation itself lacks a least-privilege boundary. ### Attack Path 1. An actor, integration, or automation with access to the exported control operation invokes `flush()` or `recover()`. 2. `flush()` clears the local task registry. 3. It calls `process.list({})` and receives all execution sessions visible to the skill's SDK context. 4. It iterates over the ret ...[truncated 726 chars]
- Remediation
- ## Remediation Suggestions 1. Capture the session identifier returned by `sessions_spawn()` and associate it with the corresponding task ID in the registry. 2. Maintain an explicit allowlist of sessions created and owned by this skill. 3. Change `flush()` to terminate only sessions in that allowlist instead of enumerating and killing all visible sessions. 4. Before termination, validate task ownership, session ownership, and the caller's authorization for destructive operations. 5. Separate global administrative recovery from ordinary skill-level recovery. If global termination is genuinely required, expose it only through a privileged, explicitly confirmed administrative interface. 6. Persist sufficient ownership metadata safely if recovery must work across restarts. 7. Return partial-failure details rather than silently swallowing every termination error, while avoiding disclosure of sensitive session metadata. 8. Add tests proving that unrelated sessions remain active after `flush()` and `recover()` are invoked.
