/** * ATR → Sage (gendigitalinc/sage) Converter * * Converts ATR YAML rules into Sage's threat rule schema, suitable for * Sage's `threats/agent-layer.yaml`. Sage uses a single-pattern-per-rule * schema with `match_on` controlling the artifact channel; ATR uses * multi-condition rules with `field`-targeted regexes. * * Conversion strategy (per research memo): * - One ATR rule → one or more Sage rules, grouped by `field` target. * Conditions on the same field combine via regex alternation. * - ATR `field` values (user_input, agent_output, content, tool_response, * tool_args, tool_name, tool_description) collapse to Sage `match_on: * content`. Sage's other channels (command, url, file_path, domain) are * command-time matchers and do not have ATR equivalents in the current * corpus. * - ATR `response.actions` (10 values) collapse to Sage `action` (3 * values: block / require_approval / log) by strongest-wins semantics. * - ATR `(?i)` inline flag (PCRE) is extracted into Sage's rule-level * `case_insensitive: true` because the Sage runtime regex compiler is * JavaScript and does not support inline flag groups. * - ATR `detection_tier: semantic` rules are SKIPPED — they require LLM * evaluation, not deterministic regex. * - License: ATR rules are MIT; Sage's `threats/*.yaml` are DRL 1.1. Per-rule * comment in the output preserves upstream attribution per both licenses. * * Used by: scripts/sync-with-atr.ts (Sage maintainers can run this script * to regenerate threats/agent-layer.yaml from a pinned ATR version), and * by any downstream tool that consumes ATR but wants Sage-flavoured output. * * @module agent-threat-rules/converters/sage */ import type { ATRRule } from '../types.js'; export type SageSeverity = 'critical' | 'high' | 'medium' | 'low'; export type SageAction = 'block' | 'require_approval' | 'log'; export type SageMatchOn = 'command' | 'url' | 'file_path' | 'content' | 'domain'; export interface SageRule { readonly id: string; readonly category: string; readonly severity: SageSeverity; readonly confidence: number; readonly action: SageAction; readonly pattern: string; readonly match_on: SageMatchOn | readonly SageMatchOn[]; readonly title: string; readonly expires_at: string | null; readonly revoked: boolean; readonly case_insensitive?: boolean; /** Non-standard provenance fields preserved per the Sage loader's permissive parsing. */ readonly upstream?: string; readonly upstream_url?: string; readonly upstream_license?: string; /** 1-2 sentence attack-scenario summary derived from ATR `description`, emitted as a `# Detects:` comment in YAML output. */ readonly detects?: string; } export interface ConversionWarning { readonly ruleId: string; readonly kind: 'semantic_tier_skipped' | 'unsupported_action_dropped' | 'regex_compat_issue' | 'split_by_length' | 'condition_without_field' | 'no_convertible_conditions' | 'deprecated_skipped' | 'draft_marked_revoked'; readonly detail: string; } export interface ConvertResult { readonly rules: readonly SageRule[]; readonly warnings: readonly ConversionWarning[]; } /** * Generate a Sage rule id matching Sage's existing 3-digit-suffix convention. * Format: CLT-- where PREFIX is the 2-3 letter category code. * * The id is generated sequentially by an IdAllocator passed in by the caller, * NOT derived from the ATR id. This matches Sage's convention (CLT-PI-001, * CLT-MCP-001, etc.) instead of producing weird-looking ids like CLT-PRV-0441. * * The ATR provenance survives in the `# Upstream: ATR-2026-NNNNN` comment. */ export declare class SageIdAllocator { private counters; /** * Construct with optional starting offsets per category. Pass * `{prompt_injection: 8}` to start prompt_injection ids at 008 (after * Sage's existing CLT-PI-001..007, for example). */ constructor(startingOffsets?: Readonly>); next(sageCategory: string, channelDisambiguator: string | null): string; } /** * Convert a single ATR rule to one or more Sage rules. * * Returns one Sage rule per unique `field` target in the ATR rule's detection * conditions. Most ATR rules in the current corpus target a single field * (user_input or tool_response or content), producing 1 Sage rule each. * * Lossy spots (documented in the bridge research memo): * - Multi-condition rules with mixed case-sensitivity → whole rule becomes * case-insensitive * - `condition: all` semantics → ignored; alternation always treats as OR * - `response.actions` reduced to strongest single action * - All compliance/metadata fields (eu_ai_act, mitre_atlas, references, etc.) * are dropped (not in Sage schema). They survive in the upstream_url link. */ export declare function atrToSage(atr: ATRRule, idAllocator?: SageIdAllocator): ConvertResult; /** * Convert many ATR rules to Sage rules. Aggregates warnings and uses a shared * id allocator so that the resulting Sage rules have sequential 3-digit ids * (CLT-PRV-001, CLT-PRV-002, ...) within each category, matching Sage's * existing convention. * * @param atrRules the ATR rules to convert * @param startingOffsets optional starting offsets per Sage category — pass * `{prompt_injection: 7}` if Sage's existing rules already use CLT-PI-001 * through CLT-PI-007 so the new rules start at CLT-PI-008. */ export declare function atrToSageBatch(atrRules: readonly ATRRule[], startingOffsets?: Readonly>): ConvertResult; /** * Serialize a list of Sage rules to a YAML string suitable for inclusion in * `threats/agent-layer.yaml`. The output matches the style used in Sage's * existing agent-layer.yaml (no list indentation, double-quoted ids, * single-line regex pattern, upstream comment after each rule). * * Caller is responsible for prepending file-level comments (license, * generation timestamp, upstream version) before this output. */ export declare function sageRulesToYaml(rules: readonly SageRule[]): string; //# sourceMappingURL=sage.d.ts.map