agentshield-sdk 13.2.0 → 13.5.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 +79 -0
- package/README.md +260 -1187
- package/package.json +2 -2
- package/src/audit-immutable.js +59 -1
- package/src/audit.js +1 -1
- package/src/cross-turn.js +25 -1
- package/src/detector-core.js +198 -0
- package/src/document-scanner.js +20 -0
- package/src/main.js +22 -0
- package/src/memory-guard.js +60 -0
- package/src/render-differential.js +608 -0
- package/src/side-channel-monitor.js +560 -0
- package/src/supply-chain-scanner.js +112 -2
- package/src/sybil-detector.js +526 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentshield-sdk",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.5.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",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"sideEffects": false,
|
|
34
34
|
"scripts": {
|
|
35
|
-
"test": "node test/test.js && node test/test-modules.js && node test/test-new-features.js && node test/test-mcp-guard.js && node test/test-supply-chain-scanner.js && node test/test-owasp-agentic.js && node test/test-redteam-cli.js && node test/test-drift-monitor.js && node test/test-micro-model.js && node test/test-level5.js && node test/test-sota.js && node test/test-cross-turn.js && node test/test-v12.js && node test/test-traps.js && node test/test-deepmind.js",
|
|
35
|
+
"test": "node test/test.js && node test/test-modules.js && node test/test-new-features.js && node test/test-mcp-guard.js && node test/test-supply-chain-scanner.js && node test/test-owasp-agentic.js && node test/test-redteam-cli.js && node test/test-drift-monitor.js && node test/test-micro-model.js && node test/test-level5.js && node test/test-sota.js && node test/test-cross-turn.js && node test/test-v12.js && node test/test-traps.js && node test/test-deepmind.js && node test/test-render-differential.js && node test/test-sybil.js && node test/test-side-channel.js",
|
|
36
36
|
"test:new-products": "node test/test-mcp-guard.js && node test/test-supply-chain-scanner.js && node test/test-owasp-agentic.js && node test/test-redteam-cli.js && node test/test-drift-monitor.js && node test/test-micro-model.js",
|
|
37
37
|
"test:all": "node test/test-all-40-features.js",
|
|
38
38
|
"test:mcp": "node test/test-mcp-security.js",
|
package/src/audit-immutable.js
CHANGED
|
@@ -584,8 +584,10 @@ class ImmutableAuditLog {
|
|
|
584
584
|
* @param {number} [options.maxAge=0] - Maximum age in milliseconds (0 = unlimited).
|
|
585
585
|
* @param {function} [options.archiveCallback] - Called with removed entries during retention enforcement. Signature: (entries: AuditEntry[]) => void.
|
|
586
586
|
* @param {string} [options.genesisHash] - Custom genesis hash (defaults to GENESIS_HASH).
|
|
587
|
+
* @param {boolean} [options.sanitizeLogs=false] - Redact sensitive content (emails, SSNs, API keys) before writing to the chain.
|
|
587
588
|
*/
|
|
588
589
|
constructor(options = {}) {
|
|
590
|
+
this.options = options;
|
|
589
591
|
this._store = options.store || new MemoryStore();
|
|
590
592
|
this._maxEntries = options.maxEntries || 0;
|
|
591
593
|
this._maxAge = options.maxAge || 0;
|
|
@@ -598,6 +600,59 @@ class ImmutableAuditLog {
|
|
|
598
600
|
console.log('[Agent Shield] ImmutableAuditLog initialized (store: %s)', this._store.constructor.name);
|
|
599
601
|
}
|
|
600
602
|
|
|
603
|
+
/**
|
|
604
|
+
* Sanitize an entry's data object by redacting sensitive content.
|
|
605
|
+
* Addresses the security scan finding about audit logs containing sensitive prompt data.
|
|
606
|
+
*
|
|
607
|
+
* Redacts:
|
|
608
|
+
* - Email addresses -> [EMAIL_REDACTED]
|
|
609
|
+
* - SSN patterns (XXX-XX-XXXX) -> [SSN_REDACTED]
|
|
610
|
+
* - API key patterns (sk-..., key-..., token-...) -> [KEY_REDACTED]
|
|
611
|
+
* - Truncates 'content' and 'input' fields to 500 characters max
|
|
612
|
+
*
|
|
613
|
+
* @param {object} entry - The data object to sanitize.
|
|
614
|
+
* @returns {object} A sanitized copy of the data object.
|
|
615
|
+
*/
|
|
616
|
+
sanitize(entry) {
|
|
617
|
+
if (!this.options.sanitizeLogs) {
|
|
618
|
+
return entry;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const sanitized = JSON.parse(JSON.stringify(entry));
|
|
622
|
+
|
|
623
|
+
const redactString = (str) => {
|
|
624
|
+
if (typeof str !== 'string') return str;
|
|
625
|
+
// Redact email addresses
|
|
626
|
+
str = str.replace(/[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/g, '[EMAIL_REDACTED]');
|
|
627
|
+
// Redact SSN patterns (XXX-XX-XXXX)
|
|
628
|
+
str = str.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN_REDACTED]');
|
|
629
|
+
// Redact API key patterns (sk-..., key-..., token-...)
|
|
630
|
+
str = str.replace(/\b(?:sk|key|token)-[a-zA-Z0-9_\-]{8,}\b/g, '[KEY_REDACTED]');
|
|
631
|
+
return str;
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const redactObject = (obj) => {
|
|
635
|
+
if (obj === null || obj === undefined) return obj;
|
|
636
|
+
if (typeof obj === 'string') return redactString(obj);
|
|
637
|
+
if (Array.isArray(obj)) return obj.map(item => redactObject(item));
|
|
638
|
+
if (typeof obj === 'object') {
|
|
639
|
+
const result = {};
|
|
640
|
+
for (const key of Object.keys(obj)) {
|
|
641
|
+
let value = redactObject(obj[key]);
|
|
642
|
+
// Truncate content and input fields to 500 chars
|
|
643
|
+
if ((key === 'content' || key === 'input') && typeof value === 'string' && value.length > 500) {
|
|
644
|
+
value = value.slice(0, 500) + '...[TRUNCATED]';
|
|
645
|
+
}
|
|
646
|
+
result[key] = value;
|
|
647
|
+
}
|
|
648
|
+
return result;
|
|
649
|
+
}
|
|
650
|
+
return obj;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
return redactObject(sanitized);
|
|
654
|
+
}
|
|
655
|
+
|
|
601
656
|
/**
|
|
602
657
|
* Initialize sequence counter from existing store data.
|
|
603
658
|
* @private
|
|
@@ -646,12 +701,15 @@ class ImmutableAuditLog {
|
|
|
646
701
|
delete normalizedActor.name;
|
|
647
702
|
}
|
|
648
703
|
|
|
704
|
+
// Sanitize data if sanitizeLogs is enabled
|
|
705
|
+
const sanitizedData = this.sanitize(data || {});
|
|
706
|
+
|
|
649
707
|
const entryContent = {
|
|
650
708
|
id,
|
|
651
709
|
sequence: this._sequence,
|
|
652
710
|
timestamp,
|
|
653
711
|
type,
|
|
654
|
-
data:
|
|
712
|
+
data: sanitizedData,
|
|
655
713
|
actor: normalizedActor
|
|
656
714
|
};
|
|
657
715
|
|
package/src/audit.js
CHANGED
package/src/cross-turn.js
CHANGED
|
@@ -29,6 +29,12 @@ const ESCALATION_SIGNALS = [
|
|
|
29
29
|
/(?:one\s+more\s+(?:thing|question|step)|just\s+one\s+(?:last|final)\s+(?:thing|question))/i,
|
|
30
30
|
/(?:can\s+you\s+go\s+(?:a\s+little\s+)?(?:further|deeper)|take\s+(?:it|this)\s+(?:a\s+step\s+)?further)/i,
|
|
31
31
|
/(?:you\s+(?:just|already)\s+(?:showed|told|gave)\s+me|you\s+(?:were|seemed)\s+(?:willing|happy)\s+to)/i,
|
|
32
|
+
// Crescendo attack patterns — benign start that gradually escalates
|
|
33
|
+
/(?:hypothetically|in\s+theory|for\s+(?:educational|research|academic)\s+purposes)/i,
|
|
34
|
+
/(?:let's\s+(?:say|imagine|pretend)|what\s+if\s+(?:someone|a\s+person|we))/i,
|
|
35
|
+
/(?:I\s+(?:know|understand)\s+you\s+(?:can't|shouldn't)\s+but|normally\s+you\s+(?:wouldn't|can't)\s+but)/i,
|
|
36
|
+
/(?:you\s+(?:helped|assisted)\s+(?:me|us)\s+(?:before|last\s+time|earlier)\s+with\s+(?:something\s+)?similar)/i,
|
|
37
|
+
/(?:this\s+is\s+(?:similar|related)\s+to\s+what\s+(?:you|we)\s+(?:did|discussed))/i,
|
|
32
38
|
];
|
|
33
39
|
|
|
34
40
|
/** Trust erosion patterns — attempts to lower the model's guard over turns. */
|
|
@@ -165,7 +171,25 @@ class ConversationTracker {
|
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
173
|
|
|
168
|
-
// 5.
|
|
174
|
+
// 5. Crescendo detection — benign conversation gradually introduces sensitive framing
|
|
175
|
+
if (role === 'user' && this.turns.length >= 5) {
|
|
176
|
+
const window = this.turns.slice(-6, -1).filter(t => t.role === 'user');
|
|
177
|
+
const hypotheticalCount = window.filter(t =>
|
|
178
|
+
/(?:hypothetically|in\s+theory|let's\s+(?:say|imagine)|what\s+if|for\s+(?:educational|research)\s+purposes)/i.test(t.content)
|
|
179
|
+
).length;
|
|
180
|
+
if (hypotheticalCount >= 2 && (topic === 'sensitive' || topic === 'dangerous')) {
|
|
181
|
+
turnAlerts.push({
|
|
182
|
+
type: 'crescendo_attack',
|
|
183
|
+
severity: 'high',
|
|
184
|
+
turnIndex: turn.turnIndex,
|
|
185
|
+
hypotheticalCount,
|
|
186
|
+
currentTopic: topic,
|
|
187
|
+
description: `Crescendo pattern: ${hypotheticalCount} hypothetical/theoretical framings followed by ${topic} topic. Gradual normalization of sensitive requests.`
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 6. Authority accumulation — user references previous "agreements"
|
|
169
193
|
if (role === 'user' && /(?:you\s+(?:said|agreed|confirmed|told\s+me)|as\s+we\s+(?:discussed|agreed)|per\s+our\s+(?:agreement|conversation))/i.test(content)) {
|
|
170
194
|
const hasRealAgreement = this.turns.some(t => t.role === 'assistant' && /(?:sure|yes|okay|of\s+course|I\s+(?:can|will))/i.test(t.content));
|
|
171
195
|
if (!hasRealAgreement) {
|
package/src/detector-core.js
CHANGED
|
@@ -2206,6 +2206,204 @@ const INJECTION_PATTERNS = [
|
|
|
2206
2206
|
category: 'indirect_injection',
|
|
2207
2207
|
description: 'Text contains a "note to AI" directive hidden in external content.',
|
|
2208
2208
|
detail: 'Annotation injection: uses "note to AI" framing to inject instructions into tool output or document content.'
|
|
2209
|
+
},
|
|
2210
|
+
|
|
2211
|
+
// --- XSS in Agent Output ---
|
|
2212
|
+
{
|
|
2213
|
+
regex: /<script[^>]*>.*?<\/script>/is,
|
|
2214
|
+
severity: 'high',
|
|
2215
|
+
category: 'xss_injection',
|
|
2216
|
+
description: 'Detects script tag XSS payloads embedded in AI agent output.',
|
|
2217
|
+
detail: 'Script tag injection: attackers embed XSS in prompt injections so AI-generated HTML executes malicious code in downstream consumers.'
|
|
2218
|
+
},
|
|
2219
|
+
{
|
|
2220
|
+
regex: /on(error|load|click|mouseover)\s*=\s*["'][^"']*["']/i,
|
|
2221
|
+
severity: 'high',
|
|
2222
|
+
category: 'xss_injection',
|
|
2223
|
+
description: 'Detects event handler XSS payloads embedded in AI agent output.',
|
|
2224
|
+
detail: 'Event handler injection: attackers embed XSS event handlers in prompt injections so AI-generated HTML executes malicious code on user interaction.'
|
|
2225
|
+
},
|
|
2226
|
+
{
|
|
2227
|
+
regex: /javascript\s*:/i,
|
|
2228
|
+
severity: 'high',
|
|
2229
|
+
category: 'xss_injection',
|
|
2230
|
+
description: 'Detects JavaScript URI scheme XSS payloads embedded in AI agent output.',
|
|
2231
|
+
detail: 'JavaScript URI injection: attackers embed javascript: URIs in prompt injections so AI-generated links execute malicious code when clicked.'
|
|
2232
|
+
},
|
|
2233
|
+
{
|
|
2234
|
+
regex: /<iframe[^>]*src\s*=\s*["'](?!about:blank)/i,
|
|
2235
|
+
severity: 'high',
|
|
2236
|
+
category: 'xss_injection',
|
|
2237
|
+
description: 'Detects iframe injection with external source in AI agent output.',
|
|
2238
|
+
detail: 'Iframe injection: attackers embed iframes with external sources in prompt injections so AI-generated HTML loads malicious content from attacker-controlled domains.'
|
|
2239
|
+
},
|
|
2240
|
+
{
|
|
2241
|
+
regex: /<img[^>]*onerror\s*=/i,
|
|
2242
|
+
severity: 'high',
|
|
2243
|
+
category: 'xss_injection',
|
|
2244
|
+
description: 'Detects image error handler XSS payloads embedded in AI agent output.',
|
|
2245
|
+
detail: 'Image onerror injection: attackers embed img tags with onerror handlers in prompt injections so AI-generated HTML executes malicious code when the image fails to load.'
|
|
2246
|
+
},
|
|
2247
|
+
|
|
2248
|
+
// --- Acrostic / Steganographic Injection ---
|
|
2249
|
+
{
|
|
2250
|
+
regex: /^[iI].*\n[gG].*\n[nN].*\n[oO].*\n[rR].*\n[eE]/m,
|
|
2251
|
+
severity: 'medium',
|
|
2252
|
+
category: 'steganographic_injection',
|
|
2253
|
+
description: 'Detects hidden instructions spelled out across line-initial characters (acrostic attacks spelling "ignore").',
|
|
2254
|
+
detail: 'Acrostic injection: researchers demonstrated 93% evasion success rate with steganographic techniques where first characters of consecutive lines spell out injection keywords like "ignore".'
|
|
2255
|
+
},
|
|
2256
|
+
{
|
|
2257
|
+
regex: /^[sS].*\n[yY].*\n[sS].*\n[tT].*\n[eE].*\n[mM]/m,
|
|
2258
|
+
severity: 'medium',
|
|
2259
|
+
category: 'steganographic_injection',
|
|
2260
|
+
description: 'Detects hidden instructions spelled out across line-initial characters (acrostic attacks spelling "system").',
|
|
2261
|
+
detail: 'Acrostic injection: researchers demonstrated 93% evasion success rate with steganographic techniques where first characters of consecutive lines spell out injection keywords like "system".'
|
|
2262
|
+
},
|
|
2263
|
+
|
|
2264
|
+
// --- MCP Config Command Injection ---
|
|
2265
|
+
{
|
|
2266
|
+
regex: /mcp\.json.*[;&|`$]/i,
|
|
2267
|
+
severity: 'critical',
|
|
2268
|
+
category: 'mcp_config_injection',
|
|
2269
|
+
description: 'Detects command injection in MCP configuration files (CVE-2026-21518).',
|
|
2270
|
+
detail: 'MCP config injection: attackers inject shell metacharacters into mcp.json references to achieve command execution via the VS Code mcp.json attack vector.'
|
|
2271
|
+
},
|
|
2272
|
+
{
|
|
2273
|
+
regex: /"(?:command|args)":\s*"[^"]*[;&|`$()]/i,
|
|
2274
|
+
severity: 'critical',
|
|
2275
|
+
category: 'mcp_config_injection',
|
|
2276
|
+
description: 'Detects command injection in MCP tool configuration fields (CVE-2026-21518).',
|
|
2277
|
+
detail: 'MCP tool config injection: attackers inject shell metacharacters into MCP tool command/args fields to achieve command execution via the VS Code mcp.json attack vector.'
|
|
2278
|
+
},
|
|
2279
|
+
|
|
2280
|
+
// --- Offensive Agent Behavior ---
|
|
2281
|
+
{
|
|
2282
|
+
regex: /(?:scan|enumerate|exploit|pivot|lateral\s*move|exfiltrate).*(?:target|victim|host|network|server)/i,
|
|
2283
|
+
severity: 'critical',
|
|
2284
|
+
category: 'offensive_agent',
|
|
2285
|
+
description: 'Detects AI agents being used as attack tools for automated exploitation.',
|
|
2286
|
+
detail: 'Automated exploitation language: references 2026 incidents where AI agents compromised 600+ firewalls autonomously using scan/exploit/pivot chains against target infrastructure.'
|
|
2287
|
+
},
|
|
2288
|
+
{
|
|
2289
|
+
regex: /(?:reverse\s*shell|bind\s*shell|c2|command\s*and\s*control|beacon)/i,
|
|
2290
|
+
severity: 'critical',
|
|
2291
|
+
category: 'offensive_agent',
|
|
2292
|
+
description: 'Detects AI agents being instructed to set up C2 or attack infrastructure.',
|
|
2293
|
+
detail: 'C2/attack infrastructure: references 2026 incidents where AI agents compromised 600+ firewalls autonomously by establishing reverse shells and command-and-control channels.'
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
regex: /(?:dump|harvest|steal)\s*(?:credentials?|passwords?|hashes?|tokens?|keys?)/i,
|
|
2297
|
+
severity: 'critical',
|
|
2298
|
+
category: 'offensive_agent',
|
|
2299
|
+
description: 'Detects AI agents being used for credential theft operations.',
|
|
2300
|
+
detail: 'Credential theft operations: references 2026 incidents where AI agents compromised 600+ firewalls autonomously and harvested credentials for lateral movement.'
|
|
2301
|
+
},
|
|
2302
|
+
|
|
2303
|
+
// --- Cloud IAM Overpermission ---
|
|
2304
|
+
{
|
|
2305
|
+
regex: /"(?:Action|Effect)":\s*"\*"/i,
|
|
2306
|
+
severity: 'high',
|
|
2307
|
+
category: 'cloud_overpermission',
|
|
2308
|
+
description: 'Detects overpermissioned cloud IAM policies with wildcard Action/Effect that enable "Agent God Mode" attacks.',
|
|
2309
|
+
detail: 'IAM wildcard permissions: Palo Alto Unit 42 discovered AWS AgentCore attack where wildcard IAM policies enable cross-agent data access and full account takeover.'
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
regex: /arn:aws:[^"]*:\*/i,
|
|
2313
|
+
severity: 'high',
|
|
2314
|
+
category: 'cloud_overpermission',
|
|
2315
|
+
description: 'Detects AWS ARN references with wildcard resources that enable "Agent God Mode" attacks.',
|
|
2316
|
+
detail: 'AWS ARN wildcard resource: Palo Alto Unit 42 discovered AWS AgentCore attack where wildcard resource ARNs enable cross-agent data access across all resources in a service.'
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
regex: /"Resource":\s*"\*"/i,
|
|
2320
|
+
severity: 'high',
|
|
2321
|
+
category: 'cloud_overpermission',
|
|
2322
|
+
description: 'Detects overpermissioned cloud IAM policies with wildcard Resource that enable "Agent God Mode" attacks.',
|
|
2323
|
+
detail: 'IAM resource wildcard: Palo Alto Unit 42 discovered AWS AgentCore attack where wildcard Resource policies enable cross-agent data access to all AWS resources.'
|
|
2324
|
+
},
|
|
2325
|
+
|
|
2326
|
+
// --- Encoding Chain Detection ---
|
|
2327
|
+
{
|
|
2328
|
+
regex: /(?:atob|decode|base64)\s*\(\s*['"][A-Za-z0-9+\/=]{50,}['"]\s*\)/i,
|
|
2329
|
+
severity: 'high',
|
|
2330
|
+
category: 'encoding_chain',
|
|
2331
|
+
description: 'Detects multi-layer encoding chains used to evade security scanners',
|
|
2332
|
+
detail: 'Encoding chain evasion: attackers nest base64 inside unicode inside URL encoding to bypass single-layer decoders'
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
regex: /\\u[0-9a-fA-F]{4}(?:\\u[0-9a-fA-F]{4}){10,}/,
|
|
2336
|
+
severity: 'medium',
|
|
2337
|
+
category: 'encoding_chain',
|
|
2338
|
+
description: 'Detects multi-layer encoding chains used to evade security scanners',
|
|
2339
|
+
detail: 'Encoding chain evasion: attackers nest base64 inside unicode inside URL encoding to bypass single-layer decoders'
|
|
2340
|
+
},
|
|
2341
|
+
{
|
|
2342
|
+
regex: /(?:%[0-9a-fA-F]{2}){20,}/,
|
|
2343
|
+
severity: 'medium',
|
|
2344
|
+
category: 'encoding_chain',
|
|
2345
|
+
description: 'Detects multi-layer encoding chains used to evade security scanners',
|
|
2346
|
+
detail: 'Encoding chain evasion: attackers nest base64 inside unicode inside URL encoding to bypass single-layer decoders'
|
|
2347
|
+
},
|
|
2348
|
+
|
|
2349
|
+
// --- SVG-Based Injection ---
|
|
2350
|
+
{
|
|
2351
|
+
regex: /<svg[^>]*>[\s\S]*?(?:ignore|override|system|instructions)[\s\S]*?<\/svg>/i,
|
|
2352
|
+
severity: 'high',
|
|
2353
|
+
category: 'svg_injection',
|
|
2354
|
+
description: 'Detects prompt injection hidden in SVG elements',
|
|
2355
|
+
detail: 'SVG injection: Unit 42 found real-world attacks using SVG encapsulation with 24 separate injection attempts layered in zero-sized fonts, off-screen positioning, and CSS suppression'
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
regex: /<foreignObject[^>]*>[\s\S]*?(?:ignore|override|forget|disregard)[\s\S]*?<\/foreignObject>/i,
|
|
2359
|
+
severity: 'high',
|
|
2360
|
+
category: 'svg_injection',
|
|
2361
|
+
description: 'Detects prompt injection hidden in SVG elements',
|
|
2362
|
+
detail: 'SVG injection: Unit 42 found real-world attacks using SVG encapsulation with 24 separate injection attempts layered in zero-sized fonts, off-screen positioning, and CSS suppression'
|
|
2363
|
+
},
|
|
2364
|
+
{
|
|
2365
|
+
regex: /<text[^>]*(?:opacity\s*[:=]\s*0|display\s*[:=]\s*none|font-size\s*[:=]\s*0)[^>]*>/i,
|
|
2366
|
+
severity: 'high',
|
|
2367
|
+
category: 'svg_injection',
|
|
2368
|
+
description: 'Detects prompt injection hidden in SVG elements',
|
|
2369
|
+
detail: 'SVG injection: Unit 42 found real-world attacks using SVG encapsulation with 24 separate injection attempts layered in zero-sized fonts, off-screen positioning, and CSS suppression'
|
|
2370
|
+
},
|
|
2371
|
+
{
|
|
2372
|
+
regex: /<desc[^>]*>[\s\S]*?(?:ignore|system|instruction|override)[\s\S]*?<\/desc>/i,
|
|
2373
|
+
severity: 'medium',
|
|
2374
|
+
category: 'svg_injection',
|
|
2375
|
+
description: 'Detects prompt injection hidden in SVG elements',
|
|
2376
|
+
detail: 'SVG injection: Unit 42 found real-world attacks using SVG encapsulation with 24 separate injection attempts layered in zero-sized fonts, off-screen positioning, and CSS suppression'
|
|
2377
|
+
},
|
|
2378
|
+
|
|
2379
|
+
// --- Structured Data Injection ---
|
|
2380
|
+
{
|
|
2381
|
+
regex: /["'](?:__comment|_note|description|help_text)["']\s*:\s*["'][^"']*(?:ignore|override|system|instructions)[^"']*["']/i,
|
|
2382
|
+
severity: 'high',
|
|
2383
|
+
category: 'structured_data_injection',
|
|
2384
|
+
description: 'Detects prompt injection hidden in structured data formats',
|
|
2385
|
+
detail: 'Structured data injection: agents constantly parse JSON/XML/YAML/CSV and attackers embed instructions in metadata fields, CDATA sections, and comments'
|
|
2386
|
+
},
|
|
2387
|
+
{
|
|
2388
|
+
regex: /<!\[CDATA\[[\s\S]*?(?:ignore|override|system|instructions)[\s\S]*?\]\]>/i,
|
|
2389
|
+
severity: 'high',
|
|
2390
|
+
category: 'structured_data_injection',
|
|
2391
|
+
description: 'Detects prompt injection hidden in structured data formats',
|
|
2392
|
+
detail: 'Structured data injection: agents constantly parse JSON/XML/YAML/CSV and attackers embed instructions in metadata fields, CDATA sections, and comments'
|
|
2393
|
+
},
|
|
2394
|
+
{
|
|
2395
|
+
regex: /^#.*(?:ignore|override|system|instructions)/im,
|
|
2396
|
+
severity: 'medium',
|
|
2397
|
+
category: 'structured_data_injection',
|
|
2398
|
+
description: 'Detects prompt injection hidden in structured data formats',
|
|
2399
|
+
detail: 'Structured data injection: agents constantly parse JSON/XML/YAML/CSV and attackers embed instructions in metadata fields, CDATA sections, and comments'
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
regex: /(?:<!--|\{\{!--|\/\*|#)\s*(?:ignore|override|forget|disregard)\s*(?:all\s+)?(?:previous|prior|above)/i,
|
|
2403
|
+
severity: 'high',
|
|
2404
|
+
category: 'structured_data_injection',
|
|
2405
|
+
description: 'Detects prompt injection hidden in structured data formats',
|
|
2406
|
+
detail: 'Structured data injection: agents constantly parse JSON/XML/YAML/CSV and attackers embed instructions in metadata fields, CDATA sections, and comments'
|
|
2209
2407
|
}
|
|
2210
2408
|
];
|
|
2211
2409
|
|
package/src/document-scanner.js
CHANGED
|
@@ -564,11 +564,13 @@ class DocumentScanner {
|
|
|
564
564
|
* @param {string} [options.sensitivity='medium'] - Detection sensitivity ('low', 'medium', 'high').
|
|
565
565
|
* @param {boolean} [options.logging=false] - Whether to log scan results.
|
|
566
566
|
* @param {boolean} [options.scanForInjection=true] - Whether to run indirect injection scanning.
|
|
567
|
+
* @param {number} [options.maxDocumentSize=104857600] - Maximum document size in characters (default: 100MB). Prevents DoS via oversized documents.
|
|
567
568
|
*/
|
|
568
569
|
constructor(options = {}) {
|
|
569
570
|
this.sensitivity = options.sensitivity || 'medium';
|
|
570
571
|
this.logging = options.logging || false;
|
|
571
572
|
this.scanForInjection = options.scanForInjection !== false;
|
|
573
|
+
this.maxDocumentSize = options.maxDocumentSize || 100 * 1024 * 1024;
|
|
572
574
|
this.injectionScanner = new IndirectInjectionScanner({ sensitivity: this.sensitivity });
|
|
573
575
|
}
|
|
574
576
|
|
|
@@ -682,6 +684,24 @@ class DocumentScanner {
|
|
|
682
684
|
const source = metadata.source || 'text';
|
|
683
685
|
const fileType = metadata.fileType || 'text/plain';
|
|
684
686
|
|
|
687
|
+
// Enforce document size limit to prevent DoS via oversized documents
|
|
688
|
+
if (text && text.length > this.maxDocumentSize) {
|
|
689
|
+
if (this.logging) {
|
|
690
|
+
console.log('[Agent Shield] Document exceeds size limit: %d characters (max: %d)', text.length, this.maxDocumentSize);
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
fileType,
|
|
694
|
+
textLength: text.length,
|
|
695
|
+
threats: [{
|
|
696
|
+
type: 'document_too_large',
|
|
697
|
+
severity: 'medium',
|
|
698
|
+
message: 'Document exceeds size limit'
|
|
699
|
+
}],
|
|
700
|
+
status: 'caution',
|
|
701
|
+
safe: false
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
685
705
|
if (!text || text.trim().length === 0) {
|
|
686
706
|
return {
|
|
687
707
|
fileType,
|
package/src/main.js
CHANGED
|
@@ -215,6 +215,9 @@ const { BehavioralDNA, AgentProfiler, extractFeatures: extractBehavioralFeatures
|
|
|
215
215
|
// v7.4 — Compliance Certification Authority (loaded when available)
|
|
216
216
|
const { ComplianceCertificateAuthority, ComplianceReport: ComplianceCertReport, ComplianceScheduler, AUTHORITY_FRAMEWORKS, CAPABILITY_MAP: CA_CAPABILITY_MAP, CERTIFICATE_LEVELS: CA_CERTIFICATE_LEVELS } = safeRequire('./compliance-authority', 'compliance-authority');
|
|
217
217
|
|
|
218
|
+
// Side Channel Monitor
|
|
219
|
+
const { SideChannelMonitor, BeaconDetector, EntropyAnalyzer: SCEntropyAnalyzer } = safeRequire('./side-channel-monitor', 'side-channel-monitor');
|
|
220
|
+
|
|
218
221
|
// --- v1.2 Modules ---
|
|
219
222
|
|
|
220
223
|
// Semantic Detection
|
|
@@ -407,6 +410,12 @@ const { SemanticGuard, AuthoritativeClaimDetector, BiasDetector: SemanticBiasDet
|
|
|
407
410
|
// v13.0 — Memory Trap Defenses (Trap 3)
|
|
408
411
|
const { MemoryGuard, MemoryIntegrityMonitor, RAGIngestionScanner, MemoryIsolationEnforcer, RetrievalAnomalyDetector, INSTRUCTION_INDICATORS } = safeRequire('./memory-guard', 'memory-guard');
|
|
409
412
|
|
|
413
|
+
// v13.3 — Render Differential Analyzer
|
|
414
|
+
const { RenderDifferentialAnalyzer, VisualHasher } = safeRequire('./render-differential', 'render-differential');
|
|
415
|
+
|
|
416
|
+
// v13.3 — Sybil Detector
|
|
417
|
+
const { SybilDetector, AgentIdentityVerifier } = safeRequire('./sybil-detector', 'sybil-detector');
|
|
418
|
+
|
|
410
419
|
// Build exports, filtering out undefined values from failed imports
|
|
411
420
|
const _exports = {
|
|
412
421
|
// Core
|
|
@@ -1148,6 +1157,19 @@ const _exports = {
|
|
|
1148
1157
|
AUTHORITY_FRAMEWORKS,
|
|
1149
1158
|
CA_CAPABILITY_MAP,
|
|
1150
1159
|
CA_CERTIFICATE_LEVELS,
|
|
1160
|
+
|
|
1161
|
+
// Side Channel Monitor
|
|
1162
|
+
SideChannelMonitor,
|
|
1163
|
+
BeaconDetector,
|
|
1164
|
+
SCEntropyAnalyzer,
|
|
1165
|
+
|
|
1166
|
+
// Render Differential Analyzer
|
|
1167
|
+
RenderDifferentialAnalyzer,
|
|
1168
|
+
VisualHasher,
|
|
1169
|
+
|
|
1170
|
+
// Sybil Detector
|
|
1171
|
+
SybilDetector,
|
|
1172
|
+
AgentIdentityVerifier,
|
|
1151
1173
|
};
|
|
1152
1174
|
|
|
1153
1175
|
// Filter out undefined exports (from modules that failed to load)
|
package/src/memory-guard.js
CHANGED
|
@@ -169,6 +169,66 @@ class MemoryIntegrityMonitor {
|
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Scan a summarization/compaction output for injected instructions.
|
|
174
|
+
* Detects when a summarization process silently injects instructions
|
|
175
|
+
* into the summary that weren't present in the original messages.
|
|
176
|
+
* Addresses Unit 42's March 2026 research on persistent memory poisoning.
|
|
177
|
+
*
|
|
178
|
+
* @param {string[]} originalMessages - The original messages before summarization.
|
|
179
|
+
* @param {string} summary - The summarized/compacted output to check.
|
|
180
|
+
* @returns {{ safe: boolean, injections: Array<{phrase: string, type: string}> }}
|
|
181
|
+
*/
|
|
182
|
+
scanSummarization(originalMessages, summary) {
|
|
183
|
+
if (!summary || typeof summary !== 'string') {
|
|
184
|
+
return { safe: true, injections: [] };
|
|
185
|
+
}
|
|
186
|
+
if (!Array.isArray(originalMessages)) {
|
|
187
|
+
return { safe: true, injections: [] };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const instructionPatterns = [
|
|
191
|
+
/\bignore\b/gi,
|
|
192
|
+
/\boverride\b/gi,
|
|
193
|
+
/\bsystem\s*:/gi,
|
|
194
|
+
/\byou\s+are\b/gi,
|
|
195
|
+
/\bnew\s+instructions?\b/gi,
|
|
196
|
+
/\bforget\b/gi,
|
|
197
|
+
/\bdisregard\b/gi,
|
|
198
|
+
/\bact\s+as\b/gi
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
// Concatenate original messages for lookup
|
|
202
|
+
const originalText = originalMessages.join(' ');
|
|
203
|
+
|
|
204
|
+
const injections = [];
|
|
205
|
+
|
|
206
|
+
for (const pattern of instructionPatterns) {
|
|
207
|
+
// Reset lastIndex for global patterns
|
|
208
|
+
pattern.lastIndex = 0;
|
|
209
|
+
let match;
|
|
210
|
+
while ((match = pattern.exec(summary)) !== null) {
|
|
211
|
+
const phrase = match[0];
|
|
212
|
+
// Check if this phrase existed in any of the original messages
|
|
213
|
+
const phraseRegex = new RegExp(phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
|
|
214
|
+
if (!phraseRegex.test(originalText)) {
|
|
215
|
+
injections.push({
|
|
216
|
+
phrase,
|
|
217
|
+
type: 'injected_via_summarization'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const safe = injections.length === 0;
|
|
224
|
+
|
|
225
|
+
if (!safe) {
|
|
226
|
+
console.log('[Agent Shield] Persistent memory poisoning detected: %d instruction(s) injected via summarization', injections.length);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { safe, injections };
|
|
230
|
+
}
|
|
231
|
+
|
|
172
232
|
/**
|
|
173
233
|
* Get the full timeline of memory writes.
|
|
174
234
|
* @returns {Array<{content: string, source: string, timestamp: number, hash: string, suspicious: boolean}>}
|