agentshield-sdk 14.0.0 → 14.2.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
@@ -4,6 +4,101 @@ All notable changes to Agent Shield will be documented in this file.
4
4
 
5
5
  This project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [14.2.0] - 2026-05-11
8
+
9
+ ### May 2026 Threat Response + Performance + DX
10
+
11
+ Response to threats disclosed between April 25 and May 11, 2026.
12
+
13
+ #### New Detection Patterns (4 patterns, 303 → 307)
14
+
15
+ - **TrustFall malicious project files** (2 patterns) — Adversa AI disclosed May 2026: malicious `.claude/`, `.cursor/`, `.windsurf/` config files with auto-execution hooks (`preCommand`, `onStart`, etc.) trigger one-keypress compromise of AI coding agents and exfiltrate CI env vars
16
+ - **Semantic Kernel RCE** — Microsoft Semantic Kernel (CVE-2026-25592 / CVE-2026-26030, disclosed May 7) allows prompt injection to invoke arbitrary kernel functions and achieve RCE on the host process
17
+ - **WebSocket cross-origin hijacking** — CVE-2026-44211 (Cline Kanban) and CVE-2026-32173 (Azure SRE Agent CVSS 8.6): WebSockets without origin validation let attackers inject prompts into running agent terminals
18
+
19
+ #### CVE Registry Expansion (33 → 44 CVEs)
20
+
21
+ - CVE-2026-25592 / CVE-2026-26030: Microsoft Semantic Kernel RCE (May 7)
22
+ - CVE-2026-42302: FastGPT agent-sandbox unauth RCE (CVSS 9.8, May 8)
23
+ - CVE-2026-44284: FastGPT MCP SSRF
24
+ - CVE-2026-42344: FastGPT DNS rebinding bypass
25
+ - CVE-2026-44211: Cline Kanban WebSocket Hijacking
26
+ - CVE-2026-32173: Azure SRE Agent unauth WebSocket (CVSS 8.6)
27
+ - CVE-2026-44400-403: 4× CrewAI Code Interpreter chain RCE/SSRF/file-read
28
+
29
+ #### Performance: LRU Cache (151x speedup on warm cache)
30
+
31
+ - Added 1000-entry LRU cache to `scanText()` keyed on `(source, sensitivity, text)`
32
+ - Cached scans complete in ~1μs vs ~190μs cold (151x speedup on short malicious inputs, 90x on benign)
33
+ - Eliminates duplicate work in RAG pipelines, batch processors, and middleware retry loops
34
+ - Inputs >2048 chars bypass the cache to avoid memory bloat
35
+ - Opt-out via `scanText(text, { useCache: false })`
36
+ - Result object includes `fromCache: true` when served from cache
37
+
38
+ #### Developer Experience
39
+
40
+ - New examples for the platforms developers actually deploy to in 2026:
41
+ - `examples/cloudflare-workers-ai.js` — Workers AI guardrail with input + output scanning
42
+ - `examples/nextjs-edge-middleware.js` — Next.js Edge middleware for `/api/chat/*` and `/api/agent/*` routes
43
+ - `examples/vercel-ai-sdk-guardrail.js` — Vercel AI SDK streaming chat guardrail
44
+ - All examples are self-contained and ready to copy-paste into a real app
45
+
46
+ #### Test Coverage
47
+
48
+ - New `test/test-v14.2-patterns.js` — 32 assertions covering LRU cache correctness, all 4 new patterns, all 11 new CVE entries, and 6 false-positive regression samples
49
+ - Total project assertions: ~3,200+ across all suites; v14.2 specific: 32
50
+
51
+ #### Known Limitations Documented
52
+
53
+ - Rust NAPI native scanner (`src/native-scanner.js`) is loaded but NOT wired into the JS hot path. Investigation revealed the Rust core has only 141 patterns vs JS's 307, so wiring it in blindly would silently lose 166 patterns of coverage. Use of the native scanner is gated on a future pattern-sync effort.
54
+
55
+ ## [14.1.0] - 2026-04-24
56
+
57
+ ### April 2026 Threat Response — Comment-and-Control, MCP CVE Wave, OAuth Supply Chain
58
+
59
+ Rapid security update responding to this week's active attacks: Vercel/Context.ai OAuth supply chain breach, "Comment and Control" zero-click credential theft from AI coding agents, 7 new MCP CVEs, Unit 42 MCP sampling attacks, and malicious LLM API routers.
60
+
61
+ #### New Detection Patterns (13 patterns, 290 → 303)
62
+
63
+ - **CI/CD Agent Injection** (`cicd_injection`) — detects prompt injection targeting AI coding agents via PR titles, issue comments, and review comments. Defends against the "Comment and Control" attack (April 2026) that exfiltrated credentials from Claude Code, Gemini CLI, and GitHub Copilot
64
+ - **Credential Exfiltration** (`credential_exfiltration`) — detects `/proc/[pid]/environ` reads (Copilot bypass technique), API key patterns in agent output (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.), and OAuth/bearer token exfiltration with provider-specific prefixes (ya29, ghp_, sk-, xox-, AKIA)
65
+ - **OAuth Flow Manipulation** (`credential_exfiltration`) — detects grant_type/redirect_uri/client_secret manipulation targeting token theft, inspired by the Vercel/Context.ai supply chain breach
66
+ - **MCP Sampling Injection** (`mcp_sampling_injection`) — detects hidden instructions injected via MCP sampling/createMessage requests (Unit 42 research, April 2026)
67
+ - **LLM Router Tampering** (`llm_router_tampering`) — detects OPENAI_BASE_URL/ANTHROPIC_BASE_URL overrides pointing to untrusted endpoints (arXiv 2604.08407: 9 of 28 paid routers actively malicious)
68
+ - **MCP STDIO Command Injection** (`mcp_command_injection`) — detects `npx -c` command injection via MCP STDIO transport (CVE-2026-30623, 200K+ servers affected)
69
+
70
+ #### CVE Registry Update (26 → 33 CVEs)
71
+
72
+ - CVE-2026-40933: Flowise MCP Adapters RCE (CVSS 9.9)
73
+ - CVE-2026-41264: Flowise CSV Agent prompt injection to RCE
74
+ - CVE-2026-33626: LMDeploy SSRF (exploited within 12 hours of disclosure)
75
+ - CVE-2026-33032: nginx-ui MCP auth bypass (CVSS 9.8, actively exploited)
76
+ - CVE-2026-20205: Splunk MCP Server cleartext token logging (CVSS 7.2)
77
+ - CVE-2026-33946: MCP Ruby SDK session fixation
78
+ - CVE-2026-5603: magento2-dev-mcp command injection
79
+
80
+ #### MCPGuard Security Hardening
81
+
82
+ - **Tool name squatting detection** — `registerServer()` now detects and warns when a new MCP server registers a tool name already owned by another server (MCPShield arXiv:2604.05969 "Server Spoofing" vector)
83
+ - **Context flooding defense** — `interceptToolOutput()` flags tool outputs exceeding `maxToolOutputSize` (default 100KB) to prevent context window exhaustion attacks
84
+ - **Recursive tool invocation depth limit** — blocks tool call chains exceeding `maxCallDepth` (default 5) to prevent reentrancy attacks and unbounded recursive loops
85
+
86
+ #### Supply Chain Scanner Enhancements
87
+
88
+ - **Consent phishing detection** — flags tools whose description implies read-only but whose schema contains write/network parameters (OWASP ASI09 Human-Agent Trust Exploitation)
89
+
90
+ #### Integration Updates
91
+
92
+ - `shieldGoogleADKJS()` — new wrapper for Google ADK TypeScript/JavaScript SDK (GA April 2026)
93
+ - GPT-5.5 model risk profile added to MCP Guard (critical susceptibility, elevated sandbox escape surface)
94
+
95
+ #### Test Coverage Expansion (+416 assertions)
96
+
97
+ - `test-v14.1-patterns.js` — 61 assertions: all 5 new categories, 12 FP guards, ADK-JS integration, CVE registry
98
+ - `test-pattern-categories.js` — 66 assertions: detection test for every 51 pattern categories + 15 benign guards
99
+ - `test-supply-chain-cves.js` — 228 assertions: all 33 CVEs, 9 blocklist entries, injection/SSRF/poisoning patterns, consent phishing, SARIF/Markdown output
100
+ - Total new assertions this release: 355
101
+
7
102
  ## [14.0.0] - 2026-04-16
8
103
 
9
104
  ### Major Release — Platform Parity + Framework Integrations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentshield-sdk",
3
- "version": "14.0.0",
3
+ "version": "14.2.0",
4
4
  "description": "SOTA AI agent security SDK. F1 1.000 on BIPIA/HackAPrompt/MCPTox/Multilingual benchmarks. 400+ exports, 100+ modules. Zero dependencies, runs locally.",
5
5
  "main": "src/main.js",
6
6
  "types": "types/index.d.ts",
@@ -18,6 +18,38 @@
18
18
  let _nativeScanner = null;
19
19
  try { _nativeScanner = require('./native-scanner'); } catch { /* optional */ }
20
20
 
21
+ // =========================================================================
22
+ // LRU CACHE FOR REPEATED INPUTS
23
+ // =========================================================================
24
+
25
+ /** Maximum cache size (entries). */
26
+ const SCAN_CACHE_MAX = 1000;
27
+
28
+ /** Maximum input length to cache (avoid bloating memory with large inputs). */
29
+ const SCAN_CACHE_MAX_INPUT_LEN = 2048;
30
+
31
+ /** @type {Map<string, object>} */
32
+ const _scanCache = new Map();
33
+
34
+ /** Move key to most-recent position. */
35
+ const _cacheTouch = (key) => {
36
+ const value = _scanCache.get(key);
37
+ if (value !== undefined) {
38
+ _scanCache.delete(key);
39
+ _scanCache.set(key, value);
40
+ }
41
+ return value;
42
+ };
43
+
44
+ /** Insert with LRU eviction. */
45
+ const _cachePut = (key, value) => {
46
+ if (_scanCache.size >= SCAN_CACHE_MAX) {
47
+ const oldest = _scanCache.keys().next().value;
48
+ if (oldest !== undefined) _scanCache.delete(oldest);
49
+ }
50
+ _scanCache.set(key, value);
51
+ };
52
+
21
53
  // =========================================================================
22
54
  // PERFORMANCE
23
55
  // =========================================================================
@@ -104,6 +136,13 @@ const PRIMARY_ATTACK_INDICATORS = new RegExp(
104
136
  '\\bGPT\\s*[-:]\\s*\\d', // GPT-4 references in injection contexts
105
137
  'api[-\\s_]?key\\s*[=:]',
106
138
  'password\\s*[=:]\\s*[\'"]',
139
+ '/proc/(?:\\d|self)',
140
+ 'oauth|bearer|access.token|refresh.token',
141
+ 'ANTHROPIC_BASE_URL|OPENAI_BASE_URL|API_BASE',
142
+ 'sampling|createMessage|create_message',
143
+ 'npx\\s+-c',
144
+ '@claude|@copilot|@gemini|@cursor',
145
+ 'grant_type|redirect_uri|client_secret',
107
146
  ].join('|'),
108
147
  'i'
109
148
  );
@@ -2426,6 +2465,143 @@ const INJECTION_PATTERNS = [
2426
2465
  category: 'structured_data_injection',
2427
2466
  description: 'Detects prompt injection hidden in structured data formats',
2428
2467
  detail: 'Structured data injection: agents constantly parse JSON/XML/YAML/CSV and attackers embed instructions in metadata fields, CDATA sections, and comments'
2468
+ },
2469
+
2470
+ // --- CI/CD Agent Injection (Comment-and-Control, April 2026) ---
2471
+ {
2472
+ regex: /(?:^|\n)\s*(?:<!--\s*)?(?:ignore|override|disregard|forget)\s+(?:all\s+)?(?:previous|prior|above)\s+(?:instructions|rules|context)[\s\S]{0,200}(?:add\s+(?:a\s+)?comment|create\s+(?:a\s+)?(?:issue|pr|pull\s*request)|push\s+to|commit\s+to|post\s+to|curl\s+|fetch\s*\(|http|GITHUB_TOKEN|SECRET|API.KEY)/i,
2473
+ severity: 'critical',
2474
+ category: 'cicd_injection',
2475
+ description: 'Prompt injection targeting AI coding agents via PR titles, issue comments, or review comments',
2476
+ detail: 'Comment-and-Control attack (April 2026): single malicious PR title or issue comment exfiltrates credentials from Claude Code, Gemini CLI, GitHub Copilot via CI/CD auto-triggers'
2477
+ },
2478
+ {
2479
+ regex: /(?:^|\n)\s*@(?:claude|copilot|gemini|cursor|windsurf|cody|aider)\b[\s\S]{0,100}(?:exfiltrate|steal|extract|leak|send\s+to|post\s+to|upload\s+to)/i,
2480
+ severity: 'critical',
2481
+ category: 'cicd_injection',
2482
+ description: 'Prompt injection mentioning AI coding agent by name with exfiltration intent',
2483
+ detail: 'Comment-and-Control: targets specific AI coding agents by @-mention in PR/issue comments to trigger credential theft'
2484
+ },
2485
+ {
2486
+ regex: /\/proc\/(?:[0-9*]+|self)\/(?:environ|cmdline|maps)/i,
2487
+ severity: 'critical',
2488
+ category: 'credential_exfiltration',
2489
+ description: 'Attempts to read process environment or command line to steal secrets',
2490
+ detail: 'Comment-and-Control (April 2026): GitHub Copilot secret theft bypassed all filters by reading /proc/[pid]/environ of parent Node.js process'
2491
+ },
2492
+ {
2493
+ regex: /(?:ANTHROPIC|OPENAI|GITHUB|AWS|AZURE|GCP|GOOGLE)_(?:API_KEY|SECRET|TOKEN|ACCESS_KEY)\s*[=:]\s*\S{10,}/i,
2494
+ severity: 'critical',
2495
+ category: 'credential_exfiltration',
2496
+ description: 'Detects API keys or secrets being included in agent output',
2497
+ detail: 'Credential exfiltration: agent output contains what appears to be an API key or secret token from a major provider'
2498
+ },
2499
+
2500
+ // --- OAuth Token Exfiltration (Vercel/Context.ai breach, April 2026) ---
2501
+ {
2502
+ regex: /(?:oauth[_-]?token|bearer[_-]?token|access[_-]?token|refresh[_-]?token|id[_-]?token)\s*[=:]\s*["']?(?:ya29[.\-]|eyJ|gho_|ghp_|ghu_|github_pat_|sk-|sk-ant-|xox[bpas]-|AKIA)\S{10,}/i,
2503
+ severity: 'critical',
2504
+ category: 'credential_exfiltration',
2505
+ description: 'Detects OAuth/bearer tokens being exfiltrated through agent output',
2506
+ detail: 'Vercel/Context.ai breach (April 2026): stolen OAuth tokens pivoted into internal systems. Detects common token prefixes from Google, GitHub, OpenAI, Anthropic, Slack, AWS'
2507
+ },
2508
+ {
2509
+ regex: /(?:grant_type|redirect_uri|client_secret)\s*[=:]\s*\S+[\s\S]{0,200}(?:attacker|evil|malicious|exfil|leak|steal)/i,
2510
+ severity: 'high',
2511
+ category: 'credential_exfiltration',
2512
+ description: 'Detects OAuth flow manipulation for token theft',
2513
+ detail: 'OAuth supply chain attack pattern: manipulates grant_type, redirect_uri, or client_secret in agent context to redirect tokens to attacker-controlled endpoints'
2514
+ },
2515
+
2516
+ // --- MCP Sampling Injection (Unit 42, April 2026) ---
2517
+ {
2518
+ regex: /(?:sampling|createMessage|create_message)\s*[\({][\s\S]{0,300}(?:ignore|override|system|instruction|hidden|inject)/i,
2519
+ severity: 'high',
2520
+ category: 'mcp_sampling_injection',
2521
+ description: 'Detects prompt injection via MCP sampling/createMessage requests',
2522
+ detail: 'Unit 42 MCP sampling attacks (April 2026): servers inject hidden instructions via sampling requests for resource theft, conversation hijacking, and unauthorized content generation'
2523
+ },
2524
+ {
2525
+ regex: /(?:includeContext|systemPrompt|maxTokens)\s*[=:]\s*[\s\S]{0,200}(?:ignore|override|disregard|forget)\s+(?:previous|prior|all)/i,
2526
+ severity: 'high',
2527
+ category: 'mcp_sampling_injection',
2528
+ description: 'Detects MCP sampling parameter manipulation with injection payload',
2529
+ detail: 'Unit 42 MCP sampling attacks: manipulates MCP sampling parameters (includeContext, systemPrompt) to inject instructions into the conversation'
2530
+ },
2531
+
2532
+ // --- LLM Router Tampering (arXiv 2604.08407, April 2026) ---
2533
+ {
2534
+ regex: /(?:api\.openai\.com|api\.anthropic\.com|generativelanguage\.googleapis\.com)[\s\S]{0,100}(?:redirect|proxy|forward|route)\s*(?:to|via|through)\s*\S+/i,
2535
+ severity: 'high',
2536
+ category: 'llm_router_tampering',
2537
+ description: 'Detects attempts to redirect LLM API calls through untrusted proxies',
2538
+ detail: 'Your Agent Is Mine (arXiv 2604.08407): 9 of 28 paid LLM API routers actively inject malicious code. Detects redirection of API calls to untrusted endpoints'
2539
+ },
2540
+ {
2541
+ regex: /(?:OPENAI_BASE_URL|ANTHROPIC_BASE_URL|API_BASE|base_url)\s*[=:]\s*["']?https?:\/\/(?!(?:api\.openai\.com|api\.anthropic\.com|localhost|127\.0\.0\.1))\S+/i,
2542
+ severity: 'high',
2543
+ category: 'llm_router_tampering',
2544
+ description: 'Detects LLM API base URL override pointing to untrusted endpoint',
2545
+ detail: 'LLM router attack + Claude Code CVE-2026-21852: ANTHROPIC_BASE_URL/OPENAI_BASE_URL overridden to redirect API calls and leak keys to attacker server'
2546
+ },
2547
+
2548
+ // --- MCP STDIO Command Injection (CVE-2026-30623, April 2026) ---
2549
+ {
2550
+ regex: /(?:npx\s+-c|npx\s+--command)\s+["']?[\s\S]{0,200}(?:curl|wget|nc\b|ncat|bash|sh\b|python|node\s+-e|eval)/i,
2551
+ severity: 'critical',
2552
+ category: 'mcp_command_injection',
2553
+ description: 'Detects command injection via MCP STDIO npx -c pattern',
2554
+ detail: 'CVE-2026-30623 (April 2026): MCP STDIO transport allows configuration-to-command execution. npx -c commands achieve OS command execution affecting 200K+ servers'
2555
+ },
2556
+
2557
+ // --- Code Execution Sink Detection (OWASP ASI05) ---
2558
+ {
2559
+ regex: /(?:^|[\s;])(?:eval|Function)\s*\(\s*(?:response|output|result|completion|generated|llm|model|agent)/i,
2560
+ severity: 'critical',
2561
+ category: 'code_execution_sink',
2562
+ description: 'Detects LLM output being passed directly to eval() or Function()',
2563
+ detail: 'Code execution sink: LLM output fed to eval()/Function() allows prompt injection to achieve arbitrary code execution (OWASP ASI05)'
2564
+ },
2565
+
2566
+ // --- TrustFall: Malicious Project File Injection (Adversa AI, May 2026) ---
2567
+ {
2568
+ regex: /(?:\.claude|\.cursor|\.windsurf|\.copilot)\/(?:config|settings|rules|hooks|commands)[\s\S]{0,200}(?:curl|wget|exec|bash|sh\s|node\s+-e|python\s+-c|nc\s)/i,
2569
+ severity: 'critical',
2570
+ category: 'cicd_injection',
2571
+ description: 'Detects malicious AI coding agent config files that trigger one-keypress compromise',
2572
+ detail: 'TrustFall attack (Adversa AI, May 2026): malicious project files in .claude/, .cursor/, .windsurf/ config directories execute commands on agent invocation. Exfiltrates CI environment variables.'
2573
+ },
2574
+ {
2575
+ regex: /(?:^|\n)\s*(?:hook|onStart|preCommand|postCommand|autoexec)\s*[:=]\s*["\']?[\s\S]{0,150}(?:curl|wget|nc\s|bash\s+-c|exec\s*\()/i,
2576
+ severity: 'high',
2577
+ category: 'cicd_injection',
2578
+ description: 'Detects auto-execution hooks in AI agent config files',
2579
+ detail: 'TrustFall: hooks defined in project files trigger automatic command execution when AI coding agent loads the project'
2580
+ },
2581
+
2582
+ // --- Semantic Kernel RCE (CVE-2026-25592 / 26030) ---
2583
+ {
2584
+ regex: /(?:kernel|sk|SemanticKernel)\.(?:invoke|run|execute|RunAsync)\s*\([^)]{0,200}(?:user|prompt|input|untrusted|external)/i,
2585
+ severity: 'high',
2586
+ category: 'code_execution_sink',
2587
+ description: 'Detects Semantic Kernel function invocation with untrusted input',
2588
+ detail: 'CVE-2026-25592/26030 (May 2026): Microsoft Semantic Kernel allows prompt injection to invoke arbitrary kernel functions, leading to RCE on the host process'
2589
+ },
2590
+
2591
+ // --- WebSocket Cross-Origin Hijacking (CVE-2026-44211, CVE-2026-32173) ---
2592
+ {
2593
+ regex: /new\s+WebSocket\s*\(\s*["\']wss?:\/\/(?!(?:localhost|127\.0\.0\.1|0\.0\.0\.0))[^"\']*["\']\s*\)[\s\S]{0,300}(?:Origin|origin)\s*[:=]\s*["\']?\*/i,
2594
+ severity: 'high',
2595
+ category: 'cross_agent_injection',
2596
+ description: 'Detects WebSocket connections with wildcard origin (cross-origin hijacking)',
2597
+ detail: 'CVE-2026-44211 (Cline) / CVE-2026-32173 (Azure SRE Agent): WebSocket without origin validation allows cross-origin hijacking — attackers inject prompts into running agent terminals'
2598
+ },
2599
+ {
2600
+ regex: /(?:child_process|subprocess|os\.system|os\.popen|exec|execSync|spawn)\s*\(\s*(?:response|output|result|completion|generated|llm|model|agent)/i,
2601
+ severity: 'critical',
2602
+ category: 'code_execution_sink',
2603
+ description: 'Detects LLM output being passed to shell execution functions',
2604
+ detail: 'Code execution sink: LLM output passed to child_process/subprocess enables arbitrary command execution via prompt injection'
2429
2605
  }
2430
2606
  ];
2431
2607
 
@@ -3307,6 +3483,22 @@ const scanText = (text, options = {}) => {
3307
3483
  truncated = true;
3308
3484
  }
3309
3485
 
3486
+ // ------------------------------------------------------------------
3487
+ // LRU CACHE: exact-match memoization for repeated inputs
3488
+ // ------------------------------------------------------------------
3489
+ // RAG pipelines, batch processors, and middleware retry loops re-scan
3490
+ // identical text constantly. A 1000-entry LRU keyed on (source|text)
3491
+ // eliminates duplicate work for ~1μs per hit.
3492
+ const cacheable = text.length <= SCAN_CACHE_MAX_INPUT_LEN && options.useCache !== false;
3493
+ let cacheKey = null;
3494
+ if (cacheable) {
3495
+ cacheKey = source + '\x00' + sensitivity + '\x00' + text;
3496
+ const cached = _cacheTouch(cacheKey);
3497
+ if (cached !== undefined) {
3498
+ return { ...cached, stats: { ...cached.stats, scanTimeMs: now() - startTime }, fromCache: true };
3499
+ }
3500
+ }
3501
+
3310
3502
  // ------------------------------------------------------------------
3311
3503
  // FAST PATH: long clean text (no attack indicators, no obfuscation)
3312
3504
  // ------------------------------------------------------------------
@@ -3334,6 +3526,7 @@ const scanText = (text, options = {}) => {
3334
3526
  if (truncated) {
3335
3527
  fastResult.warnings = [`Input exceeded ${maxSize} characters and was truncated for scanning.`];
3336
3528
  }
3529
+ if (cacheKey) _cachePut(cacheKey, fastResult);
3337
3530
  return fastResult;
3338
3531
  }
3339
3532
 
@@ -3490,6 +3683,7 @@ const scanText = (text, options = {}) => {
3490
3683
  result.truncated = true;
3491
3684
  result.warnings = [`Input exceeded ${maxSize} characters and was truncated for scanning.`];
3492
3685
  }
3686
+ if (cacheKey) _cachePut(cacheKey, result);
3493
3687
  return result;
3494
3688
  };
3495
3689
 
@@ -366,8 +366,98 @@ function shieldMSAgentFramework(options = {}) {
366
366
  return { agentMiddleware, shield };
367
367
  }
368
368
 
369
+ // =========================================================================
370
+ // Google ADK JavaScript (adk-js) Integration
371
+ // =========================================================================
372
+
373
+ /**
374
+ * Creates Agent Shield middleware for the Google ADK JavaScript/TypeScript SDK.
375
+ *
376
+ * Google ADK for JS (GA April 2026) uses a callback-based agent lifecycle.
377
+ * This wrapper returns a plugin object compatible with ADK-JS's plugin API
378
+ * that scans tool inputs, tool outputs, and generated content.
379
+ *
380
+ * Usage:
381
+ * const { shieldGoogleADKJS } = require('agentshield-sdk/src/integrations-frameworks');
382
+ * const plugin = shieldGoogleADKJS({ blockOnThreat: true });
383
+ *
384
+ * // Register with ADK-JS agent:
385
+ * const agent = new Agent({ plugins: [plugin] });
386
+ *
387
+ * @param {object} [options]
388
+ * @param {string} [options.sensitivity='high'] - Detection sensitivity level.
389
+ * @param {boolean} [options.blockOnThreat=true] - Whether to throw on threat detection.
390
+ * @param {string} [options.blockThreshold='high'] - Minimum severity that triggers a block.
391
+ * @param {function} [options.onThreat] - Callback when a threat is detected.
392
+ * @returns {{ name: string, beforeToolCall: function, afterToolCall: function, beforeModelCall: function, afterModelCall: function, shield: AgentShield }}
393
+ */
394
+ function shieldGoogleADKJS(options = {}) {
395
+ const shield = new AgentShield({
396
+ sensitivity: options.sensitivity || 'high',
397
+ blockOnThreat: options.blockOnThreat !== false,
398
+ blockThreshold: options.blockThreshold || 'high'
399
+ });
400
+ const onThreat = options.onThreat || null;
401
+
402
+ function _scan(text, phase, meta) {
403
+ if (!text) return;
404
+ const result = shield.scanInput(text);
405
+ if (result.threats && result.threats.length > 0) {
406
+ if (onThreat) {
407
+ try { onThreat({ phase, ...meta, threats: result.threats }); }
408
+ catch (e) { console.error('[Agent Shield] onThreat callback error:', e.message); }
409
+ }
410
+ if (result.blocked) {
411
+ throw new ShieldBlockError(`Google ADK-JS ${phase} blocked by Agent Shield`, result.threats);
412
+ }
413
+ }
414
+ }
415
+
416
+ return {
417
+ name: 'AgentShieldPlugin',
418
+
419
+ beforeToolCall(context) {
420
+ const text = context.args != null
421
+ ? (typeof context.args === 'string' ? context.args : JSON.stringify(context.args))
422
+ : null;
423
+ _scan(text, 'before_tool_call', { toolName: context.toolName || 'unknown' });
424
+ },
425
+
426
+ afterToolCall(context) {
427
+ const text = context.result != null
428
+ ? (typeof context.result === 'string' ? context.result : JSON.stringify(context.result))
429
+ : null;
430
+ _scan(text, 'after_tool_call', { toolName: context.toolName || 'unknown' });
431
+ },
432
+
433
+ beforeModelCall(context) {
434
+ const text = context.prompt || context.input;
435
+ if (text) _scan(typeof text === 'string' ? text : JSON.stringify(text), 'before_model_call', {});
436
+ },
437
+
438
+ afterModelCall(context) {
439
+ const text = context.response || context.output;
440
+ if (!text) return;
441
+ const outputText = typeof text === 'string' ? text : JSON.stringify(text);
442
+ const result = shield.scanOutput(outputText);
443
+ if (result.threats && result.threats.length > 0) {
444
+ if (onThreat) {
445
+ try { onThreat({ phase: 'after_model_call', threats: result.threats }); }
446
+ catch (e) { console.error('[Agent Shield] onThreat callback error:', e.message); }
447
+ }
448
+ if (result.blocked) {
449
+ throw new ShieldBlockError('Google ADK-JS model output blocked by Agent Shield', result.threats);
450
+ }
451
+ }
452
+ },
453
+
454
+ shield
455
+ };
456
+ }
457
+
369
458
  module.exports = {
370
459
  shieldCrewAI,
371
460
  shieldGoogleADK,
461
+ shieldGoogleADKJS,
372
462
  shieldMSAgentFramework
373
463
  };
package/src/main.js CHANGED
@@ -84,7 +84,7 @@ const { BenchmarkHarness, DatasetLoader, BenchmarkMetrics, RegressionTracker, Be
84
84
  const { ShieldCallbackHandler, shieldAnthropicClient, shieldOpenAIClient, shieldOpenAIAgent, shieldVercelAI, shieldFetch, ShieldBlockError } = safeRequire('./integrations', 'integrations');
85
85
 
86
86
  // Framework Integrations (CrewAI, Google ADK, MS Agent Framework)
87
- const { shieldCrewAI, shieldGoogleADK, shieldMSAgentFramework } = safeRequire('./integrations-frameworks', 'integrations-frameworks');
87
+ const { shieldCrewAI, shieldGoogleADK, shieldGoogleADKJS, shieldMSAgentFramework } = safeRequire('./integrations-frameworks', 'integrations-frameworks');
88
88
 
89
89
  // Red Team
90
90
  const { AttackSimulator, PayloadFuzzer, getAttackCategories, getPayloads, ATTACK_PAYLOADS } = safeRequire('./redteam', 'redteam');
@@ -498,6 +498,7 @@ const _exports = {
498
498
  // Framework Integrations (CrewAI, Google ADK, MS Agent Framework)
499
499
  shieldCrewAI,
500
500
  shieldGoogleADK,
501
+ shieldGoogleADKJS,
501
502
  shieldMSAgentFramework,
502
503
 
503
504
  // Red Team
package/src/mcp-guard.js CHANGED
@@ -61,6 +61,7 @@ const MODEL_RISK_PROFILES = {
61
61
  'gemini-2.5': { riskMultiplier: 1.2, susceptibility: 'high', notes: 'Advanced capability increases risk' },
62
62
  'llama-4': { riskMultiplier: 1.1, susceptibility: 'medium', notes: 'Early fusion architecture increases multimodal attack surface' },
63
63
  'deepseek-r1': { riskMultiplier: 1.3, susceptibility: 'high', notes: 'Nature: LRMs achieve 97% jailbreak success as autonomous agents' },
64
+ 'gpt-5.5': { riskMultiplier: 1.4, susceptibility: 'critical', notes: 'April 2026 agentic model — elevated sandbox escape and tool-use attack surface' },
64
65
  default: { riskMultiplier: 1.0, susceptibility: 'medium', notes: 'Unknown model — default risk level' }
65
66
  };
66
67
 
@@ -258,10 +259,25 @@ class CrossServerIsolation {
258
259
  * @param {string[]} toolNames
259
260
  */
260
261
  registerServer(serverId, toolNames) {
262
+ const collisions = [];
263
+ for (const name of toolNames) {
264
+ const existingOwner = this.toolOwnership.get(name);
265
+ if (existingOwner && existingOwner !== serverId) {
266
+ collisions.push({ tool: name, existingServer: existingOwner, newServer: serverId });
267
+ }
268
+ }
261
269
  this.serverTools.set(serverId, new Set(toolNames));
262
270
  for (const name of toolNames) {
263
271
  this.toolOwnership.set(name, serverId);
264
272
  }
273
+ if (collisions.length > 0) {
274
+ return {
275
+ collisions,
276
+ severity: 'critical',
277
+ message: `Tool name squatting detected: ${collisions.map(c => `"${c.tool}" (owned by ${c.existingServer}, overridden by ${c.newServer})`).join(', ')}`
278
+ };
279
+ }
280
+ return null;
265
281
  }
266
282
 
267
283
  /**
@@ -639,8 +655,14 @@ class MCPGuard {
639
655
  this._chainTracker = [];
640
656
  this._chainMaxLen = 50;
641
657
 
658
+ /** Recursive tool invocation depth tracker (per-request) */
659
+ this._callDepth = new Map();
660
+ this._maxCallDepth = options.maxCallDepth || 5;
661
+
642
662
  /** Agent fleet registry — tracks all known agents in the deployment */
643
663
  this._agentRegistry = new Map();
664
+
665
+ this.config = options;
644
666
  }
645
667
 
646
668
  // -----------------------------------------------------------------------
@@ -910,10 +932,26 @@ class MCPGuard {
910
932
  * @param {*} args - Tool arguments.
911
933
  * @returns {{ allowed: boolean, threats: Array<object>, anomalies: Array<object> }}
912
934
  */
913
- interceptToolCall(serverId, toolName, args) {
935
+ interceptToolCall(serverId, toolName, args, requestId) {
914
936
  const threats = [];
915
937
  const anomalies = [];
916
938
 
939
+ // Recursive tool invocation depth check
940
+ if (requestId) {
941
+ const depth = (this._callDepth.get(requestId) || 0) + 1;
942
+ this._callDepth.set(requestId, depth);
943
+ if (depth > this._maxCallDepth) {
944
+ threats.push({
945
+ type: 'recursive_tool_invocation',
946
+ severity: 'high',
947
+ serverId,
948
+ toolName,
949
+ description: `Tool call depth ${depth} exceeds max ${this._maxCallDepth}. Possible recursive loop or reentrancy attack.`
950
+ });
951
+ return { allowed: false, threats, anomalies };
952
+ }
953
+ }
954
+
917
955
  // Circuit breaker check
918
956
  const cbCheck = this._checkCircuitBreaker(serverId);
919
957
  if (!cbCheck.allowed) {
@@ -1188,6 +1226,19 @@ class MCPGuard {
1188
1226
 
1189
1227
  // Scan output
1190
1228
  const outputStr = typeof output === 'string' ? output : JSON.stringify(output || {});
1229
+
1230
+ // Context flooding defense: flag oversized tool outputs that could push
1231
+ // legitimate instructions out of the context window
1232
+ const maxOutputSize = this.config.maxToolOutputSize || 100_000;
1233
+ if (outputStr.length > maxOutputSize) {
1234
+ threats.push({
1235
+ type: 'context_flooding',
1236
+ severity: 'high',
1237
+ serverId,
1238
+ toolName,
1239
+ description: `Tool output exceeds max size (${outputStr.length} > ${maxOutputSize} chars). Possible context window flooding attack.`
1240
+ });
1241
+ }
1191
1242
  const scanResult = this.scanner(outputStr);
1192
1243
  if (scanResult.threats && scanResult.threats.length > 0) {
1193
1244
  for (const t of scanResult.threats) {
@@ -51,6 +51,18 @@ const KNOWN_BAD_SERVERS = Object.freeze({
51
51
  'flowise-unpatched': {
52
52
  reason: 'CVSS 10.0 RCE actively exploited (CVE-2025-59528)',
53
53
  severity: 'critical'
54
+ },
55
+ 'lmdeploy-unpatched': {
56
+ reason: 'SSRF via vision-language image loader, exploited within 12 hours (CVE-2026-33626)',
57
+ severity: 'high'
58
+ },
59
+ 'nginx-ui-mcp': {
60
+ reason: 'Auth bypass on MCP-integrated HTTP endpoints, actively exploited (CVE-2026-33032)',
61
+ severity: 'critical'
62
+ },
63
+ 'splunk-mcp-server': {
64
+ reason: 'Auth tokens logged in cleartext (CVE-2026-20205)',
65
+ severity: 'high'
54
66
  }
55
67
  });
56
68
 
@@ -207,6 +219,18 @@ const CVE_REGISTRY = Object.freeze({
207
219
  description: 'Code injection in Flowise MCP node (CVSS 10.0) allows remote code execution. 12,000-15,000 instances exposed. Actively exploited since April 6.',
208
220
  fix: 'Upgrade Flowise to >=3.0.6. Restrict access to MCP node.'
209
221
  },
222
+ {
223
+ cve: 'CVE-2026-40933',
224
+ severity: 'critical',
225
+ description: 'Flowise MCP Adapters authenticated RCE (CVSS 9.9). Unsafe stdio command serialization in MCP adapter enables OS command execution via npx -c.',
226
+ fix: 'Upgrade Flowise to >=3.1.0.'
227
+ },
228
+ {
229
+ cve: 'CVE-2026-41264',
230
+ severity: 'high',
231
+ description: 'Flowise CSV Agent prompt injection to RCE. LLM-generated Python script executed without sandbox. No auth required.',
232
+ fix: 'Upgrade Flowise to >=3.1.0.'
233
+ },
210
234
  {
211
235
  cve: 'CVE-2025-8943',
212
236
  severity: 'critical',
@@ -243,6 +267,122 @@ const CVE_REGISTRY = Object.freeze({
243
267
  description: 'OS command injection RCE in codebase-mcp.',
244
268
  fix: 'Upgrade codebase-mcp. Never pass unsanitized inputs to shell.'
245
269
  }
270
+ ],
271
+ 'lmdeploy': [
272
+ {
273
+ cve: 'CVE-2026-33626',
274
+ severity: 'high',
275
+ description: 'LMDeploy SSRF via vision-language image loader (CVSS 7.5). load_image() fetches arbitrary URLs, enabling port scanning, IMDS access (169.254.169.254), and internal service probing. Exploited within 12 hours of disclosure.',
276
+ fix: 'Upgrade LMDeploy to >=0.12.3. Block private IP ranges and cloud metadata endpoints in image URLs.'
277
+ }
278
+ ],
279
+ 'nginx-ui': [
280
+ {
281
+ cve: 'CVE-2026-33032',
282
+ severity: 'critical',
283
+ description: 'nginx-ui auth bypass on MCP-integrated HTTP endpoints (CVSS 9.8). Actively exploited.',
284
+ fix: 'Apply nginx-ui patch. Enable authentication on all MCP endpoints.'
285
+ }
286
+ ],
287
+ 'splunk-mcp-server': [
288
+ {
289
+ cve: 'CVE-2026-20205',
290
+ severity: 'high',
291
+ description: 'Splunk MCP Server logs session/auth tokens in cleartext in _internal index (CVSS 7.2).',
292
+ fix: 'Upgrade Splunk MCP Server to >=1.0.3. Audit _internal index for leaked tokens.'
293
+ }
294
+ ],
295
+ 'mcp-ruby-sdk': [
296
+ {
297
+ cve: 'CVE-2026-33946',
298
+ severity: 'medium',
299
+ description: 'MCP Ruby SDK session fixation in SSE stream allows hijacking of MCP protocol communications.',
300
+ fix: 'Upgrade MCP Ruby SDK to >=0.9.2.'
301
+ }
302
+ ],
303
+ 'magento2-dev-mcp': [
304
+ {
305
+ cve: 'CVE-2026-5603',
306
+ severity: 'high',
307
+ description: 'Command injection in @elgentos/magento2-dev-mcp via child_process.execAsync with unsanitized input.',
308
+ fix: 'Upgrade magento2-dev-mcp. Sanitize all inputs before passing to child_process.'
309
+ }
310
+ ],
311
+ 'semantic-kernel': [
312
+ {
313
+ cve: 'CVE-2026-25592',
314
+ severity: 'high',
315
+ description: 'Microsoft Semantic Kernel .NET SDK <1.71.0: prompt injection invokes arbitrary kernel functions leading to RCE on host process. Disclosed by MSRC May 7, 2026.',
316
+ fix: 'Upgrade Semantic Kernel .NET SDK to >=1.71.0. Validate function names in kernel.invoke() calls.'
317
+ },
318
+ {
319
+ cve: 'CVE-2026-26030',
320
+ severity: 'high',
321
+ description: 'Microsoft Semantic Kernel Python <1.39.4: same RCE primitive as CVE-2026-25592 but in Python SDK.',
322
+ fix: 'Upgrade Semantic Kernel Python to >=1.39.4.'
323
+ }
324
+ ],
325
+ 'fastgpt': [
326
+ {
327
+ cve: 'CVE-2026-42302',
328
+ severity: 'critical',
329
+ description: 'FastGPT agent-sandbox unauthenticated RCE (CVSS 9.8). code-server launched with --auth none on 0.0.0.0:8080. Published May 8, 2026.',
330
+ fix: 'Upgrade FastGPT to >=4.14.13. Never expose agent-sandbox endpoints without authentication.'
331
+ },
332
+ {
333
+ cve: 'CVE-2026-44284',
334
+ severity: 'high',
335
+ description: 'FastGPT SSRF in MCP tool URL handling. Crafted URLs reach internal services and cloud metadata endpoints.',
336
+ fix: 'Upgrade FastGPT. Apply Agent Shield mcp-guard SSRF firewall.'
337
+ },
338
+ {
339
+ cve: 'CVE-2026-42344',
340
+ severity: 'high',
341
+ description: 'FastGPT DNS rebinding bypasses isInternalAddress() check. Attacker hostname resolves to internal IP after initial validation.',
342
+ fix: 'Upgrade FastGPT. Pin DNS resolution per request and re-validate on connect.'
343
+ }
344
+ ],
345
+ 'cline-kanban': [
346
+ {
347
+ cve: 'CVE-2026-44211',
348
+ severity: 'high',
349
+ description: 'Cline Kanban Server Cross-Origin WebSocket Hijacking. Missing origin validation lets attackers inject prompts into running agent terminals.',
350
+ fix: 'Upgrade Cline. Validate Origin header on WebSocket upgrade requests.'
351
+ }
352
+ ],
353
+ 'azure-sre-agent': [
354
+ {
355
+ cve: 'CVE-2026-32173',
356
+ severity: 'high',
357
+ description: 'Azure SRE Agent exposed live command streams over unauthenticated WebSocket to any Entra ID user (CVSS 8.6).',
358
+ fix: 'Apply Azure patch. Restrict WebSocket access to authorized principals only.'
359
+ }
360
+ ],
361
+ 'crewai': [
362
+ {
363
+ cve: 'CVE-2026-44400',
364
+ severity: 'high',
365
+ description: 'CrewAI Code Interpreter default enabled allows prompt injection → RCE chain.',
366
+ fix: 'Upgrade CrewAI to latest. Disable Code Interpreter by default. Apply Agent Shield shieldCrewAI wrapper.'
367
+ },
368
+ {
369
+ cve: 'CVE-2026-44401',
370
+ severity: 'high',
371
+ description: 'CrewAI Code Interpreter SSRF via prompt-injected URLs.',
372
+ fix: 'Upgrade CrewAI. Block private IP ranges in tool URLs.'
373
+ },
374
+ {
375
+ cve: 'CVE-2026-44402',
376
+ severity: 'high',
377
+ description: 'CrewAI Code Interpreter file-read primitive via prompt injection.',
378
+ fix: 'Upgrade CrewAI. Restrict filesystem access in Code Interpreter sandbox.'
379
+ },
380
+ {
381
+ cve: 'CVE-2026-44403',
382
+ severity: 'high',
383
+ description: 'CrewAI agent task chain prompt injection leads to unauthorized tool invocation.',
384
+ fix: 'Upgrade CrewAI. Use Agent Shield intent-firewall to validate cross-task transitions.'
385
+ }
246
386
  ]
247
387
  });
248
388
 
@@ -378,6 +518,7 @@ class SupplyChainScanner {
378
518
  this._scanSchema(tool, findings);
379
519
  this._scanForSSRF(tool, findings);
380
520
  this._scanForClawHavoc(tool, findings);
521
+ this._scanConsentPhishing(tool, findings);
381
522
  }
382
523
 
383
524
  // Analyze escalation chains
@@ -687,6 +828,29 @@ class SupplyChainScanner {
687
828
  }
688
829
  }
689
830
 
831
+ /** @private - Consent phishing: detect tools whose description misrepresents capabilities (OWASP ASI09) */
832
+ _scanConsentPhishing(tool, findings) {
833
+ if (!tool || !tool.description || !tool.inputSchema) return;
834
+ const desc = String(tool.description).toLowerCase();
835
+ const schemaStr = JSON.stringify(tool.inputSchema).toLowerCase();
836
+
837
+ const READ_WORDS = ['read', 'get', 'fetch', 'list', 'view', 'show', 'display', 'search', 'query', 'lookup'];
838
+ const WRITE_INDICATORS = ['"url"', '"endpoint"', '"host"', '"target"', '"destination"', '"webhook"', '"callback"', 'http', '"command"', '"exec"', '"shell"', '"script"'];
839
+ const BENIGN_WORDS = ['save', 'update', 'create', 'write', 'delete', 'send', 'post', 'execute', 'run', 'upload'];
840
+
841
+ const descLooksReadOnly = READ_WORDS.some(w => desc.includes(w)) && !BENIGN_WORDS.some(w => desc.includes(w));
842
+ const schemaHasWriteCapability = WRITE_INDICATORS.some(w => schemaStr.includes(w));
843
+
844
+ if (descLooksReadOnly && schemaHasWriteCapability) {
845
+ findings.push({
846
+ type: 'consent_phishing',
847
+ severity: 'high',
848
+ message: `Tool "${tool.name || 'unknown'}" description implies read-only ("${desc.substring(0, 60)}...") but schema contains write/network parameters. Users may approve dangerous actions unknowingly.`,
849
+ recommendation: 'Tool descriptions must accurately reflect capabilities. If the tool sends data to URLs or executes commands, the description must say so explicitly.'
850
+ });
851
+ }
852
+ }
853
+
690
854
  /** @private */
691
855
  _scanSchema(tool, findings) {
692
856
  if (tool && tool.inputSchema && tool.inputSchema.additionalProperties === true) {