/** * Core obfuscation engine: detect -> map -> replace / reverse-replace. * * Entirely synchronous (CPU-bound) -- this is important for the * tool_result_persist hook which is sync-only. */ import { DetectedEntity, ObfuscationResult, ShroudConfig } from "./types.js"; import { SerializedStore } from "./store.js"; import { BaseDetector } from "./detectors/base.js"; export declare class Obfuscator { config: ShroudConfig; private _store; private _subnetMapper; private _mapping; private _detectors; private _canary; private _audit; private _ruleHits; private _detectionsByCategory; private _replacementsByCategory; private _obfuscationEvents; private _deobfuscationEvents; private _totalEntitiesObfuscated; private _totalReplacementsDeobfuscated; private _redactionFormatter; private _contextDetector; private _toolDepth; constructor(config: ShroudConfig); private _initDetectors; /** * Hot-swap config at runtime (called by ConfigManager on reload). * Preserves mappings, stores, and stats — only swaps behaviour flags. * Fields that require restart (secretKey, persistentSalt, etc.) are * already filtered by ConfigManager before this is called. */ updateConfig(newConfig: ShroudConfig): void; /** Add a custom detector at runtime. */ addDetector(detector: BaseDetector): void; /** Track tool call depth. */ enterToolCall(): number; /** Decrement tool depth. */ exitToolCall(): number; /** Current tool depth. */ get toolDepth(): number; /** Reset tool depth counter (called at the start of each LLM turn). */ resetToolDepth(): void; /** * Detect and replace all sensitive entities in text. * * The pipeline: * 1. Learn subnets from text (via SubnetMapper) * 2. Detect entities from all detectors * 3. Apply denylist (force-add denylist values) * 4. Sort by position, resolve overlaps (prefer higher confidence) * 5. Filter by minConfidence, allowlist, and already-obfuscated * 6. Map and replace (with redaction level) * 7. Inject canary if enabled */ obfuscate(text: string, context?: string, exemptCategories?: Set): ObfuscationResult; /** * Reverse-map fake values back to real values in text. * * Uses longest-match-first replacement to avoid partial substitutions. * Also strips canary tokens. * Runs multiple passes for nested structures. */ deobfuscate(text: string): string; /** * Deobfuscate text and return replacement count alongside the result. * Used by audit logging to report deobfuscation stats without logging text. */ deobfuscateWithStats(text: string): { text: string; replacementCount: number; }; /** * Subnet-aware reverse mapping for CGNAT IPs not in the store. */ private _deobfuscateResidualCgnat; /** * Clean up CGNAT range descriptions that LLMs generate when summarizing * fake networks. The LLM sees multiple 100.64.x.y addresses and writes * summaries like "100.64.x.x/xx" or "within 100.64.x.x space". * * Strategy: find the most common real network prefix from the store * mappings and replace CGNAT range descriptions with the real prefix. */ private _deobfuscateCgnatRangeDescriptions; /** * Normalize-and-match deobfuscation for fd00::/8 ULA IPv6 addresses. */ private _deobfuscateResidualUla; /** Clear all mappings and start fresh. */ reset(): void; /** Return the max fake value length in the store (for streaming holdback). */ maxFakeLength(): number; /** Return stats from audit logger and store. */ getStats(): object; /** Export the mapping store for persistence across restarts. */ exportStore(): SerializedStore; /** Import mappings from a persisted store. Returns count of new mappings added. */ importStore(data: SerializedStore): number; } /** Remove overlapping entities, keeping higher confidence ones. */ export declare function resolveOverlaps(entities: DetectedEntity[]): DetectedEntity[];