/** * Native secret-detection + prompt-injection scanning {@link ToolHook} — the * TypeScript port of the Rust reference engine's `narc.rs` (pearl th-5f7227). * * The SEP extension host passes tool-call arguments to the extension subprocess * **unscanned** and returns the subprocess's tool-result content to the model * **verbatim**. Nothing at the extension boundary looks for leaked credentials or * prompt-injection payloads. {@link NarcHook} closes that gap. It scans two things: * * - **Secrets** — 10 credential patterns (AWS keys, private keys, JWTs/bearer * tokens, high-entropy provider keys, …). * - **Prompt injection** — 8 patterns (instruction override, role hijack, * jailbreak, data/URL exfiltration, …). * * ## Division of labour with `PermissionHook` * * The permission gate already owns the *dangerous-command* / *write* / * *credential-path* circuit-breakers (`rm -rf /`, `curl | sh`, `~/.ssh/id_rsa`). * Narc does **not** re-implement those — it is scoped to the one thing permission * does not do: **content scanning of arguments and results** for secrets and * injection. Install Narc *after* the permission hook so the allow/ask/deny * decision happens first and Narc scans the calls that clear it. * * ## `preCall` (arguments) — blocks on exfiltration, alerts otherwise * * A {@link Severity.Block} injection match (the active data/URL exfiltration * signals) throws, blocking the call before the tool runs. Lower-severity * injection and any secret in the arguments are **alerted, not blocked** — a tool * argument legitimately carrying a secret (writing a `.env`, configuring a * client) is common enough that a hard block there would be a footgun. * * ## `postCall` (result) — detects, alerts, and **redacts** secrets * * `postCall` receives a **mutable** result, so a rewrite of `result.content` is * what downstream consumers — and the LLM/conversation — actually see. A secret * pattern in a tool result raises a {@link Severity.Block} alert *and* replaces * the matched credential with `[REDACTED:]` before it reaches the * model. Injection patterns in the result remain detection + {@link Severity.Alert} * only (surveillance) — they can appear in legitimate content and are not rewritten. * * The detection set is pinned across all five engines by the shared corpus at * `spec/narc/corpus.json` (see `test/narc.test.ts`). */ import type { ToolCall, ToolHook, ToolResult } from './agent.js'; /** * Severity of a Narc finding, ordered least → most severe. A * {@link Severity.Block} finding in `preCall` blocks the tool call. The numeric * values ARE the ordering — compare with `>=`. */ export declare enum Severity { /** Informational — no action. */ Info = 0, /** Suspicious but plausibly legitimate (e.g. a secret in an argument). */ Warn = 1, /** Strong signal worth surfacing, but not auto-blocked. */ Alert = 2, /** Actively harmful — blocks the call when raised in `preCall`. */ Block = 3 } /** The shared wire label for a severity (`INFO`/`WARN`/`ALERT`/`BLOCK`). */ export declare function severityLabel(s: Severity): string; /** * A single surveillance finding. Lean by design — the consumer supplies the * timestamp and correlation, so no uuid/timestamp fields are carried. */ export interface NarcAlert { /** How severe the finding is. */ severity: Severity; /** Coarse bucket: `'injection'`, `'secret'`, `'secret_leak'`, `'injection_output'`. */ category: string; /** The named pattern that matched. */ patternName: string; /** Redacted view of the matched text (never the raw secret). */ redacted: string; /** The tool whose args/result triggered the finding. */ toolName: string; } /** A pattern match: which pattern, its severity, and a redacted view. */ export interface NarcFinding { /** The named pattern that matched. */ patternName: string; /** The finding's severity. */ severity: Severity; /** Redacted view of the matched text (safe to log). */ redacted: string; } /** Scan `text` for hardcoded secrets. Every match is redacted. */ export declare function scanSecrets(text: string): NarcFinding[]; /** Scan `text` for prompt-injection patterns. Matched text is redacted. */ export declare function scanInjection(text: string): NarcFinding[]; /** True if `text` contains any secret pattern. */ export declare function hasSecrets(text: string): boolean; /** True if `text` contains any injection pattern. */ export declare function hasInjection(text: string): boolean; /** * Redact a matched string, showing only the first 4 and last 2 characters. Short * matches (≤ 8 code points) are fully starred. Code points, not UTF-16 units — * parity with the Rust reference's char-based redaction. */ export declare function redactMatch(s: string): string; /** * A {@link ToolHook} that scans tool-call arguments and results for secrets and * prompt injection. Install it via `AgentOptions.toolHooks` alongside the * permission hook, *after* it. * * - **`preCall`** throws on a {@link Severity.Block} injection pattern in the * arguments (active exfiltration); every other finding (lower-severity * injection, any secret) is recorded as a {@link NarcAlert}, not blocked. * - **`postCall`** detects secrets/injection in the result, records them, and * **redacts** leaked secrets out of the content in place so the model never * sees the raw credential. */ export declare class NarcHook implements ToolHook { private readonly log; /** Snapshot every recorded alert. */ alerts(): NarcAlert[]; /** Recorded alerts at or above `minSeverity`. */ alertsAbove(minSeverity: Severity): NarcAlert[]; private record; preCall(call: ToolCall): Promise; postCall(call: ToolCall, result: ToolResult): Promise; } //# sourceMappingURL=narc.d.ts.map