/** * NemoClaw / OpenShell supply-chain pre-flight scanner (ATR Seam B). * * Scans an agent bundle — SKILL.md skills and MCP server configs — BEFORE it is * provisioned into a NemoClaw/OpenShell sandbox, and returns a gate verdict: * exit 0 = pass (all targets scanned, nothing at/above threshold) * exit 1 = block (a rule at/above the threshold matched) * exit 2 = incomplete (could not fully scan — oversized/unreadable/symlink/ * too many files) OR internal error * * It is FAIL-CLOSED: because this is the only pre-deployment barrier, an * incomplete scan is never reported as a pass. A skipped file (too large to * scan, unreadable, a symlink, or beyond the file cap) yields an "incomplete" * verdict the caller must treat as a gate failure, not a clean result. * * This is the pre-deployment counterpart to the runtime content filter * (openshell-filter): the filter inspects L7 traffic at run time; this inspects * the skill / MCP supply chain at provision time — a gap neither OpenShell's * capability policy nor a runtime guard covers. * * Why MCP configs specifically: OpenShell/NemoClaw run OpenClaw/Hermes agents * that load skills and register MCP servers (.mcp.json, .cursor/mcp.json, * .claude/settings.json, ...). Malicious server definitions (stdio RCE, unauth * registration, SymJack symlink-config redirection) are a content-level attack * surface ATR has rules for. The `atr scan` CLI treats .json as MCP *events*; * this scans MCP *config* files as content, which is the missing path. * * Symlinks are never followed (cycle-safe, version-consistent across the * supported Node range, and SymJack-safe); a symlink that would otherwise be a * scan target is recorded as skipped so it cannot pass silently. * * Findings use the canonical atr.* shape (matched values redacted by default per * the engine INTERFACE-CONTRACT), consistent with openshell-filter, so Seam A * (runtime) and Seam B (supply chain) feed the same OCSF detection_finding * audit stream. Reuses ATREngine in skill context; adds no dependency. * * @module agent-threat-rules/nemoclaw-preflight */ export type Decision = 'pass' | 'block' | 'incomplete'; /** Reason a target could not be fully scanned (fail-closed signals). */ export type SkipReason = 'too_large' | 'unreadable' | 'symlink' | 'max_files_exceeded'; export interface PreflightOptions { /** Block when a matched rule's severity is at or above this (default "high"). */ readonly minSeverity: string; /** Directory of ATR rule YAML files. Omit to use the rules bundled with the package. */ readonly rulesDir?: string; /** When true, include the raw matched value instead of redacting it. */ readonly forensic: boolean; /** Skip files larger than this (default 1 MB) — skipped files block the gate. */ readonly maxFileBytes?: number; /** Stop collecting after this many target files (default 2000) — truncation blocks the gate. */ readonly maxFiles?: number; } /** A finding, in the canonical atr.* shape plus the source file that triggered it. */ export interface PreflightFinding { readonly 'atr.rule_id': string; readonly 'atr.severity': string; readonly 'atr.category': string; readonly 'atr.confidence': number; readonly 'atr.matched_value_redacted': string; readonly 'atr.source_path': string; } export interface SkippedFile { readonly path: string; readonly reason: SkipReason; } export interface PreflightResult { readonly decision: Decision; /** Number of files actually read and evaluated (not merely discovered). */ readonly scanned: number; /** Targets that could not be fully scanned. Non-empty ⇒ verdict cannot be a clean pass. */ readonly skipped: readonly SkippedFile[]; readonly findings: readonly PreflightFinding[]; } /** The supply-chain pre-flight scanner. Construct once, reuse across bundles. */ export declare class NemoClawPreflight { private readonly options; private readonly engine; private readonly blockRank; private readonly maxFileBytes; private readonly maxFiles; private loadPromise; constructor(options: PreflightOptions); private ensureLoaded; /** * Scan one file's content in skill context. Returns the findings and a skip * reason (null if the file was actually read and evaluated). Oversized and * unreadable files are reported as skipped (fail-closed), never silently * treated as clean. */ private scanFile; private toFinding; /** * Scan an agent bundle (directory or single file). Block on any finding at or * above the threshold; otherwise report incomplete if any target could not be * fully scanned (fail-closed); otherwise pass. */ scanBundle(root: string): Promise; } /** Build options from the ATR_* environment contract. */ export declare function optionsFromEnv(): PreflightOptions; //# sourceMappingURL=nemoclaw-preflight.d.ts.map