/** * Skill sandbox — explicit execution policy for third-party skills (P5-01c). * * ── Threat model ────────────────────────────────────────────────────────── * The Genius Store serves skills as TEXT (ADR-001 §5: "le contenu est injecté * comme texte"): there is no code path in Cortex that executes a served skill's * scripts. That is the first and strongest control — a served skill cannot run. * This module makes that guarantee EXPLICIT and enforceable, so any future code * that *would* run a skill-supplied script (an installer, a `scripts/setup.sh`, * a hook a skill ships) must pass through a single gate that FAILS LOUD instead * of auto-executing. * * ── Sandbox model (documented policy) ───────────────────────────────────── * 1. DRY-RUN BY DEFAULT. A skill's executable payload (its `scripts/`, its * hooks, any `curl … | sh`) is never run automatically. The default * decision for any execution request is 'dry-run': describe what would run, * run nothing. * 2. EXECUTION ON CONSENT ONLY. Real execution requires explicit, per-action * human consent (`consent: true`), never a global/ambient flag. This mirrors * GUARD-POLICY §5.5 (escape hatch is traceable and per-action, never a * permanent global override) and the INSTALL frontier (GUARD-POLICY §4.1). * 3. MINIMAL PERMISSIONS. Consent is scoped to the permissions the skill's * Skill Card declares. A request that needs a scope the card did not declare * is denied even WITH consent — you cannot consent to more than was declared. * 4. FIRST-PARTY vs THIRD-PARTY. First-party skills (canonical GT owner) may * run in dry-run without extra ceremony; third-party skills are denied * outright unless a red lint is absent AND consent is given AND scopes fit. * 5. RED LINT IS A HARD STOP. If the skill's security lint failed (critical * finding: exfiltration, hidden bidi, wallet-swap), execution is denied even * with consent — vet/quarantine first. * * This module is pure/deterministic (no I/O): it evaluates a request and returns * a decision. Enforcement is the caller's job — call `assertExecutionAllowed` * at the boundary and it throws SandboxViolationError when execution is not * permitted, so the failure is loud and un-swallowable. */ import type { LintReport } from './skill-lint'; export type ExecutionMode = 'dry-run' | 'execute' | 'denied'; export declare class SandboxViolationError extends Error { readonly decision: ExecutionDecision; constructor(decision: ExecutionDecision); } export interface ExecutionRequest { /** Skill slug, for messages. */ skill: string; /** True when the skill's provenance owner is the canonical first-party owner. */ firstParty: boolean; /** Explicit, per-action human consent to actually execute. Default false. */ consent?: boolean; /** Permission scopes the requested action needs. */ requestedScopes?: string[]; /** Permission scopes the skill's Skill Card declares. */ declaredScopes?: string[]; /** The skill's security lint report, when available. */ lint?: LintReport | null; } export interface ExecutionDecision { mode: ExecutionMode; /** True only when mode === 'execute'. Convenience for callers. */ allowed: boolean; /** Human-readable justification (always populated). */ reason: string; /** Scopes requested but NOT declared on the card (empty when all fit). */ undeclaredScopes: string[]; } /** The default, safe policy: nothing runs without explicit consent. */ export interface SandboxPolicy { /** When true (default), no request is ever auto-executed without consent. */ requireConsent: boolean; /** When true (default), a critical lint finding blocks execution outright. */ blockOnRedLint: boolean; } export declare const DEFAULT_SANDBOX_POLICY: SandboxPolicy; /** * Evaluate an execution request against the sandbox policy. Deterministic and * side-effect-free — this is the decision function; `assertExecutionAllowed` * turns a non-'execute' decision into a thrown error at the boundary. */ export declare function evaluateExecution(req: ExecutionRequest, policy?: SandboxPolicy): ExecutionDecision; /** * Boundary gate: throw SandboxViolationError unless the request resolves to * real execution. Call this at any point that would actually run a skill's * script/installer/hook. It is the "non-auto-execution gate": with no consent * the default decision is 'dry-run', which is NOT allowed, so this throws — * fail-loud, aligned with the GUARD-POLICY INSTALL frontier. */ export declare function assertExecutionAllowed(req: ExecutionRequest, policy?: SandboxPolicy): ExecutionDecision; /** * Human-readable summary of the sandbox model, surfaced by `cortex doctor` so * operators can see the policy that governs skill execution. */ export declare function describeSandboxPolicy(policy?: SandboxPolicy): string;