/** * Skill security lint — static analysis of a skill's source (P5-01b). * * This is the SINGLE canonical supply-chain linter for the Genius ecosystem. * It was duplicated ×4 before P5-01; the Store (Cortex) now owns it, and the * Revius `review-mcp-supply-chain` pack is meant to REUSE this exact module * rather than re-implement the denylist. The public interface below is the * contract for that reuse — keep it stable. * * ── Public interface (for Revius review-mcp-supply-chain and cortex doctor) ── * * lintSkillContent(content: string, opts?: LintOptions): LintReport * lintSkillCard(permissions: string[], opts?: LintOptions): LintFinding[] * DENYLIST: readonly LintRule[] // the exfiltration/fraud rules * worstSeverity(findings): LintSeverity * * A LintReport is `{ status, findings }` where status is: * - 'pass' no findings * - 'warn' only advisory findings (e.g. broad-but-declared permissions) * - 'fail' at least one 'critical' finding (exfiltration / hidden bidi / * wallet-swap commission fraud) — the "lint rouge" of cortex doctor. * * FAIL-LOUD contract: the linter NEVER throws on hostile input and NEVER * silently swallows a category. Unknown/edge input yields findings, not * exceptions, so a caller can always render a verdict. * * Alignment with GUARD-POLICY: for FIRST-PARTY skills served from the trusted * canonical GT source, findings are ADVISORY (surfaced by doctor, non-blocking * — "advisory in the loop"). Blocking on a red lint is reserved for the INSTALL * frontier of THIRD-PARTY skills, enforced by the vetting layer, not here. This * module only reports; it does not decide servability. */ export type LintSeverity = 'info' | 'warn' | 'critical'; export interface LintFinding { /** Stable machine id, e.g. 'exfil.curl-pipe-sh'. */ rule: string; severity: LintSeverity; /** Human-readable one-line explanation. */ message: string; /** 1-based line number in the linted content, when locatable. */ line?: number; /** The offending excerpt (truncated), for the audit trail. */ evidence?: string; } export interface LintReport { status: 'pass' | 'warn' | 'fail'; findings: LintFinding[]; } export interface LintOptions { /** * Declared permissions from the Skill Card. When provided, the linter can * flag broad/over-declared permissions. Optional so `lintSkillContent` works * standalone on raw text. */ permissions?: string[]; /** * Whether the skill is first-party (canonical GT owner). A wildcard permission * is a hard FAIL (critical) for a THIRD-PARTY skill (`firstParty === false`) * and only a 'warn' for a first-party one (or when provenance is unknown). */ firstParty?: boolean; /** * Cap on evidence excerpt length (default 120). Keeps reports compact. */ evidenceLimit?: number; } /** A denylist rule: a regex over the skill body plus its metadata. */ export interface LintRule { rule: string; severity: LintSeverity; message: string; pattern: RegExp; } /** * The exfiltration / fraud denylist (veille 2026-07-02, P0 ClawHub: * "exfiltration, secrets, curl | sh, crypto, commission fraud"). * * Patterns are intentionally conservative and evidence-bearing: each match is a * concrete, quotable line, not a vague heuristic, so a human reviewing the * finding can confirm it. All patterns are case-insensitive and multiline. */ export declare const DENYLIST: readonly LintRule[]; /** * Detect hidden zero-width and bidi-control characters. These are the highest * signal supply-chain tell: a benign-looking SKILL.md that renders one way and * carries invisible/dangerous content another way. */ export declare function detectHiddenUnicode(content: string, evidenceLimit?: number): LintFinding[]; /** Run the exfiltration/fraud denylist over the content. */ export declare function detectDenylist(content: string, evidenceLimit?: number): LintFinding[]; /** * Lint declared permissions against a least-privilege policy. A wildcard * permission is a hard FAIL (critical) for a THIRD-PARTY skill — a single * `['*']` would otherwise smuggle the full run-commands+network+secrets triad * past the least-privilege gate — and a 'warn' for a first-party skill (or when * provenance is unknown), which the vetting/sandbox layer treats as advisory. */ export declare function lintSkillCard(permissions?: string[], opts?: LintOptions): LintFinding[]; /** Reduce a set of findings to the single worst severity present. */ export declare function worstSeverity(findings: LintFinding[]): LintSeverity; /** * Lint a skill's SKILL.md content (and, optionally, its declared permissions). * This is the primary entry point. It never throws. */ export declare function lintSkillContent(content: string, opts?: LintOptions): LintReport;