/** * 12 AST patterns the audit scanner walks for. Each pattern carries a * stable id, a severity tier, the matcher predicate (over the AST * node) and a suggested-fix key. Patterns are pure functions over the * @typescript-eslint/parser AST; the scanner.ts walker calls them on * each `CallExpression` / `MemberExpression` it encounters. * * Source of truth for the threat catalogue: Ox-Security MCP audit * (venturebeat 2026-05-02), LiteLLM CVE patched in v1.83.7, plus the * `mcp-anti-patterns` Memory entity (#6 shell:true, #11 template-literal). */ import type { TSESTree } from "@typescript-eslint/types"; import type { AuditPatternId } from "./suggested-fix.js"; export type AuditSeverity = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"; export interface AuditFinding { id: AuditPatternId; severity: AuditSeverity; file: string; line: number; column: number; snippet: string; suggestedFix: string; } export interface PatternMatch { id: AuditPatternId; severity: AuditSeverity; /** Short label rendered in markdown reports. */ label: string; } interface AnalysedCall { /** Final dotted-name like `child_process.exec` if resolvable. */ callee: string | null; args: TSESTree.CallExpressionArgument[]; /** * Canonical child_process method this call resolves to when the callee * is a *renamed* binding the scanner proved originates from * child_process (e.g. `execAsync` from `promisify(exec)` resolves to * `"exec"`). When set, the rules treat the call as that method even * though `callee` is the alias. Purely additive on top of the * name-based matching above. See ./bindings.ts. */ resolvedMethod?: string | null; } declare function isChildProcessMethod(name: string | null, method: string): boolean; declare function isLiteralString(node: TSESTree.Node | undefined): boolean; declare function isDynamicString(node: TSESTree.Node | undefined): boolean; declare function templateHasInterpolation(node: TSESTree.Node | undefined): boolean; declare function isLiteralArray(node: TSESTree.Node | undefined): boolean; declare function isDynamicArray(node: TSESTree.Node | undefined): boolean; declare function objectHasShellTrue(node: TSESTree.Node | undefined): boolean; export interface PatternRule { id: AuditPatternId; severity: AuditSeverity; label: string; /** * Inspect a CallExpression and decide whether this rule fires. A * single rule can fire only once per call. */ test(call: AnalysedCall): boolean; } export declare const PATTERN_RULES: readonly PatternRule[]; export declare const SEVERITY_RANK: Record; export declare function severityAtOrAbove(finding: AuditSeverity, floor: AuditSeverity): boolean; /** * Apply rules to one CallExpression. Each rule is at most fired once * per call, but a single call may match multiple rules (e.g. dynamic * exec + missing timeout). The caller gets the union. * * `resolvedMethod` lets the scanner pass a canonical child_process * method name when the callee is a *renamed* binding it proved comes * from child_process (see ./bindings.ts). It is optional and purely * additive — omitting it preserves the original name-based behaviour. */ export declare function evaluateCall(node: TSESTree.CallExpression, resolvedMethod?: string | null): PatternMatch[]; export declare const __test__: { isLiteralString: typeof isLiteralString; isDynamicString: typeof isDynamicString; templateHasInterpolation: typeof templateHasInterpolation; isLiteralArray: typeof isLiteralArray; isDynamicArray: typeof isDynamicArray; objectHasShellTrue: typeof objectHasShellTrue; isChildProcessMethod: typeof isChildProcessMethod; }; export {}; //# sourceMappingURL=patterns.d.ts.map