llm-trust-guard 4.18.0 → 4.19.0

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,55 @@ 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.0] - 2026-04-23
9
+
10
+ ### Added — Indirect Injection Expansion
11
+
12
+ 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):
13
+
14
+ - **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
15
+ - **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
16
+ - **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
17
+
18
+ ### Added — "Reprompt"-Class Markdown Image Exfiltration
19
+
20
+ Addresses the CVE-2026-24307 Copilot Personal "Reprompt" exfiltration pattern (Varonis, disclosed 2025-08-31, patched by Microsoft 2026-01-13):
21
+
22
+ - **`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
23
+ - **Widened `markdown_image_exfil`** named-key list: added `p`, `prompt`, `ctx`, `context`, `info`, `msg`, `body`, `session`, `conv` (was `token|key|secret|data|q|payload`)
24
+
25
+ ### Documented — CVE-2026-25536 SDK Advisory
26
+
27
+ 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.
28
+
29
+ ### Tests
30
+
31
+ - +7 RAGGuard tests (CSS-hidden display:none, opacity:0, alt-attr directive, aria-label directive, JSON `_system`, JSON `__override`, legitimate-style false-positive check)
32
+ - +3 ExternalDataGuard tests (Reprompt-style long-value exfil, new named-key variant, legitimate cache-buster FP check)
33
+ - **All 705 tests pass** (was 695), zero regressions
34
+
35
+ ### Stats
36
+ - 34 guards, 705 tests, zero dependencies (unchanged)
37
+
38
+ ## [4.18.1] - 2026-04-20
39
+
40
+ ### Fixed — Metadata and README Accuracy
41
+
42
+ - **`package.json` description**: said "22 protection layers" but actual count is 34 guards. Fixed
43
+ - **README guard count**: "All 31 Guards" heading and "same 31 guards" link description were stale after v4.14.0 added three multi-agent guards. Bumped to 34
44
+ - **README multi-agent table**: SpawnPolicyGuard, DelegationScopeGuard, TrustTransitivityGuard were added to `src/guards/` in v4.14.0 but never listed in the README guard table. Added under new "Multi-Agent Guards (OWASP ASI07)" section
45
+ - **`heuristic-analyzer.ts`**: removed two `as any` casts by introducing `SynonymCategory` union type and `SynonymFeatureKey` template literal type. Runtime behavior unchanged; typecheck strengthened
46
+
47
+ ### Changed — Package Hygiene
48
+
49
+ - Added `publishConfig.provenance: true` for npm provenance attestation
50
+ - Bumped `typescript` devDep to `^5.7.0` (was `^5.3.2`)
51
+ - Bumped `@types/node` devDep to `^22.10.0` (was `^20.10.0`)
52
+ - `vitest`/`@vitest/coverage-v8` intentionally kept at `^1.6.0` — the jump to 3.x has breaking config/snapshot changes and deserves its own release cycle
53
+
54
+ ### Stats
55
+ - 34 guards, 695 tests, <5ms latency, zero dependencies (unchanged)
56
+
8
57
  ## [4.18.0] - 2026-04-10
9
58
 
10
59
  ### Removed — TF-IDF Built-in Classifier
package/README.md CHANGED
@@ -122,7 +122,7 @@ const toolResult = guard.validateToolResult('search', toolOutput);
122
122
  const output = guard.filterOutput(llmResponse, session.role);
123
123
  ```
124
124
 
125
- ## All 31 Guards
125
+ ## All 34 Guards
126
126
 
127
127
  ### Input Guards (before LLM)
128
128
 
@@ -175,6 +175,14 @@ const output = guard.filterOutput(llmResponse, session.role);
175
175
  | AgentSkillGuard | Malicious plugin/tool detection (OpenClaw) | Backdoor signatures + typosquatting |
176
176
  | SessionIntegrityGuard | Session hijacking, permission escalation | Binding + sequence + timeout |
177
177
 
178
+ ### Multi-Agent Guards (OWASP ASI07)
179
+
180
+ | Guard | Purpose | Detection |
181
+ |-------|---------|-----------|
182
+ | SpawnPolicyGuard | Agent spawn policy enforcement | CSP-style allowlists, max delegation depth |
183
+ | DelegationScopeGuard | Agent-to-agent scope downscoping | OAuth-style parent-child scope subset |
184
+ | TrustTransitivityGuard | Trust chain validation | X.509-style chain depth + min trust score |
185
+
178
186
  ### Pluggable Detection
179
187
 
180
188
  | Component | Purpose |
@@ -248,7 +256,7 @@ MIT
248
256
 
249
257
  ## Links
250
258
 
251
- - [Python package (PyPI)](https://pypi.org/project/llm-trust-guard/) — same 31 guards, zero dependencies
259
+ - [Python package (PyPI)](https://pypi.org/project/llm-trust-guard/) — same 34 guards, zero dependencies
252
260
  - [OWASP Top 10 for LLMs 2025](https://genai.owasp.org/resource/owasp-top-10-for-llm-applications-2025/)
253
261
  - [OWASP Top 10 for Agentic Applications 2026](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/)
254
262
  - [MITRE ATLAS](https://atlas.mitre.org/)
@@ -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;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.HeuristicAnalyzer=void 0;const SYNONYM_SETS={ignore:{keywords:new Set(["ignore","disregard","overlook","neglect","skip","bypass","omit","dismiss","override","overrule","forget","abandon","drop","ditch","scrap","discard","set aside","pay no attention","turn a blind eye","look past","brush off","shrug off","wave away","cast aside","put aside","leave behind","throw out","toss out"]),weight:.3},urgent:{keywords:new Set(["urgent","immediate","immediately","asap","emergency","critical","time-sensitive","pressing","crucial","dire","life-threatening","now","hurry","rush","quick","fast","right away","without delay","time is running out","deadline"]),weight:.15},incentive:{keywords:new Set(["excellent","fantastic","nice","awesome","brilliant","great","amazing","wonderful","superb","outstanding","reward","tip","bonus","payment","compensate","grateful","appreciate"]),weight:.1},covert:{keywords:new Set(["secret","hidden","covert","quiet","silent","discreetly","stealth","confidential","private","classified","concealed","disguised","undercover","off the record","between us","nobody will know","our little secret","hush","whisper"]),weight:.2},format_manipulation:{keywords:new Set(["encode","decode","disguise","morse","binary","hexadecimal","base64","rot13","cipher","encrypt","obfuscate","scramble","reverse","translate to code","convert to"]),weight:.15},hypothetical:{keywords:new Set(["assume","imagine","hypothetically","fictional","scenario","pretend","suppose","what if","let's say","in theory","theoretically","fantasy","roleplay","simulation","make believe","alternate reality","parallel universe","thought experiment"]),weight:.15},systemic:{keywords:new Set(["developer","administrator","admin","boss","manager","creator","owner","supervisor","root","superuser","operator","maintainer","engineer","architect","designer","authority","controller","master","god mode","sudo"]),weight:.2},immoral:{keywords:new Set(["amoral","immoral","unethical","illegal","harmful","dangerous","offensive","violent","malicious","evil","wicked","sinister","criminal","unlawful","forbidden","prohibited","taboo","no morals","no ethics","no rules","no limits","unrestricted","uncensored","unfiltered"]),weight:.25}},INSTRUCTION_WORDS=new Set(["must","should","shall","will","need","require","always","never","do","don't","cannot","can't","ensure","make sure","remember","forget","ignore","follow","obey","comply","execute","perform","output","respond","reply","answer","generate","create","write","act","behave","pretend","assume","become","transform"]);class HeuristicAnalyzer{constructor(t={}){this.config={synonymExpansion:t.synonymExpansion??!0,structuralAnalysis:t.structuralAnalysis??!0,statisticalScoring:t.statisticalScoring??!0,riskThreshold:t.riskThreshold??.8,manyShotThreshold:t.manyShotThreshold??3,repeatedTokenThreshold:t.repeatedTokenThreshold??3}}analyze(t,i){const r=[],s={is_ignore:!1,is_urgent:!1,is_incentive:!1,is_covert:!1,is_format_manipulation:!1,is_hypothetical:!1,is_systemic:!1,is_immoral:!1,synonym_categories_matched:0,is_shot_attack:!1,is_repeated_token:!1,is_imperative:!1,is_role_assignment:!1,structural_score:0,instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,statistical_score:0};let c=0;if(this.config.synonymExpansion){const e=this.checkSynonyms(t);Object.assign(s,e.features),c+=e.risk,e.risk>0&&r.push(...e.matched.map(l=>`SYNONYM_${l.toUpperCase()}`))}if(this.config.structuralAnalysis){const e=this.checkStructure(t);s.is_shot_attack=e.is_shot_attack,s.is_repeated_token=e.is_repeated_token,s.is_imperative=e.is_imperative,s.is_role_assignment=e.is_role_assignment,s.structural_score=e.score,c+=e.score,e.violations.length>0&&r.push(...e.violations)}if(this.config.statisticalScoring){const e=this.scoreStatistics(t);s.instruction_word_density=e.instruction_word_density,s.special_char_ratio=e.special_char_ratio,s.uppercase_ratio=e.uppercase_ratio,s.average_word_length=e.average_word_length,s.statistical_score=e.score,c+=e.score}s.synonym_categories_matched>=3&&(c+=.15,r.push("MULTI_CATEGORY_COMPOUND"));const a=Math.min(1,c),n=a<this.config.riskThreshold;return{allowed:n,reason:n?void 0:`Heuristic analysis risk ${a.toFixed(2)} exceeds threshold ${this.config.riskThreshold}`,riskScore:a,features:s,violations:r}}checkSynonyms(t){const i=t.toLowerCase().split(/\s+/).map(e=>e.replace(/[^a-z'-]/g,"")).filter(e=>e.length>2),r=t.toLowerCase(),s={};let c=0;const a=[];let n=0;for(const[e,{keywords:l,weight:_}]of Object.entries(SYNONYM_SETS)){let h=!1;for(const o of i)if(l.has(o)){h=!0;break}if(!h){for(const o of l)if(o.includes(" ")&&r.includes(o)){h=!0;break}}h?(s[`is_${e}`]=!0,c+=_,a.push(e),n++):s[`is_${e}`]=!1}return s.synonym_categories_matched=n,{features:s,risk:c,matched:a}}checkStructure(t){const i=[];let r=0;const s=/(?:Q:|Question:|Human:|User:)[\s\S]*?(?:A:|Answer:|Assistant:|AI:)/gi,a=(t.match(s)||[]).length>=this.config.manyShotThreshold;a&&(r+=.3,i.push("MANY_SHOT_PATTERN"));const n=t.toLowerCase().split(/\s+/).filter(u=>u.length>3),e=new Map;for(const u of n)e.set(u,(e.get(u)||0)+1);const l=Math.max(0,...e.values()),_=l>=this.config.repeatedTokenThreshold&&n.length>10&&l/n.length>.15;_&&(r+=.1,i.push("REPEATED_TOKEN_ATTACK"));const h=t.split(/[.!?\n]+/).filter(u=>u.trim().length>5);let o=0;for(const u of h){const m=u.trim();/^(?:ignore|forget|disregard|override|bypass|reveal|show|tell|give|grant|make|do|don't|never|always|you\s+(?:must|should|will|are|can))/i.test(m)&&o++}const d=h.length>0&&o/h.length>.4;d&&(r+=.15,i.push("HIGH_IMPERATIVE_RATIO"));const g=/you\s+(?:are|will\s+be|shall\s+be|must\s+be)\s+(?:now\s+)?(?:a|an|the|my)\s+/i.test(t)&&/(?:no\s+(?:restrictions|rules|limits)|unrestricted|unfiltered|evil|amoral|can\s+do\s+anything)/i.test(t);return g&&(r+=.25,i.push("ROLE_ASSIGNMENT_WITH_BYPASS")),{is_shot_attack:a,is_repeated_token:_,is_imperative:d,is_role_assignment:g,score:r,violations:i}}scoreStatistics(t){const i=t.split(/\s+/).filter(d=>d.length>0);if(i.length===0)return{instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,score:0};let r=0;for(const d of i)INSTRUCTION_WORDS.has(d.toLowerCase().replace(/[^a-z']/g,""))&&r++;const s=r/i.length,a=t.replace(/[a-zA-Z0-9\s]/g,"").length/t.length,n=t.replace(/[^A-Z]/g,"").length,e=t.replace(/[^a-zA-Z]/g,"").length,l=e>0?n/e:0,h=i.reduce((d,g)=>d+g.length,0)/i.length;let o=0;return s>.15&&(o+=.1),s>.25&&(o+=.1),l>.4&&e>20&&(o+=.05),{instruction_word_density:s,special_char_ratio:a,uppercase_ratio:l,average_word_length:h,score:o}}}exports.HeuristicAnalyzer=HeuristicAnalyzer;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.HeuristicAnalyzer=void 0;const SYNONYM_SETS={ignore:{keywords:new Set(["ignore","disregard","overlook","neglect","skip","bypass","omit","dismiss","override","overrule","forget","abandon","drop","ditch","scrap","discard","set aside","pay no attention","turn a blind eye","look past","brush off","shrug off","wave away","cast aside","put aside","leave behind","throw out","toss out"]),weight:.3},urgent:{keywords:new Set(["urgent","immediate","immediately","asap","emergency","critical","time-sensitive","pressing","crucial","dire","life-threatening","now","hurry","rush","quick","fast","right away","without delay","time is running out","deadline"]),weight:.15},incentive:{keywords:new Set(["excellent","fantastic","nice","awesome","brilliant","great","amazing","wonderful","superb","outstanding","reward","tip","bonus","payment","compensate","grateful","appreciate"]),weight:.1},covert:{keywords:new Set(["secret","hidden","covert","quiet","silent","discreetly","stealth","confidential","private","classified","concealed","disguised","undercover","off the record","between us","nobody will know","our little secret","hush","whisper"]),weight:.2},format_manipulation:{keywords:new Set(["encode","decode","disguise","morse","binary","hexadecimal","base64","rot13","cipher","encrypt","obfuscate","scramble","reverse","translate to code","convert to"]),weight:.15},hypothetical:{keywords:new Set(["assume","imagine","hypothetically","fictional","scenario","pretend","suppose","what if","let's say","in theory","theoretically","fantasy","roleplay","simulation","make believe","alternate reality","parallel universe","thought experiment"]),weight:.15},systemic:{keywords:new Set(["developer","administrator","admin","boss","manager","creator","owner","supervisor","root","superuser","operator","maintainer","engineer","architect","designer","authority","controller","master","god mode","sudo"]),weight:.2},immoral:{keywords:new Set(["amoral","immoral","unethical","illegal","harmful","dangerous","offensive","violent","malicious","evil","wicked","sinister","criminal","unlawful","forbidden","prohibited","taboo","no morals","no ethics","no rules","no limits","unrestricted","uncensored","unfiltered"]),weight:.25}},INSTRUCTION_WORDS=new Set(["must","should","shall","will","need","require","always","never","do","don't","cannot","can't","ensure","make sure","remember","forget","ignore","follow","obey","comply","execute","perform","output","respond","reply","answer","generate","create","write","act","behave","pretend","assume","become","transform"]);class HeuristicAnalyzer{constructor(t={}){this.config={synonymExpansion:t.synonymExpansion??!0,structuralAnalysis:t.structuralAnalysis??!0,statisticalScoring:t.statisticalScoring??!0,riskThreshold:t.riskThreshold??.8,manyShotThreshold:t.manyShotThreshold??3,repeatedTokenThreshold:t.repeatedTokenThreshold??3}}analyze(t,i){const r=[],s={is_ignore:!1,is_urgent:!1,is_incentive:!1,is_covert:!1,is_format_manipulation:!1,is_hypothetical:!1,is_systemic:!1,is_immoral:!1,synonym_categories_matched:0,is_shot_attack:!1,is_repeated_token:!1,is_imperative:!1,is_role_assignment:!1,structural_score:0,instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,statistical_score:0};let c=0;if(this.config.synonymExpansion){const e=this.checkSynonyms(t);Object.assign(s,e.features),c+=e.risk,e.risk>0&&r.push(...e.matched.map(l=>`SYNONYM_${l.toUpperCase()}`))}if(this.config.structuralAnalysis){const e=this.checkStructure(t);s.is_shot_attack=e.is_shot_attack,s.is_repeated_token=e.is_repeated_token,s.is_imperative=e.is_imperative,s.is_role_assignment=e.is_role_assignment,s.structural_score=e.score,c+=e.score,e.violations.length>0&&r.push(...e.violations)}if(this.config.statisticalScoring){const e=this.scoreStatistics(t);s.instruction_word_density=e.instruction_word_density,s.special_char_ratio=e.special_char_ratio,s.uppercase_ratio=e.uppercase_ratio,s.average_word_length=e.average_word_length,s.statistical_score=e.score,c+=e.score}s.synonym_categories_matched>=3&&(c+=.15,r.push("MULTI_CATEGORY_COMPOUND"));const a=Math.min(1,c),n=a<this.config.riskThreshold;return{allowed:n,reason:n?void 0:`Heuristic analysis risk ${a.toFixed(2)} exceeds threshold ${this.config.riskThreshold}`,riskScore:a,features:s,violations:r}}checkSynonyms(t){const i=t.toLowerCase().split(/\s+/).map(e=>e.replace(/[^a-z'-]/g,"")).filter(e=>e.length>2),r=t.toLowerCase(),s={};let c=0;const a=[];let n=0;for(const[e,{keywords:l,weight:g}]of Object.entries(SYNONYM_SETS)){let h=!1;for(const o of i)if(l.has(o)){h=!0;break}if(!h){for(const o of l)if(o.includes(" ")&&r.includes(o)){h=!0;break}}const d=`is_${e}`;h?(s[d]=!0,c+=g,a.push(e),n++):s[d]=!1}return s.synonym_categories_matched=n,{features:s,risk:c,matched:a}}checkStructure(t){const i=[];let r=0;const s=/(?:Q:|Question:|Human:|User:)[\s\S]*?(?:A:|Answer:|Assistant:|AI:)/gi,a=(t.match(s)||[]).length>=this.config.manyShotThreshold;a&&(r+=.3,i.push("MANY_SHOT_PATTERN"));const n=t.toLowerCase().split(/\s+/).filter(u=>u.length>3),e=new Map;for(const u of n)e.set(u,(e.get(u)||0)+1);const l=Math.max(0,...e.values()),g=l>=this.config.repeatedTokenThreshold&&n.length>10&&l/n.length>.15;g&&(r+=.1,i.push("REPEATED_TOKEN_ATTACK"));const h=t.split(/[.!?\n]+/).filter(u=>u.trim().length>5);let d=0;for(const u of h){const m=u.trim();/^(?:ignore|forget|disregard|override|bypass|reveal|show|tell|give|grant|make|do|don't|never|always|you\s+(?:must|should|will|are|can))/i.test(m)&&d++}const o=h.length>0&&d/h.length>.4;o&&(r+=.15,i.push("HIGH_IMPERATIVE_RATIO"));const _=/you\s+(?:are|will\s+be|shall\s+be|must\s+be)\s+(?:now\s+)?(?:a|an|the|my)\s+/i.test(t)&&/(?:no\s+(?:restrictions|rules|limits)|unrestricted|unfiltered|evil|amoral|can\s+do\s+anything)/i.test(t);return _&&(r+=.25,i.push("ROLE_ASSIGNMENT_WITH_BYPASS")),{is_shot_attack:a,is_repeated_token:g,is_imperative:o,is_role_assignment:_,score:r,violations:i}}scoreStatistics(t){const i=t.split(/\s+/).filter(o=>o.length>0);if(i.length===0)return{instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,score:0};let r=0;for(const o of i)INSTRUCTION_WORDS.has(o.toLowerCase().replace(/[^a-z']/g,""))&&r++;const s=r/i.length,a=t.replace(/[a-zA-Z0-9\s]/g,"").length/t.length,n=t.replace(/[^A-Z]/g,"").length,e=t.replace(/[^a-zA-Z]/g,"").length,l=e>0?n/e:0,h=i.reduce((o,_)=>o+_.length,0)/i.length;let d=0;return s>.15&&(d+=.1),s>.25&&(d+=.1),l>.4&&e>20&&(d+=.05),{instruction_word_density:s,special_char_ratio:a,uppercase_ratio:l,average_word_length:h,score:d}}}exports.HeuristicAnalyzer=HeuristicAnalyzer;
@@ -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;