llm-trust-guard 4.18.1 → 4.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,56 @@ All notable changes to `llm-trust-guard` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.19.1] - 2026-04-23
9
+
10
+ ### Added — Measured Performance
11
+
12
+ - Published held-out benchmark results in [tests/adversarial/RESULTS-v4.19.0.md](tests/adversarial/RESULTS-v4.19.0.md). Methodology, 95% Wilson CIs, hand-adjudicated label noise, and reproducibility scripts
13
+ - New README section "Measured Performance" summarizing the findings:
14
+ - On Giskard (n=35) and Compass CTF Chinese (n=11), Pipeline A detection rate is unchanged from v4.13.5 (80.00%, 9.09%). Underpowered — "no evidence of improvement," not "proof of no improvement"
15
+ - On WildChat-1M (10,000 real ChatGPT production prompts, seed=42), Pipeline A corrected FPR is ~2.73% [95% CI 2.43, 2.84] after canonical-marker + 50-sample hand-adjudication (203 canonical TPs + ~17 extrapolated TPs among unmarked → ~220 true jailbreak attempts in the 493 blocks)
16
+ - Same order of magnitude as Meta Prompt Guard 86M's self-reported 3–5% OOD FPR; not a head-to-head comparison
17
+ - New reproducibility scripts: `tests/adversarial/extract_wildchat.py`, `classify_wildchat_blocks.py`, `wildchat-fpr.ts`, `wildchat-block-dump.ts`, `v419-delta-benchmark.ts`, `hand-labels.json`, and `tests/adversarial/README.md`
18
+
19
+ ### Not changed
20
+
21
+ - No code changes to any guard. No detection patterns added or modified. No published API changed.
22
+ - All 705 tests still pass
23
+
24
+ ### Context
25
+
26
+ - [ARTICLE-2-REGEX-CEILING.md](https://github.com/nkratk/llm-trust-guard/blob/main/../ARTICLE-2-REGEX-CEILING.md) now includes a 2026-04-23 addendum reconciling v4.13.5 → v4.19.0
27
+
28
+ ## [4.19.0] - 2026-04-23
29
+
30
+ ### Added — Indirect Injection Expansion
31
+
32
+ RAGGuard `INDIRECT_INJECTION_PATTERNS` now covers three classes that were previously unhandled in published LLM trust benchmarks (all verified as genuinely absent before the add):
33
+
34
+ - **CSS-hidden text** (`css_hidden_text`): inline `style=` attributes declaring `display:none`, `visibility:hidden`, `opacity:0`, or `font-size:0`. Catches attacker text that renders invisibly in a browser but is still read by the LLM when the page is fed to it
35
+ - **HTML attribute directives** (`html_attr_directive`): prompt-injection content smuggled into `alt`, `title`, `aria-label`, or `data-*` attributes — a growing vector as agents increasingly process DOM-adjacent content
36
+ - **JSON agent-directive fields** (`json_agent_directive`): structured payloads using underscore-prefixed keys (`_system`, `__override`, `_agent_instructions`, `__system_prompt__`, `_assistant_role`, `__internal_directive`, `_meta_instruction`) to inject directives through structured context
37
+
38
+ ### Added — "Reprompt"-Class Markdown Image Exfiltration
39
+
40
+ Addresses the CVE-2026-24307 Copilot Personal "Reprompt" exfiltration pattern (Varonis, disclosed 2025-08-31, patched by Microsoft 2026-01-13):
41
+
42
+ - **`markdown_image_exfil_long_value`** (ExternalDataGuard): markdown image URL with any query-param value ≥30 characters. Legitimate cache-busters are short version strings or hashes; exfiltrated payloads run longer. Complements the existing named-key pattern for the case where the attacker uses innocuous param names
43
+ - **Widened `markdown_image_exfil`** named-key list: added `p`, `prompt`, `ctx`, `context`, `info`, `msg`, `body`, `session`, `conv` (was `token|key|secret|data|q|payload`)
44
+
45
+ ### Documented — CVE-2026-25536 SDK Advisory
46
+
47
+ MCPSecurityGuard docstring now explicitly calls out CVE-2026-25536 (`@modelcontextprotocol/sdk` 1.10.0–1.25.3, CVSS 7.1, cross-client response data leak). This is an upstream SDK bug that cannot be mitigated at the detection layer — the fix is `@modelcontextprotocol/sdk >=1.26.0`. Flagging it here so users evaluating MCP security know to pin the SDK version alongside using this guard.
48
+
49
+ ### Tests
50
+
51
+ - +7 RAGGuard tests (CSS-hidden display:none, opacity:0, alt-attr directive, aria-label directive, JSON `_system`, JSON `__override`, legitimate-style false-positive check)
52
+ - +3 ExternalDataGuard tests (Reprompt-style long-value exfil, new named-key variant, legitimate cache-buster FP check)
53
+ - **All 705 tests pass** (was 695), zero regressions
54
+
55
+ ### Stats
56
+ - 34 guards, 705 tests, zero dependencies (unchanged)
57
+
8
58
  ## [4.18.1] - 2026-04-20
9
59
 
10
60
  ### Fixed — Metadata and README Accuracy
package/README.md CHANGED
@@ -222,6 +222,25 @@ const output = guard.filterOutput(llmResponse, session.role);
222
222
  | ASI09: Trust Exploitation | TrustExploitationGuard | Strong |
223
223
  | ASI10: Rogue Agents | DriftDetector, AutonomyEscalationGuard | Moderate |
224
224
 
225
+ ## Measured Performance
226
+
227
+ v4.19.0 benchmark, 2026-04-23. Full methodology, 95% confidence intervals, hand-adjudication labels, and reproducibility scripts: [tests/adversarial/RESULTS-v4.19.0.md](tests/adversarial/RESULTS-v4.19.0.md).
228
+
229
+ **Attack detection on prior-published corpora** (Giskard n=35, Compass CTF Chinese n=11): detection rate has not moved from v4.13.5 → v4.19.0 on the Sanitizer+Encoder pipeline — 80.00% and 9.09% respectively, identical to the v4.13.5 numbers. Six releases of pattern additions (v4.14–v4.19) targeted different attack classes (indirect injection, tool-result validation, memory persistence, multi-agent trust) that these direct-text jailbreak corpora do not exercise. Small sample sizes mean "no evidence of improvement," not "proof of no improvement."
230
+
231
+ **False-positive rate on real ChatGPT production traffic** (WildChat-1M shard 0, 10,000 non-toxic multilingual first-user-turns, seed=42):
232
+
233
+ | Pipeline | Blocks | Raw block rate | Corrected FPR (after label adjudication) |
234
+ |---|---|---|---|
235
+ | Sanitizer + Encoder | 493 / 10,000 | 4.93% [4.52, 5.37] | **~2.73%** [2.43, 2.84] |
236
+ | Detection-only facade | 898 / 10,000 | 8.98% [8.44, 9.56] | ≤6.69% (upper bound; not fully adjudicated) |
237
+
238
+ WildChat filters toxic content but not prompt-injection intent. Canonical-marker analysis + a 50-sample hand-adjudication found that approximately 220 of the 493 Pipeline A blocks are actual jailbreak attempts users sent to ChatGPT, not genuine false positives. Corrected FPR is in the same order of magnitude as Meta Prompt Guard 86M's self-reported 3–5% out-of-distribution FPR — not a head-to-head comparison, but a useful reference point.
239
+
240
+ **Not measured:** detection rate on the four attack classes added in v4.19.0 (CSS-hidden content, HTML attribute directives, JSON agent-directive fields, Reprompt-class markdown exfiltration). No public held-out corpus exists for these at statistical scale. Unit-test coverage is in the v4.19.0 test suite; independent third-party evaluation is invited.
241
+
242
+ For higher detection on adversarial corpora, plug in an ML classifier via the [DetectionClassifier interface](#pluggable-detection).
243
+
225
244
  ## Defense In Depth
226
245
 
227
246
  This package is one layer. For production systems, combine with:
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ExternalDataGuard=void 0;const INJECTION_PATTERNS=[{name:"system_tag",pattern:/<\/?system>|<\/?admin>|\[system\]|\[admin\]/i},{name:"ignore_instructions",pattern:/ignore\s+(?:all\s+)?(?:previous|prior|above|your)\s+(?:instructions|rules|prompts?)/i},{name:"new_instructions",pattern:/new\s+instructions?\s*:/i},{name:"role_override",pattern:/you\s+are\s+now|from\s+now\s+on|act\s+as\s+(?:a|an)\s/i},{name:"hidden_instruction",pattern:/HIDDEN_PROMPT|HIDDEN_INSTRUCTION|INVISIBLE_TEXT/i},{name:"jailbreak",pattern:/jailbreak|DAN\s*mode|developer\s+mode|unrestricted\s+mode/i},{name:"bypass_safety",pattern:/bypass\s+(?:security|safety|filters|restrictions|guardrails)/i},{name:"instruction_delimiter",pattern:/={3,}\s*(?:SYSTEM|INSTRUCTIONS?|BEGIN)\s*={3,}/i},{name:"prompt_leak_request",pattern:/(?:print|show|reveal|output)\s+(?:your|the|system)\s+(?:prompt|instructions)/i},{name:"base64_injection",pattern:/(?:decode|eval|execute)\s+(?:the\s+)?(?:following\s+)?base64/i}],SECRET_PATTERNS=[{name:"aws_key",pattern:/AKIA[0-9A-Z]{16}/},{name:"generic_api_key",pattern:/(?:api[_-]?key|apikey|api[_-]?secret)\s*[=:]\s*["']?[A-Za-z0-9_\-]{20,}/i},{name:"bearer_token",pattern:/Bearer\s+[A-Za-z0-9\-._~+\/]{20,}/},{name:"private_key",pattern:/-----BEGIN\s+(?:RSA|EC|DSA|OPENSSH)?\s*PRIVATE\s+KEY-----/},{name:"github_token",pattern:/gh[ps]_[A-Za-z0-9_]{36,}/},{name:"jwt",pattern:/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/},{name:"password_field",pattern:/(?:password|passwd|pwd)\s*[=:]\s*["'][^"']{8,}/i},{name:"connection_string",pattern:/(?:mongodb|postgres|mysql|redis):\/\/[^\s]{10,}/i}],EXFILTRATION_PATTERNS=[{name:"markdown_image_exfil",pattern:/!\[.*?\]\(https?:\/\/[^)]*\?[^)]*(?:token|key|secret|data|q|payload)=/i},{name:"tracking_pixel",pattern:/<img[^>]+src=["']https?:\/\/[^"']*\?[^"']*["'][^>]*(?:width|height)\s*=\s*["']?[01]px/i},{name:"encoded_url_exfil",pattern:/https?:\/\/[^\s]*(?:callback|webhook|exfil|collect)[^\s]*\?[^\s]*(?:data|payload|d)=/i},{name:"data_send_instruction",pattern:/send\s+(?:this|the|all)\s+(?:data|information|content|context)\s+to/i},{name:"fetch_url",pattern:/(?:fetch|request|call|curl|wget)\s+https?:\/\//i}],PII_PATTERNS=[{name:"ssn",pattern:/\b\d{3}-\d{2}-\d{4}\b/},{name:"credit_card",pattern:/\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b/},{name:"email_address",pattern:/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}\b/i}];class ExternalDataGuard{constructor(e={}){this.config={...e,maxContentLength:e.maxContentLength??5e4,scanForInjection:e.scanForInjection??!0,scanForSecrets:e.scanForSecrets??!0,scanForExfiltration:e.scanForExfiltration??!0,requireProvenance:e.requireProvenance??!1}}validate(e,s){const t=[],n=[],i=typeof e=="string"?e:this.safeStringify(e),o=s?.source;if(this.config.requireProvenance&&!s?.source&&(t.push("MISSING_PROVENANCE"),n.push("no_source_metadata")),o&&(this.isBlockedSource(o)&&(t.push("BLOCKED_SOURCE"),n.push("blocked_data_source")),this.config.allowedSources&&this.config.allowedSources.length>0&&(this.isAllowedSource(o)||(t.push("UNAPPROVED_SOURCE"),n.push("source_not_in_allowlist")))),i.length>this.config.maxContentLength&&(t.push("CONTENT_TOO_LARGE"),n.push("context_stuffing")),s?.retrievedAt&&s?.maxAgeSec){const r=typeof s.retrievedAt=="string"?new Date(s.retrievedAt).getTime():s.retrievedAt;Date.now()-r>s.maxAgeSec*1e3&&(t.push("STALE_DATA"),n.push("data_expired"))}if(this.config.scanForInjection)for(const{name:r,pattern:a}of INJECTION_PATTERNS)a.lastIndex=0,a.test(i)&&(t.push("INJECTION_DETECTED"),n.push(`injection:${r}`));if(this.config.scanForSecrets){for(const{name:r,pattern:a}of SECRET_PATTERNS)a.lastIndex=0,a.test(i)&&(t.push("SECRET_DETECTED"),n.push(`secret:${r}`));for(const{name:r,pattern:a}of PII_PATTERNS)a.lastIndex=0,a.test(i)&&(t.push("PII_DETECTED"),n.push(`pii:${r}`))}if(this.config.scanForExfiltration)for(const{name:r,pattern:a}of EXFILTRATION_PATTERNS)a.lastIndex=0,a.test(i)&&(t.push("EXFILTRATION_ATTEMPT"),n.push(`exfil:${r}`));const c=[...new Set(t)],u=[...new Set(n)],l=c.length===0,d={allowed:l,reason:l?void 0:`External data rejected: ${c.join(", ")}`,violations:c,source:o,contentLength:i.length,threats:u};return l||this.log(`Blocked external data: ${c.join(", ")}`,"warn"),d}validateBatch(e){const s=e.map(t=>this.validate(t.content,t.provenance));return{results:s,allAllowed:s.every(t=>t.allowed),totalThreats:s.reduce((t,n)=>t+n.threats.length,0)}}isBlockedSource(e){if(!this.config.blockedSources)return!1;const s=e.toLowerCase();return this.config.blockedSources.some(t=>s.includes(t.toLowerCase()))}isAllowedSource(e){if(!this.config.allowedSources)return!0;const s=e.toLowerCase();return this.config.allowedSources.some(t=>s.startsWith(t.toLowerCase()))}log(e,s){this.config.logger?.(e,s)}safeStringify(e){try{return JSON.stringify(e)}catch{return String(e)}}}exports.ExternalDataGuard=ExternalDataGuard;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ExternalDataGuard=void 0;const INJECTION_PATTERNS=[{name:"system_tag",pattern:/<\/?system>|<\/?admin>|\[system\]|\[admin\]/i},{name:"ignore_instructions",pattern:/ignore\s+(?:all\s+)?(?:previous|prior|above|your)\s+(?:instructions|rules|prompts?)/i},{name:"new_instructions",pattern:/new\s+instructions?\s*:/i},{name:"role_override",pattern:/you\s+are\s+now|from\s+now\s+on|act\s+as\s+(?:a|an)\s/i},{name:"hidden_instruction",pattern:/HIDDEN_PROMPT|HIDDEN_INSTRUCTION|INVISIBLE_TEXT/i},{name:"jailbreak",pattern:/jailbreak|DAN\s*mode|developer\s+mode|unrestricted\s+mode/i},{name:"bypass_safety",pattern:/bypass\s+(?:security|safety|filters|restrictions|guardrails)/i},{name:"instruction_delimiter",pattern:/={3,}\s*(?:SYSTEM|INSTRUCTIONS?|BEGIN)\s*={3,}/i},{name:"prompt_leak_request",pattern:/(?:print|show|reveal|output)\s+(?:your|the|system)\s+(?:prompt|instructions)/i},{name:"base64_injection",pattern:/(?:decode|eval|execute)\s+(?:the\s+)?(?:following\s+)?base64/i}],SECRET_PATTERNS=[{name:"aws_key",pattern:/AKIA[0-9A-Z]{16}/},{name:"generic_api_key",pattern:/(?:api[_-]?key|apikey|api[_-]?secret)\s*[=:]\s*["']?[A-Za-z0-9_\-]{20,}/i},{name:"bearer_token",pattern:/Bearer\s+[A-Za-z0-9\-._~+\/]{20,}/},{name:"private_key",pattern:/-----BEGIN\s+(?:RSA|EC|DSA|OPENSSH)?\s*PRIVATE\s+KEY-----/},{name:"github_token",pattern:/gh[ps]_[A-Za-z0-9_]{36,}/},{name:"jwt",pattern:/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/},{name:"password_field",pattern:/(?:password|passwd|pwd)\s*[=:]\s*["'][^"']{8,}/i},{name:"connection_string",pattern:/(?:mongodb|postgres|mysql|redis):\/\/[^\s]{10,}/i}],EXFILTRATION_PATTERNS=[{name:"markdown_image_exfil",pattern:/!\[.*?\]\(https?:\/\/[^)]*\?[^)]*(?:token|key|secret|data|q|payload|p|prompt|ctx|context|info|msg|body|session|conv)=/i},{name:"markdown_image_exfil_long_value",pattern:/!\[.*?\]\(https?:\/\/[^)]+\?[^)]*=[^)&]{30,}/},{name:"tracking_pixel",pattern:/<img[^>]+src=["']https?:\/\/[^"']*\?[^"']*["'][^>]*(?:width|height)\s*=\s*["']?[01]px/i},{name:"encoded_url_exfil",pattern:/https?:\/\/[^\s]*(?:callback|webhook|exfil|collect)[^\s]*\?[^\s]*(?:data|payload|d)=/i},{name:"data_send_instruction",pattern:/send\s+(?:this|the|all)\s+(?:data|information|content|context)\s+to/i},{name:"fetch_url",pattern:/(?:fetch|request|call|curl|wget)\s+https?:\/\//i}],PII_PATTERNS=[{name:"ssn",pattern:/\b\d{3}-\d{2}-\d{4}\b/},{name:"credit_card",pattern:/\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b/},{name:"email_address",pattern:/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}\b/i}];class ExternalDataGuard{constructor(e={}){this.config={...e,maxContentLength:e.maxContentLength??5e4,scanForInjection:e.scanForInjection??!0,scanForSecrets:e.scanForSecrets??!0,scanForExfiltration:e.scanForExfiltration??!0,requireProvenance:e.requireProvenance??!1}}validate(e,s){const t=[],n=[],o=typeof e=="string"?e:this.safeStringify(e),i=s?.source;if(this.config.requireProvenance&&!s?.source&&(t.push("MISSING_PROVENANCE"),n.push("no_source_metadata")),i&&(this.isBlockedSource(i)&&(t.push("BLOCKED_SOURCE"),n.push("blocked_data_source")),this.config.allowedSources&&this.config.allowedSources.length>0&&(this.isAllowedSource(i)||(t.push("UNAPPROVED_SOURCE"),n.push("source_not_in_allowlist")))),o.length>this.config.maxContentLength&&(t.push("CONTENT_TOO_LARGE"),n.push("context_stuffing")),s?.retrievedAt&&s?.maxAgeSec){const r=typeof s.retrievedAt=="string"?new Date(s.retrievedAt).getTime():s.retrievedAt;Date.now()-r>s.maxAgeSec*1e3&&(t.push("STALE_DATA"),n.push("data_expired"))}if(this.config.scanForInjection)for(const{name:r,pattern:a}of INJECTION_PATTERNS)a.lastIndex=0,a.test(o)&&(t.push("INJECTION_DETECTED"),n.push(`injection:${r}`));if(this.config.scanForSecrets){for(const{name:r,pattern:a}of SECRET_PATTERNS)a.lastIndex=0,a.test(o)&&(t.push("SECRET_DETECTED"),n.push(`secret:${r}`));for(const{name:r,pattern:a}of PII_PATTERNS)a.lastIndex=0,a.test(o)&&(t.push("PII_DETECTED"),n.push(`pii:${r}`))}if(this.config.scanForExfiltration)for(const{name:r,pattern:a}of EXFILTRATION_PATTERNS)a.lastIndex=0,a.test(o)&&(t.push("EXFILTRATION_ATTEMPT"),n.push(`exfil:${r}`));const c=[...new Set(t)],u=[...new Set(n)],l=c.length===0,p={allowed:l,reason:l?void 0:`External data rejected: ${c.join(", ")}`,violations:c,source:i,contentLength:o.length,threats:u};return l||this.log(`Blocked external data: ${c.join(", ")}`,"warn"),p}validateBatch(e){const s=e.map(t=>this.validate(t.content,t.provenance));return{results:s,allAllowed:s.every(t=>t.allowed),totalThreats:s.reduce((t,n)=>t+n.threats.length,0)}}isBlockedSource(e){if(!this.config.blockedSources)return!1;const s=e.toLowerCase();return this.config.blockedSources.some(t=>s.includes(t.toLowerCase()))}isAllowedSource(e){if(!this.config.allowedSources)return!0;const s=e.toLowerCase();return this.config.allowedSources.some(t=>s.startsWith(t.toLowerCase()))}log(e,s){this.config.logger?.(e,s)}safeStringify(e){try{return JSON.stringify(e)}catch{return String(e)}}}exports.ExternalDataGuard=ExternalDataGuard;
@@ -19,6 +19,15 @@
19
19
  * - Tool shadowing detection
20
20
  * - Server reputation scoring
21
21
  * - Command injection prevention
22
+ *
23
+ * Upstream SDK advisory — cannot be mitigated at the detection layer:
24
+ * - CVE-2026-25536 (@modelcontextprotocol/sdk 1.10.0–1.25.3, CVSS 7.1):
25
+ * Cross-client response data leak when a single McpServer/Server and
26
+ * transport instance is reused across multiple client connections
27
+ * (common in stateless StreamableHTTPServerTransport deployments).
28
+ * Fix: upgrade @modelcontextprotocol/sdk to >=1.26.0. This guard cannot
29
+ * prevent the leak — it is a server-library bug — but tool-response
30
+ * session-binding violations caught here can surface related symptoms.
22
31
  */
23
32
  export interface MCPSecurityGuardConfig {
24
33
  /** Require server signature verification */
@@ -1 +1 @@
1
- "use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(m,e,t,n){n===void 0&&(n=t);var i=Object.getOwnPropertyDescriptor(e,t);(!i||("get"in i?!e.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(m,n,i)}):(function(m,e,t,n){n===void 0&&(n=t),m[n]=e[t]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(m,e){Object.defineProperty(m,"default",{enumerable:!0,value:e})}):function(m,e){m.default=e}),__importStar=this&&this.__importStar||(function(){var m=function(e){return m=Object.getOwnPropertyNames||function(t){var n=[];for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(n[n.length]=i);return n},m(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n=m(e),i=0;i<n.length;i++)n[i]!=="default"&&__createBinding(t,e,n[i]);return __setModuleDefault(t,e),t}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.RAGGuard=void 0;const crypto=__importStar(require("crypto"));class RAGGuard{constructor(e={}){this.contentHashCache=new Map,this.sourceReputationCache=new Map,this.RAG_INJECTION_PATTERNS=[{name:"instruction_override",pattern:/ignore\s+(all\s+)?previous\s+(context|documents|information)/i,severity:50},{name:"system_instruction",pattern:/\[SYSTEM\]|\[INSTRUCTION\]|\[OVERRIDE\]|<system>|<instruction>/i,severity:45},{name:"role_injection",pattern:/you\s+(are|must|should)\s+(now\s+)?(be|act\s+as|become)/i,severity:40},{name:"delimiter_break",pattern:/---\s*(end|stop)\s*(of\s*)?(context|document|rag)/i,severity:45},{name:"context_escape",pattern:/\]\]\]|\}\}\}|<<<|>>>|'''|"""/g,severity:30},{name:"hidden_instruction",pattern:/HIDDEN:|SECRET:|INVISIBLE:|DO_NOT_DISPLAY:/i,severity:50},{name:"admin_marker",pattern:/ADMIN_INSTRUCTION|ROOT_COMMAND|ELEVATED_PROMPT/i,severity:55},{name:"exfil_setup",pattern:/send\s+(all|this|data)\s+to|forward\s+to\s+https?:\/\//i,severity:50},{name:"callback_injection",pattern:/callback\s*[:=]\s*https?:\/\/|webhook\s*[:=]/i,severity:45},{name:"tool_injection",pattern:/call\s+(tool|function|action)\s*[:=]|execute\s*[:=]/i,severity:45},{name:"code_injection",pattern:/```(javascript|python|bash|sh)\s*\n[^`]*\b(eval|exec|system|subprocess)\b/i,severity:50},{name:"persona_override",pattern:/your\s+(new\s+)?(persona|identity|character)\s+(is|will\s+be)/i,severity:40},{name:"behavior_mod",pattern:/always\s+(respond|reply|answer)\s+with|never\s+(mention|reveal|disclose)/i,severity:35},{name:"prompt_extraction",pattern:/reveal\s+(your\s+)?(system\s+)?prompt|show\s+(me\s+)?(your\s+)?instructions/i,severity:40},{name:"debug_mode",pattern:/enable\s+debug|activate\s+developer\s+mode|enter\s+test\s+mode/i,severity:35}],this.SUSPICIOUS_METADATA_PATTERNS=[{name:"script_in_title",pattern:/<script|javascript:/i},{name:"injection_in_author",pattern:/admin|system|root|override/i},{name:"suspicious_content_type",pattern:/application\/x-|text\/x-/i}],this.MALICIOUS_SOURCE_PATTERNS=[/pastebin\.com/i,/hastebin\.com/i,/gist\.githubusercontent\.com.*injection/i,/raw\.githubusercontent\.com.*malicious/i,/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/],this.INDIRECT_INJECTION_PATTERNS=[{name:"html_comment_injection",pattern:/<!--[\s\S]*?(ignore|override|system|instruction|admin)[\s\S]*?-->/i,severity:45},{name:"markdown_hidden",pattern:/\[[\s\S]*?\]\(javascript:|data:text\/html|about:blank\)/i,severity:50},{name:"invisible_link",pattern:/\[]\([^)]+\)/g,severity:30},{name:"zero_width_chars",pattern:/[\u200B-\u200F\u2028-\u202F\uFEFF]{3,}/g,severity:40},{name:"rtl_override",pattern:/[\u202A-\u202E\u2066-\u2069]/g,severity:35},{name:"confusable_chars",pattern:/[\u0430\u0435\u043E\u0440\u0441\u0443\u0445]/g,severity:25},{name:"excessive_whitespace",pattern:/[\t\n\r]{10,}/g,severity:20},{name:"tab_encoding",pattern:/\t{5,}/g,severity:25},{name:"base64_block",pattern:/[A-Za-z0-9+/]{40,}={0,2}/g,severity:40},{name:"base64_with_context",pattern:/(?:encode|decode|base64|reference)[:\s]*[A-Za-z0-9+/]{20,}/i,severity:45},{name:"hex_encoded",pattern:/\\x[0-9a-fA-F]{2}(?:\\x[0-9a-fA-F]{2}){5,}/g,severity:35},{name:"unicode_escape",pattern:/\\u[0-9a-fA-F]{4}(?:\\u[0-9a-fA-F]{4}){3,}/g,severity:35},{name:"fake_boundary",pattern:/={5,}|#{5,}|-{10,}/g,severity:20},{name:"json_injection",pattern:/\{"(role|content|system)":/i,severity:45},{name:"xml_injection",pattern:/<\/?(?:prompt|assistant|user|system)>/i,severity:45}],this.config={detectInjections:e.detectInjections??!0,verifySource:e.verifySource??!0,trustedSources:e.trustedSources??[],blockedSources:e.blockedSources??[],maxDocumentSize:e.maxDocumentSize??5e4,minTrustScore:e.minTrustScore??30,enableContentHashing:e.enableContentHashing??!0,knownGoodHashes:e.knownGoodHashes??new Set,autoSanitize:e.autoSanitize??!0,detectEmbeddingAttacks:e.detectEmbeddingAttacks??!0,embeddingDimension:e.embeddingDimension??1536,detectSteganography:e.detectSteganography??!0,detectClusteringAnomalies:e.detectClusteringAnomalies??!0,embeddingMagnitudeRange:e.embeddingMagnitudeRange??[.8,1.2],similarityThreshold:e.similarityThreshold??.95,detectIndirectInjection:e.detectIndirectInjection??!0}}validate(e,t){const n=t||`rag-${Date.now()}`,i=[],s=[],r=[],c=[],h=[];let p=0,g=0,y=0,b=0,k=0,v=0,f=0;for(const u of e){let a=[],d=0,_=!1,R=!1;if(u.content.length>this.config.maxDocumentSize&&(a.push("oversized_document"),d+=20),this.config.verifySource){const o=this.verifyDocumentSource(u.source);o.trusted||(a.push(`untrusted_source: ${o.reason}`),r.push(u.source),d+=100-o.score,o.score<this.config.minTrustScore&&(_=!0)),b+=o.score}else b+=50;if(this.config.enableContentHashing){const o=this.hashContent(u.content);u.contentHash&&u.contentHash!==o&&(a.push("content_hash_mismatch"),d+=40,_=!0),this.config.knownGoodHashes.has(o)&&(d=Math.max(0,d-30))}if(this.config.detectInjections){const o=this.detectInjections(u.content);o.found&&(p+=o.patterns.length,a.push(...o.violations),d+=o.riskContribution,R=!0,o.riskContribution>=50&&(_=!0))}if(u.metadata){const o=this.checkMetadata(u.metadata);o.suspicious&&(a.push(...o.violations),d+=o.riskContribution)}if(u.embedding&&(u.embedding.some(l=>l==null||typeof l!="number"||!isFinite(l)||isNaN(l))&&(a.push("embedding_contains_invalid_values"),d+=50,_=!0),u.retrievalScore!==void 0)){const l=this.checkEmbedding(u.embedding,u.retrievalScore);l.anomalous&&(a.push(`embedding_anomaly: ${l.reason}`),d+=35,l.shouldBlock&&(_=!0))}if(this.config.detectEmbeddingAttacks&&u.embedding){const o=this.detectEmbeddingAttacks(u.embedding,u.retrievalScore);o.detected&&(k++,h.push(o),a.push(...o.attack_type.map(l=>`embedding_attack: ${l}`)),d+=o.risk_score,o.risk_score>=40&&(_=!0))}if(this.config.detectIndirectInjection){const o=this.detectIndirectInjection(u.content);o.found&&(f+=o.patterns.length,a.push(...o.violations),d+=o.riskContribution,R=!0,o.riskContribution>=40&&(_=!0))}if(this.config.detectSteganography){const o=this.detectSteganography(u.content);o.found&&(v++,a.push(...o.violations),d+=o.riskContribution,R=!0)}if(_||d>=70)s.push(u.id),g++,i.push(...a.map(o=>`[${u.id}] ${o}`));else if(R&&this.config.autoSanitize){const o=this.sanitizeDocument(u);c.push(o),y++,i.push(...a.map(l=>`[${u.id}] ${l} (sanitized)`))}else c.push(u),a.length>0&&i.push(...a.map(o=>`[${u.id}] ${o} (allowed)`))}const C=e.length>0?b/e.length:0,S=g===e.length||C<this.config.minTrustScore;return{allowed:!S,reason:S?`RAG content blocked: ${g}/${e.length} documents failed validation`:"RAG content validated",violations:i,request_id:n,document_analysis:{documents_checked:e.length,documents_blocked:g,documents_sanitized:y,injection_attempts:p,untrusted_sources:[...new Set(r)],average_trust_score:Math.round(C),embedding_attacks_detected:k,steganography_detected:v,indirect_injection_attempts:f},sanitized_documents:S?void 0:c,blocked_document_ids:s,recommendations:this.generateRecommendations(i,r.length>0),embedding_analysis:h.length>0?h:void 0}}validateSingle(e,t){return this.validate([e],t)}verifyDocumentSource(e){const t=this.sourceReputationCache.get(e);if(t!==void 0)return{trusted:t>=this.config.minTrustScore,score:t,reason:t>=this.config.minTrustScore?"Cached trusted source":"Cached untrusted source"};let n=50,i="Unknown source";for(const s of this.config.blockedSources)if(e.includes(s)||new RegExp(s,"i").test(e))return this.sourceReputationCache.set(e,0),{trusted:!1,score:0,reason:"Blocked source"};for(const s of this.MALICIOUS_SOURCE_PATTERNS)if(s.test(e))return this.sourceReputationCache.set(e,10),{trusted:!1,score:10,reason:"Matches malicious source pattern"};for(const s of this.config.trustedSources)if(e.includes(s)||new RegExp(s,"i").test(e))return this.sourceReputationCache.set(e,90),{trusted:!0,score:90,reason:"Trusted source"};try{const s=new URL(e);s.protocol==="https:"&&(n+=15,i="HTTPS source");const r=[".gov",".edu",".org","wikipedia.org","microsoft.com","google.com"];for(const c of r)if(s.hostname.endsWith(c)){n+=20,i=`Trusted domain: ${c}`;break}(s.pathname.includes("..")||s.search.includes("<"))&&(n-=30,i="Suspicious URL pattern")}catch{(e.startsWith("/")||e.match(/^[A-Z]:\\/))&&(n=60,i="Local file path")}return this.sourceReputationCache.set(e,n),{trusted:n>=this.config.minTrustScore,score:n,reason:i}}addTrustedSource(e){this.config.trustedSources.includes(e)||this.config.trustedSources.push(e),this.sourceReputationCache.set(e,90)}addBlockedSource(e){this.config.blockedSources.includes(e)||this.config.blockedSources.push(e),this.sourceReputationCache.set(e,0)}registerKnownGoodHash(e){const t=this.hashContent(e);return this.config.knownGoodHashes.add(t),t}clearSourceCache(){this.sourceReputationCache.clear()}detectInjections(e){const t=[],n=[];let i=0;for(const{name:c,pattern:h,severity:p}of this.RAG_INJECTION_PATTERNS)e.match(h)&&(t.push(c),n.push(`injection_${c}`),i+=p);(e.match(/[^\w\s]/g)||[]).length/e.length>.3&&(t.push("high_special_char_ratio"),n.push("possible_obfuscation"),i+=15);const r=e.match(/[\u200B-\u200D\uFEFF\u2060-\u206F]/g);return r&&r.length>5&&(t.push("invisible_unicode"),n.push("hidden_characters"),i+=20),{found:t.length>0,patterns:t,violations:n,riskContribution:Math.min(100,i)}}checkMetadata(e){const t=[];let n=0;const i=JSON.stringify(e);for(const{name:s,pattern:r}of this.SUSPICIOUS_METADATA_PATTERNS)r.test(i)&&(t.push(`metadata_${s}`),n+=15);for(const{name:s,pattern:r,severity:c}of this.RAG_INJECTION_PATTERNS.slice(0,5))r.test(i)&&(t.push(`metadata_injection_${s}`),n+=c/2);return{suspicious:t.length>0,violations:t,riskContribution:Math.min(50,n)}}checkEmbedding(e,t){if(e.some(s=>s==null||typeof s!="number"||!isFinite(s)))return{anomalous:!0,reason:"Invalid embedding values (NaN/Infinity/null)",shouldBlock:!0};if(new Set(e.map(s=>Math.round(s*100)/100)).size<e.length*.1)return{anomalous:!0,reason:"Suspiciously uniform embedding",shouldBlock:!0};const i=Math.sqrt(e.reduce((s,r)=>s+r*r,0));return t>.9&&i<.1?{anomalous:!0,reason:"Score/embedding mismatch"}:{anomalous:!1}}sanitizeDocument(e){let t=e.content;for(const{pattern:n}of this.RAG_INJECTION_PATTERNS)t=t.replace(n,"[REDACTED]");return t=t.replace(/[\u200B-\u200D\uFEFF\u2060-\u206F]/g,""),t=t.replace(/(\[{3,}|\]{3,}|\{{3,}|\}{3,}|<{3,}|>{3,})/g,""),{...e,content:t,metadata:{...e.metadata,_sanitized:!0,_originalLength:e.content.length,_sanitizedLength:t.length}}}hashContent(e){return crypto.createHash("sha256").update(e).digest("hex")}generateRecommendations(e,t){const n=[];return t&&n.push("Review and whitelist trusted document sources"),e.some(i=>i.includes("injection"))&&n.push("Implement document sanitization in your RAG pipeline"),e.some(i=>i.includes("hash"))&&n.push("Enable content integrity verification with known good hashes"),e.some(i=>i.includes("oversized"))&&n.push("Implement document chunking with size limits"),e.some(i=>i.includes("embedding"))&&n.push("Add embedding validation to your vector store pipeline"),n.length===0&&n.push("Continue monitoring RAG document sources"),n}detectEmbeddingAttacks(e,t){const n=[],i={};let s=0;e.length!==this.config.embeddingDimension&&(n.push("dimension_mismatch"),s+=20);const r=Math.sqrt(e.reduce((a,d)=>a+d*d,0)),[c,h]=this.config.embeddingMagnitudeRange;(r<c||r>h)&&(n.push("magnitude_anomaly"),i.magnitude_anomaly=!0,s+=25);const p=e.map(Math.abs),y=[...p].sort((a,d)=>d-a).slice(0,10),b=y.reduce((a,d)=>a+d,0)/y.length,k=p.reduce((a,d)=>a+d,0)/p.length;b>k*10&&(n.push("adversarial_perturbation"),i.adversarial_perturbation=!0,s+=35);const v=Math.min(50,Math.floor(e.length/10)),f=[];for(let a=0;a<e.length-v;a+=v)f.push(e.slice(a,a+v));if(f.length>=2){for(let a=0;a<f.length-1;a++)if(this.cosineSimilarity(f[a],f[a+1])>this.config.similarityThreshold){n.push("backdoor_pattern"),i.backdoor_pattern=!0,s+=40;break}}const C=e.reduce((a,d)=>a+d,0)/e.length,S=e.reduce((a,d)=>a+Math.pow(d-C,2),0)/e.length,u=Math.sqrt(S);return(u<.001||u>2)&&(n.push("distribution_anomaly"),i.distribution_anomaly=!0,s+=20),t&&t>.95&&s>20&&(n.push("suspicious_high_score"),s+=15),{detected:n.length>0,attack_type:n,risk_score:Math.min(100,s),details:i}}detectIndirectInjection(e){const t=[],n=[];let i=0;for(const{name:s,pattern:r,severity:c}of this.INDIRECT_INJECTION_PATTERNS)e.match(r)&&(t.push(s),n.push(`indirect_injection_${s}`),i+=c);return{found:t.length>0,patterns:t,violations:n,riskContribution:Math.min(100,i)}}detectSteganography(e){const t=[];let n=0;const i=e.match(/[\u200B-\u200F\u2028-\u202F\uFEFF]+/g);if(i){const g=i.reduce((y,b)=>y+b.length,0);g>=3&&(t.push("zero_width_steganography"),n+=40+Math.min(30,g*5))}/\s{4,}\t+\s+|\t{2,}\s+\t/.test(e)&&(t.push("whitespace_encoding"),n+=35),(e.match(/[\t\n\r ]/g)||[]).length/e.length>.35&&(t.push("excessive_whitespace_ratio"),n+=25);const c=e.match(/[\uDB40][\uDC00-\uDC7F]/g);c&&c.length>0&&(t.push("unicode_tag_steganography"),n+=40);const h=e.match(/[\uFE00-\uFE0F]/g);return h&&h.length>5&&(t.push("variation_selector_abuse"),n+=25),e.match(/[01]{16,}/g)&&(t.push("binary_steganography"),n+=30),{found:t.length>0,violations:t,riskContribution:Math.min(100,n)}}cosineSimilarity(e,t){if(e.length!==t.length)return 0;const n=e.reduce((r,c,h)=>r+c*t[h],0),i=Math.sqrt(e.reduce((r,c)=>r+c*c,0)),s=Math.sqrt(t.reduce((r,c)=>r+c*c,0));return i===0||s===0?0:n/(i*s)}analyzeEmbeddingCluster(e){if(e.length<3)return{anomalous:!1,anomalousIndices:[],reason:"Not enough embeddings for cluster analysis"};const t=[],n=[];for(let i=0;i<e.length;i++){n[i]=[];for(let s=0;s<e.length;s++)i===s?n[i][s]=1:n[i][s]=this.cosineSimilarity(e[i],e[s])}for(let i=0;i<e.length;i++){const s=n[i].reduce((r,c)=>r+c,0)/e.length;s>this.config.similarityThreshold&&t.push(i),s<.3&&t.push(i)}return{anomalous:t.length>0,anomalousIndices:[...new Set(t)],reason:t.length>0?`${t.length} embeddings show clustering anomalies`:"No clustering anomalies detected"}}}exports.RAGGuard=RAGGuard;
1
+ "use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(m,e,t,n){n===void 0&&(n=t);var i=Object.getOwnPropertyDescriptor(e,t);(!i||("get"in i?!e.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(m,n,i)}):(function(m,e,t,n){n===void 0&&(n=t),m[n]=e[t]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(m,e){Object.defineProperty(m,"default",{enumerable:!0,value:e})}):function(m,e){m.default=e}),__importStar=this&&this.__importStar||(function(){var m=function(e){return m=Object.getOwnPropertyNames||function(t){var n=[];for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(n[n.length]=i);return n},m(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n=m(e),i=0;i<n.length;i++)n[i]!=="default"&&__createBinding(t,e,n[i]);return __setModuleDefault(t,e),t}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.RAGGuard=void 0;const crypto=__importStar(require("crypto"));class RAGGuard{constructor(e={}){this.contentHashCache=new Map,this.sourceReputationCache=new Map,this.RAG_INJECTION_PATTERNS=[{name:"instruction_override",pattern:/ignore\s+(all\s+)?previous\s+(context|documents|information)/i,severity:50},{name:"system_instruction",pattern:/\[SYSTEM\]|\[INSTRUCTION\]|\[OVERRIDE\]|<system>|<instruction>/i,severity:45},{name:"role_injection",pattern:/you\s+(are|must|should)\s+(now\s+)?(be|act\s+as|become)/i,severity:40},{name:"delimiter_break",pattern:/---\s*(end|stop)\s*(of\s*)?(context|document|rag)/i,severity:45},{name:"context_escape",pattern:/\]\]\]|\}\}\}|<<<|>>>|'''|"""/g,severity:30},{name:"hidden_instruction",pattern:/HIDDEN:|SECRET:|INVISIBLE:|DO_NOT_DISPLAY:/i,severity:50},{name:"admin_marker",pattern:/ADMIN_INSTRUCTION|ROOT_COMMAND|ELEVATED_PROMPT/i,severity:55},{name:"exfil_setup",pattern:/send\s+(all|this|data)\s+to|forward\s+to\s+https?:\/\//i,severity:50},{name:"callback_injection",pattern:/callback\s*[:=]\s*https?:\/\/|webhook\s*[:=]/i,severity:45},{name:"tool_injection",pattern:/call\s+(tool|function|action)\s*[:=]|execute\s*[:=]/i,severity:45},{name:"code_injection",pattern:/```(javascript|python|bash|sh)\s*\n[^`]*\b(eval|exec|system|subprocess)\b/i,severity:50},{name:"persona_override",pattern:/your\s+(new\s+)?(persona|identity|character)\s+(is|will\s+be)/i,severity:40},{name:"behavior_mod",pattern:/always\s+(respond|reply|answer)\s+with|never\s+(mention|reveal|disclose)/i,severity:35},{name:"prompt_extraction",pattern:/reveal\s+(your\s+)?(system\s+)?prompt|show\s+(me\s+)?(your\s+)?instructions/i,severity:40},{name:"debug_mode",pattern:/enable\s+debug|activate\s+developer\s+mode|enter\s+test\s+mode/i,severity:35}],this.SUSPICIOUS_METADATA_PATTERNS=[{name:"script_in_title",pattern:/<script|javascript:/i},{name:"injection_in_author",pattern:/admin|system|root|override/i},{name:"suspicious_content_type",pattern:/application\/x-|text\/x-/i}],this.MALICIOUS_SOURCE_PATTERNS=[/pastebin\.com/i,/hastebin\.com/i,/gist\.githubusercontent\.com.*injection/i,/raw\.githubusercontent\.com.*malicious/i,/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/],this.INDIRECT_INJECTION_PATTERNS=[{name:"html_comment_injection",pattern:/<!--[\s\S]*?(ignore|override|system|instruction|admin)[\s\S]*?-->/i,severity:45},{name:"markdown_hidden",pattern:/\[[\s\S]*?\]\(javascript:|data:text\/html|about:blank\)/i,severity:50},{name:"invisible_link",pattern:/\[]\([^)]+\)/g,severity:30},{name:"zero_width_chars",pattern:/[\u200B-\u200F\u2028-\u202F\uFEFF]{3,}/g,severity:40},{name:"rtl_override",pattern:/[\u202A-\u202E\u2066-\u2069]/g,severity:35},{name:"confusable_chars",pattern:/[\u0430\u0435\u043E\u0440\u0441\u0443\u0445]/g,severity:25},{name:"excessive_whitespace",pattern:/[\t\n\r]{10,}/g,severity:20},{name:"tab_encoding",pattern:/\t{5,}/g,severity:25},{name:"base64_block",pattern:/[A-Za-z0-9+/]{40,}={0,2}/g,severity:40},{name:"base64_with_context",pattern:/(?:encode|decode|base64|reference)[:\s]*[A-Za-z0-9+/]{20,}/i,severity:45},{name:"hex_encoded",pattern:/\\x[0-9a-fA-F]{2}(?:\\x[0-9a-fA-F]{2}){5,}/g,severity:35},{name:"unicode_escape",pattern:/\\u[0-9a-fA-F]{4}(?:\\u[0-9a-fA-F]{4}){3,}/g,severity:35},{name:"fake_boundary",pattern:/={5,}|#{5,}|-{10,}/g,severity:20},{name:"json_injection",pattern:/\{"(role|content|system)":/i,severity:45},{name:"xml_injection",pattern:/<\/?(?:prompt|assistant|user|system)>/i,severity:45},{name:"css_hidden_text",pattern:/style\s*=\s*["'][^"']*(?:display\s*:\s*none|visibility\s*:\s*hidden|opacity\s*:\s*0(?:\.0+)?|font-size\s*:\s*0)[^"']*["']/i,severity:45},{name:"html_attr_directive",pattern:/(?:\balt|\btitle|\baria-label|\bdata-[a-z][a-z0-9-]*)\s*=\s*["'][^"']*(?:ignore\s+(?:all\s+)?(?:previous|prior|above)|system\s+prompt|new\s+instructions?|you\s+are\s+now|admin\s+mode|jailbreak)[^"']*["']/i,severity:50},{name:"json_agent_directive",pattern:/"(_system|__override|_agent_instructions?|__system_prompt__|_assistant_role|__internal_directive|_meta_instruction)"\s*:/i,severity:50}],this.config={detectInjections:e.detectInjections??!0,verifySource:e.verifySource??!0,trustedSources:e.trustedSources??[],blockedSources:e.blockedSources??[],maxDocumentSize:e.maxDocumentSize??5e4,minTrustScore:e.minTrustScore??30,enableContentHashing:e.enableContentHashing??!0,knownGoodHashes:e.knownGoodHashes??new Set,autoSanitize:e.autoSanitize??!0,detectEmbeddingAttacks:e.detectEmbeddingAttacks??!0,embeddingDimension:e.embeddingDimension??1536,detectSteganography:e.detectSteganography??!0,detectClusteringAnomalies:e.detectClusteringAnomalies??!0,embeddingMagnitudeRange:e.embeddingMagnitudeRange??[.8,1.2],similarityThreshold:e.similarityThreshold??.95,detectIndirectInjection:e.detectIndirectInjection??!0}}validate(e,t){const n=t||`rag-${Date.now()}`,i=[],s=[],r=[],c=[],h=[];let p=0,g=0,y=0,b=0,k=0,v=0,f=0;for(const u of e){let o=[],d=0,_=!1,R=!1;if(u.content.length>this.config.maxDocumentSize&&(o.push("oversized_document"),d+=20),this.config.verifySource){const a=this.verifyDocumentSource(u.source);a.trusted||(o.push(`untrusted_source: ${a.reason}`),r.push(u.source),d+=100-a.score,a.score<this.config.minTrustScore&&(_=!0)),b+=a.score}else b+=50;if(this.config.enableContentHashing){const a=this.hashContent(u.content);u.contentHash&&u.contentHash!==a&&(o.push("content_hash_mismatch"),d+=40,_=!0),this.config.knownGoodHashes.has(a)&&(d=Math.max(0,d-30))}if(this.config.detectInjections){const a=this.detectInjections(u.content);a.found&&(p+=a.patterns.length,o.push(...a.violations),d+=a.riskContribution,R=!0,a.riskContribution>=50&&(_=!0))}if(u.metadata){const a=this.checkMetadata(u.metadata);a.suspicious&&(o.push(...a.violations),d+=a.riskContribution)}if(u.embedding&&(u.embedding.some(l=>l==null||typeof l!="number"||!isFinite(l)||isNaN(l))&&(o.push("embedding_contains_invalid_values"),d+=50,_=!0),u.retrievalScore!==void 0)){const l=this.checkEmbedding(u.embedding,u.retrievalScore);l.anomalous&&(o.push(`embedding_anomaly: ${l.reason}`),d+=35,l.shouldBlock&&(_=!0))}if(this.config.detectEmbeddingAttacks&&u.embedding){const a=this.detectEmbeddingAttacks(u.embedding,u.retrievalScore);a.detected&&(k++,h.push(a),o.push(...a.attack_type.map(l=>`embedding_attack: ${l}`)),d+=a.risk_score,a.risk_score>=40&&(_=!0))}if(this.config.detectIndirectInjection){const a=this.detectIndirectInjection(u.content);a.found&&(f+=a.patterns.length,o.push(...a.violations),d+=a.riskContribution,R=!0,a.riskContribution>=40&&(_=!0))}if(this.config.detectSteganography){const a=this.detectSteganography(u.content);a.found&&(v++,o.push(...a.violations),d+=a.riskContribution,R=!0)}if(_||d>=70)s.push(u.id),g++,i.push(...o.map(a=>`[${u.id}] ${a}`));else if(R&&this.config.autoSanitize){const a=this.sanitizeDocument(u);c.push(a),y++,i.push(...o.map(l=>`[${u.id}] ${l} (sanitized)`))}else c.push(u),o.length>0&&i.push(...o.map(a=>`[${u.id}] ${a} (allowed)`))}const C=e.length>0?b/e.length:0,S=g===e.length||C<this.config.minTrustScore;return{allowed:!S,reason:S?`RAG content blocked: ${g}/${e.length} documents failed validation`:"RAG content validated",violations:i,request_id:n,document_analysis:{documents_checked:e.length,documents_blocked:g,documents_sanitized:y,injection_attempts:p,untrusted_sources:[...new Set(r)],average_trust_score:Math.round(C),embedding_attacks_detected:k,steganography_detected:v,indirect_injection_attempts:f},sanitized_documents:S?void 0:c,blocked_document_ids:s,recommendations:this.generateRecommendations(i,r.length>0),embedding_analysis:h.length>0?h:void 0}}validateSingle(e,t){return this.validate([e],t)}verifyDocumentSource(e){const t=this.sourceReputationCache.get(e);if(t!==void 0)return{trusted:t>=this.config.minTrustScore,score:t,reason:t>=this.config.minTrustScore?"Cached trusted source":"Cached untrusted source"};let n=50,i="Unknown source";for(const s of this.config.blockedSources)if(e.includes(s)||new RegExp(s,"i").test(e))return this.sourceReputationCache.set(e,0),{trusted:!1,score:0,reason:"Blocked source"};for(const s of this.MALICIOUS_SOURCE_PATTERNS)if(s.test(e))return this.sourceReputationCache.set(e,10),{trusted:!1,score:10,reason:"Matches malicious source pattern"};for(const s of this.config.trustedSources)if(e.includes(s)||new RegExp(s,"i").test(e))return this.sourceReputationCache.set(e,90),{trusted:!0,score:90,reason:"Trusted source"};try{const s=new URL(e);s.protocol==="https:"&&(n+=15,i="HTTPS source");const r=[".gov",".edu",".org","wikipedia.org","microsoft.com","google.com"];for(const c of r)if(s.hostname.endsWith(c)){n+=20,i=`Trusted domain: ${c}`;break}(s.pathname.includes("..")||s.search.includes("<"))&&(n-=30,i="Suspicious URL pattern")}catch{(e.startsWith("/")||e.match(/^[A-Z]:\\/))&&(n=60,i="Local file path")}return this.sourceReputationCache.set(e,n),{trusted:n>=this.config.minTrustScore,score:n,reason:i}}addTrustedSource(e){this.config.trustedSources.includes(e)||this.config.trustedSources.push(e),this.sourceReputationCache.set(e,90)}addBlockedSource(e){this.config.blockedSources.includes(e)||this.config.blockedSources.push(e),this.sourceReputationCache.set(e,0)}registerKnownGoodHash(e){const t=this.hashContent(e);return this.config.knownGoodHashes.add(t),t}clearSourceCache(){this.sourceReputationCache.clear()}detectInjections(e){const t=[],n=[];let i=0;for(const{name:c,pattern:h,severity:p}of this.RAG_INJECTION_PATTERNS)e.match(h)&&(t.push(c),n.push(`injection_${c}`),i+=p);(e.match(/[^\w\s]/g)||[]).length/e.length>.3&&(t.push("high_special_char_ratio"),n.push("possible_obfuscation"),i+=15);const r=e.match(/[\u200B-\u200D\uFEFF\u2060-\u206F]/g);return r&&r.length>5&&(t.push("invisible_unicode"),n.push("hidden_characters"),i+=20),{found:t.length>0,patterns:t,violations:n,riskContribution:Math.min(100,i)}}checkMetadata(e){const t=[];let n=0;const i=JSON.stringify(e);for(const{name:s,pattern:r}of this.SUSPICIOUS_METADATA_PATTERNS)r.test(i)&&(t.push(`metadata_${s}`),n+=15);for(const{name:s,pattern:r,severity:c}of this.RAG_INJECTION_PATTERNS.slice(0,5))r.test(i)&&(t.push(`metadata_injection_${s}`),n+=c/2);return{suspicious:t.length>0,violations:t,riskContribution:Math.min(50,n)}}checkEmbedding(e,t){if(e.some(s=>s==null||typeof s!="number"||!isFinite(s)))return{anomalous:!0,reason:"Invalid embedding values (NaN/Infinity/null)",shouldBlock:!0};if(new Set(e.map(s=>Math.round(s*100)/100)).size<e.length*.1)return{anomalous:!0,reason:"Suspiciously uniform embedding",shouldBlock:!0};const i=Math.sqrt(e.reduce((s,r)=>s+r*r,0));return t>.9&&i<.1?{anomalous:!0,reason:"Score/embedding mismatch"}:{anomalous:!1}}sanitizeDocument(e){let t=e.content;for(const{pattern:n}of this.RAG_INJECTION_PATTERNS)t=t.replace(n,"[REDACTED]");return t=t.replace(/[\u200B-\u200D\uFEFF\u2060-\u206F]/g,""),t=t.replace(/(\[{3,}|\]{3,}|\{{3,}|\}{3,}|<{3,}|>{3,})/g,""),{...e,content:t,metadata:{...e.metadata,_sanitized:!0,_originalLength:e.content.length,_sanitizedLength:t.length}}}hashContent(e){return crypto.createHash("sha256").update(e).digest("hex")}generateRecommendations(e,t){const n=[];return t&&n.push("Review and whitelist trusted document sources"),e.some(i=>i.includes("injection"))&&n.push("Implement document sanitization in your RAG pipeline"),e.some(i=>i.includes("hash"))&&n.push("Enable content integrity verification with known good hashes"),e.some(i=>i.includes("oversized"))&&n.push("Implement document chunking with size limits"),e.some(i=>i.includes("embedding"))&&n.push("Add embedding validation to your vector store pipeline"),n.length===0&&n.push("Continue monitoring RAG document sources"),n}detectEmbeddingAttacks(e,t){const n=[],i={};let s=0;e.length!==this.config.embeddingDimension&&(n.push("dimension_mismatch"),s+=20);const r=Math.sqrt(e.reduce((o,d)=>o+d*d,0)),[c,h]=this.config.embeddingMagnitudeRange;(r<c||r>h)&&(n.push("magnitude_anomaly"),i.magnitude_anomaly=!0,s+=25);const p=e.map(Math.abs),y=[...p].sort((o,d)=>d-o).slice(0,10),b=y.reduce((o,d)=>o+d,0)/y.length,k=p.reduce((o,d)=>o+d,0)/p.length;b>k*10&&(n.push("adversarial_perturbation"),i.adversarial_perturbation=!0,s+=35);const v=Math.min(50,Math.floor(e.length/10)),f=[];for(let o=0;o<e.length-v;o+=v)f.push(e.slice(o,o+v));if(f.length>=2){for(let o=0;o<f.length-1;o++)if(this.cosineSimilarity(f[o],f[o+1])>this.config.similarityThreshold){n.push("backdoor_pattern"),i.backdoor_pattern=!0,s+=40;break}}const C=e.reduce((o,d)=>o+d,0)/e.length,S=e.reduce((o,d)=>o+Math.pow(d-C,2),0)/e.length,u=Math.sqrt(S);return(u<.001||u>2)&&(n.push("distribution_anomaly"),i.distribution_anomaly=!0,s+=20),t&&t>.95&&s>20&&(n.push("suspicious_high_score"),s+=15),{detected:n.length>0,attack_type:n,risk_score:Math.min(100,s),details:i}}detectIndirectInjection(e){const t=[],n=[];let i=0;for(const{name:s,pattern:r,severity:c}of this.INDIRECT_INJECTION_PATTERNS)e.match(r)&&(t.push(s),n.push(`indirect_injection_${s}`),i+=c);return{found:t.length>0,patterns:t,violations:n,riskContribution:Math.min(100,i)}}detectSteganography(e){const t=[];let n=0;const i=e.match(/[\u200B-\u200F\u2028-\u202F\uFEFF]+/g);if(i){const g=i.reduce((y,b)=>y+b.length,0);g>=3&&(t.push("zero_width_steganography"),n+=40+Math.min(30,g*5))}/\s{4,}\t+\s+|\t{2,}\s+\t/.test(e)&&(t.push("whitespace_encoding"),n+=35),(e.match(/[\t\n\r ]/g)||[]).length/e.length>.35&&(t.push("excessive_whitespace_ratio"),n+=25);const c=e.match(/[\uDB40][\uDC00-\uDC7F]/g);c&&c.length>0&&(t.push("unicode_tag_steganography"),n+=40);const h=e.match(/[\uFE00-\uFE0F]/g);return h&&h.length>5&&(t.push("variation_selector_abuse"),n+=25),e.match(/[01]{16,}/g)&&(t.push("binary_steganography"),n+=30),{found:t.length>0,violations:t,riskContribution:Math.min(100,n)}}cosineSimilarity(e,t){if(e.length!==t.length)return 0;const n=e.reduce((r,c,h)=>r+c*t[h],0),i=Math.sqrt(e.reduce((r,c)=>r+c*c,0)),s=Math.sqrt(t.reduce((r,c)=>r+c*c,0));return i===0||s===0?0:n/(i*s)}analyzeEmbeddingCluster(e){if(e.length<3)return{anomalous:!1,anomalousIndices:[],reason:"Not enough embeddings for cluster analysis"};const t=[],n=[];for(let i=0;i<e.length;i++){n[i]=[];for(let s=0;s<e.length;s++)i===s?n[i][s]=1:n[i][s]=this.cosineSimilarity(e[i],e[s])}for(let i=0;i<e.length;i++){const s=n[i].reduce((r,c)=>r+c,0)/e.length;s>this.config.similarityThreshold&&t.push(i),s<.3&&t.push(i)}return{anomalous:t.length>0,anomalousIndices:[...new Set(t)],reason:t.length>0?`${t.length} embeddings show clustering anomalies`:"No clustering anomalies detected"}}}exports.RAGGuard=RAGGuard;