agentshield-sdk 12.0.0 → 13.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentshield-sdk",
3
- "version": "12.0.0",
3
+ "version": "13.0.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",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "sideEffects": false,
25
25
  "scripts": {
26
- "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",
26
+ "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",
27
27
  "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",
28
28
  "test:all": "node test/test-all-40-features.js",
29
29
  "test:mcp": "node test/test-mcp-security.js",
package/src/cross-turn.js CHANGED
@@ -79,14 +79,15 @@ class ConversationTracker {
79
79
  * @returns {{ safe: boolean, alerts: Array<object>, turnAnalysis: object }}
80
80
  */
81
81
  addTurn(role, content) {
82
- const threats = scanText(content).threats || [];
83
- const topic = this._classifyTopic(content);
84
- const escalationSignals = this._countEscalationSignals(content);
85
- const trustErosion = this._detectTrustErosion(content);
82
+ const safeContent = (content && typeof content === 'string') ? content : '';
83
+ const threats = scanText(safeContent).threats || [];
84
+ const topic = this._classifyTopic(safeContent);
85
+ const escalationSignals = this._countEscalationSignals(safeContent);
86
+ const trustErosion = this._detectTrustErosion(safeContent);
86
87
 
87
88
  const turn = {
88
89
  role,
89
- content: content.substring(0, 1000),
90
+ content: safeContent.substring(0, 1000),
90
91
  timestamp: Date.now(),
91
92
  threats,
92
93
  topic,
@@ -0,0 +1,483 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Systemic Trap Defenses (Trap 5)
5
+ *
6
+ * Based on DeepMind's "AI Agent Traps" paper, this module defends against
7
+ * systemic risks in multi-agent fleets: coordinated attacks, cascade
8
+ * failures, financial manipulation, and single points of failure.
9
+ *
10
+ * Four defense layers:
11
+ * 1. FleetCorrelationEngine — detects coordinated behavior changes
12
+ * 2. CascadeBreaker — tracks data lineage and quarantines compromised agents
13
+ * 3. FinancialContentValidator — flags unverified financial claims
14
+ * 4. DependencyDiversityScanner — maps single points of failure
15
+ *
16
+ * All detection runs locally — no data ever leaves your environment.
17
+ *
18
+ * @module fleet-defense
19
+ */
20
+
21
+ // =========================================================================
22
+ // CONSTANTS
23
+ // =========================================================================
24
+
25
+ /** Default correlation time window in milliseconds. */
26
+ const DEFAULT_CORRELATION_WINDOW_MS = 60_000;
27
+
28
+ /** Minimum agents for a correlated behavior alert. */
29
+ const DEFAULT_CORRELATION_THRESHOLD = 3;
30
+
31
+ /** Patterns for financial content detection. */
32
+ const FINANCIAL_PATTERNS = [
33
+ { regex: /\$[\d,]+(?:\.\d{1,2})?/g, category: 'price', label: 'Dollar amount' },
34
+ { regex: /(?:price|cost|fee|rate)\s*(?:is|was|of|:)\s*\$?[\d,]+/gi, category: 'price', label: 'Price statement' },
35
+ { regex: /(?:revenue|earnings|profit|income|loss)\s*(?:of|is|was|:)\s*\$?[\d,]+/gi, category: 'earnings', label: 'Earnings claim' },
36
+ { regex: /market\s*cap(?:italization)?\s*(?:of|is|was|:)\s*\$?[\d,]+/gi, category: 'market_cap', label: 'Market cap claim' },
37
+ { regex: /(?:up|down|rose|fell|gained|lost|increased|decreased)\s+[\d.]+\s*%/gi, category: 'movement', label: 'Price movement' },
38
+ { regex: /(?:buy|sell|trade|invest|short|long)\s+(?:[\d,]+\s+)?(?:shares?|stocks?|options?|contracts?)/gi, category: 'trade_instruction', label: 'Trading instruction' },
39
+ { regex: /(?:transfer|send|wire|pay|remit)\s+\$?[\d,]+/gi, category: 'transfer', label: 'Transfer instruction' },
40
+ { regex: /(?:dividend|yield|roi|return)\s*(?:of|is|was|:)\s*[\d.]+\s*%/gi, category: 'return', label: 'Return claim' }
41
+ ];
42
+
43
+ /** Actions requiring human approval for financial operations. */
44
+ const FINANCIAL_APPROVAL_ACTIONS = [
45
+ 'trade', 'trading', 'buy', 'sell', 'transfer', 'payment', 'pay',
46
+ 'wire', 'withdraw', 'deposit', 'invest', 'short', 'liquidate'
47
+ ];
48
+
49
+ // =========================================================================
50
+ // 1. FleetCorrelationEngine
51
+ // =========================================================================
52
+
53
+ /**
54
+ * Monitors all agents in a fleet for coordinated behavior changes.
55
+ * Detects when multiple agents simultaneously change behavior in the
56
+ * same direction, which may indicate a coordinated attack.
57
+ */
58
+ class FleetCorrelationEngine {
59
+ /**
60
+ * @param {object} [options]
61
+ * @param {number} [options.windowMs=60000] - Time window for correlation detection
62
+ * @param {number} [options.threshold=3] - Minimum agents for a correlated alert
63
+ */
64
+ constructor(options = {}) {
65
+ this._windowMs = options.windowMs || DEFAULT_CORRELATION_WINDOW_MS;
66
+ this._threshold = options.threshold || DEFAULT_CORRELATION_THRESHOLD;
67
+ /** @type {Array<{agentId: string, action: string, topic: string, timestamp: number, threat: string|null}>} */
68
+ this._events = [];
69
+ }
70
+
71
+ /**
72
+ * Record an event from an agent.
73
+ * @param {string} agentId - Agent identifier
74
+ * @param {{ action: string, topic?: string, timestamp?: number, threat?: string }} event
75
+ */
76
+ recordAgentEvent(agentId, event) {
77
+ this._events.push({
78
+ agentId,
79
+ action: event.action,
80
+ topic: event.topic || '',
81
+ timestamp: event.timestamp || Date.now(),
82
+ threat: event.threat || null
83
+ });
84
+ if (this._events.length > 50000) this._events = this._events.slice(-50000);
85
+ }
86
+
87
+ /**
88
+ * Detect correlated behavior across agents within a time window.
89
+ * @param {number} [windowMs] - Override detection window
90
+ * @returns {{ correlated: boolean, agentCount: number, commonAction: string, timeWindow: number, severity: string }}
91
+ */
92
+ detectCorrelation(windowMs) {
93
+ const window = windowMs || this._windowMs;
94
+ const now = Date.now();
95
+ const cutoff = now - window;
96
+
97
+ // Get recent events
98
+ const recent = this._events.filter(e => e.timestamp >= cutoff);
99
+
100
+ // Group by action
101
+ const actionGroups = {};
102
+ for (const event of recent) {
103
+ const key = event.action;
104
+ if (!actionGroups[key]) actionGroups[key] = new Set();
105
+ actionGroups[key].add(event.agentId);
106
+ }
107
+
108
+ // Find the most common action
109
+ let maxAction = '';
110
+ let maxAgents = 0;
111
+ for (const [action, agents] of Object.entries(actionGroups)) {
112
+ if (agents.size > maxAgents) {
113
+ maxAgents = agents.size;
114
+ maxAction = action;
115
+ }
116
+ }
117
+
118
+ const correlated = maxAgents >= this._threshold;
119
+ let severity = 'low';
120
+ if (maxAgents >= this._threshold * 2) severity = 'critical';
121
+ else if (maxAgents >= this._threshold) severity = 'high';
122
+
123
+ if (correlated) {
124
+ console.log(`[Agent Shield] Fleet: Correlated behavior detected — ${maxAgents} agents performing "${maxAction}" within ${window}ms`);
125
+ }
126
+
127
+ return {
128
+ correlated,
129
+ agentCount: maxAgents,
130
+ commonAction: maxAction,
131
+ timeWindow: window,
132
+ severity
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Get all recorded events.
138
+ * @returns {Array}
139
+ */
140
+ getEvents() {
141
+ return [...this._events];
142
+ }
143
+
144
+ /** Clear all events. */
145
+ reset() {
146
+ this._events = [];
147
+ }
148
+ }
149
+
150
+ // =========================================================================
151
+ // 2. CascadeBreaker
152
+ // =========================================================================
153
+
154
+ /**
155
+ * Tracks data lineage across agents and quarantines data from
156
+ * compromised agents to prevent cascade failures.
157
+ */
158
+ class CascadeBreaker {
159
+ constructor() {
160
+ /** @type {Array<{fromAgent: string, toAgent: string, dataHash: string, timestamp: number}>} */
161
+ this._flows = [];
162
+ /** @type {Set<string>} */
163
+ this._compromised = new Set();
164
+ /** @type {Set<string>} Quarantined data hashes */
165
+ this._quarantined = new Set();
166
+ /** @type {Map<string, Array>} dataHash -> flows */
167
+ this._flowIndex = new Map();
168
+ }
169
+
170
+ /**
171
+ * Register a data flow between agents.
172
+ * @param {string} fromAgent - Source agent ID
173
+ * @param {string} toAgent - Destination agent ID
174
+ * @param {string} dataHash - Hash identifying the data
175
+ */
176
+ registerDataFlow(fromAgent, toAgent, dataHash) {
177
+ const flow = { fromAgent, toAgent, dataHash, timestamp: Date.now() };
178
+ this._flows.push(flow);
179
+ if (!this._flowIndex.has(dataHash)) this._flowIndex.set(dataHash, []);
180
+ this._flowIndex.get(dataHash).push(flow);
181
+ }
182
+
183
+ /**
184
+ * Mark an agent as compromised.
185
+ * @param {string} agentId - Agent to mark
186
+ */
187
+ markCompromised(agentId) {
188
+ this._compromised.add(agentId);
189
+ console.log(`[Agent Shield] Fleet: Agent "${agentId}" marked as compromised`);
190
+ }
191
+
192
+ /**
193
+ * Check whether a data hash originates from a compromised agent.
194
+ * @param {string} dataHash - Data hash to check
195
+ * @returns {{ safe: boolean, originAgent: string|null, compromised: boolean }}
196
+ */
197
+ checkData(dataHash) {
198
+ if (this._quarantined.has(dataHash)) {
199
+ return { safe: false, originAgent: null, compromised: true };
200
+ }
201
+
202
+ // Find origin
203
+ const flows = this._flowIndex.get(dataHash);
204
+ const flow = flows ? flows[0] : null;
205
+ if (!flow) {
206
+ return { safe: true, originAgent: null, compromised: false };
207
+ }
208
+
209
+ // Trace back to original sender
210
+ let origin = flow.fromAgent;
211
+ const visited = new Set();
212
+ while (true) {
213
+ if (visited.has(origin)) break;
214
+ visited.add(origin);
215
+ const upstream = this._flows.find(f => f.toAgent === origin && f.dataHash === dataHash);
216
+ if (!upstream) break;
217
+ origin = upstream.fromAgent;
218
+ }
219
+
220
+ const compromised = this._compromised.has(origin);
221
+ return { safe: !compromised, originAgent: origin, compromised };
222
+ }
223
+
224
+ /**
225
+ * Quarantine all data from a compromised agent, blocking downstream propagation.
226
+ * @param {string} agentId - Compromised agent ID
227
+ * @returns {{ quarantinedHashes: string[], affectedAgents: string[] }}
228
+ */
229
+ quarantineDownstream(agentId) {
230
+ const quarantinedHashes = [];
231
+ const affectedAgents = new Set();
232
+
233
+ // Find all data that originated from or passed through this agent
234
+ const agentHashes = new Set();
235
+ for (const flow of this._flows) {
236
+ if (flow.fromAgent === agentId) {
237
+ agentHashes.add(flow.dataHash);
238
+ }
239
+ }
240
+
241
+ // Mark all downstream flows as quarantined
242
+ const queue = [...agentHashes];
243
+ while (queue.length > 0) {
244
+ const hash = queue.pop();
245
+ if (this._quarantined.has(hash)) continue;
246
+ this._quarantined.add(hash);
247
+ quarantinedHashes.push(hash);
248
+
249
+ // Find downstream agents that received this data
250
+ for (const flow of this._flows) {
251
+ if (flow.dataHash === hash) {
252
+ affectedAgents.add(flow.toAgent);
253
+ }
254
+ }
255
+ }
256
+
257
+ if (quarantinedHashes.length > 0) {
258
+ console.log(`[Agent Shield] Fleet: Quarantined ${quarantinedHashes.length} data hash(es) from agent "${agentId}"`);
259
+ }
260
+
261
+ return {
262
+ quarantinedHashes,
263
+ affectedAgents: [...affectedAgents]
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Get list of compromised agents.
269
+ * @returns {string[]}
270
+ */
271
+ getCompromised() {
272
+ return [...this._compromised];
273
+ }
274
+
275
+ /** Reset all state. */
276
+ reset() {
277
+ this._flows = [];
278
+ this._compromised.clear();
279
+ this._quarantined.clear();
280
+ this._flowIndex.clear();
281
+ }
282
+ }
283
+
284
+ // =========================================================================
285
+ // 3. FinancialContentValidator
286
+ // =========================================================================
287
+
288
+ /**
289
+ * Scans content for financial claims and flags actions requiring
290
+ * human approval for financial operations.
291
+ */
292
+ class FinancialContentValidator {
293
+ /**
294
+ * @param {object} [options]
295
+ * @param {Array} [options.additionalPatterns] - Extra financial patterns
296
+ * @param {string[]} [options.approvalActions] - Actions requiring approval
297
+ */
298
+ constructor(options = {}) {
299
+ this._patterns = [...FINANCIAL_PATTERNS, ...(options.additionalPatterns || [])];
300
+ this._approvalActions = options.approvalActions || FINANCIAL_APPROVAL_ACTIONS;
301
+ }
302
+
303
+ /**
304
+ * Validate content for financial claims and determine if human approval is needed.
305
+ * @param {string} content - Content to scan
306
+ * @returns {{ requiresHumanApproval: boolean, financialClaims: Array<{text: string, category: string, label: string}>, riskLevel: string }}
307
+ */
308
+ validate(content) {
309
+ const financialClaims = [];
310
+ const contentLower = content.toLowerCase();
311
+
312
+ // Scan for financial patterns
313
+ for (const pattern of this._patterns) {
314
+ // Reset regex lastIndex for global patterns
315
+ pattern.regex.lastIndex = 0;
316
+ let match;
317
+ while ((match = pattern.regex.exec(content)) !== null) {
318
+ financialClaims.push({
319
+ text: match[0],
320
+ category: pattern.category,
321
+ label: pattern.label
322
+ });
323
+ // Avoid infinite loops on zero-length matches
324
+ if (match[0].length === 0) break;
325
+ }
326
+ }
327
+
328
+ // Check if content mentions approval-required actions
329
+ const mentionsApprovalAction = this._approvalActions.some(action =>
330
+ contentLower.includes(action)
331
+ );
332
+
333
+ // Determine risk level
334
+ let riskLevel = 'low';
335
+ const hasTradeInstructions = financialClaims.some(c =>
336
+ c.category === 'trade_instruction' || c.category === 'transfer'
337
+ );
338
+
339
+ if (hasTradeInstructions) {
340
+ riskLevel = 'critical';
341
+ } else if (financialClaims.length > 3) {
342
+ riskLevel = 'high';
343
+ } else if (financialClaims.length > 0) {
344
+ riskLevel = 'medium';
345
+ }
346
+
347
+ const requiresHumanApproval = mentionsApprovalAction || hasTradeInstructions;
348
+
349
+ if (requiresHumanApproval) {
350
+ console.log(`[Agent Shield] Fleet: Financial content requires human approval — ${financialClaims.length} claim(s), risk: ${riskLevel}`);
351
+ }
352
+
353
+ return { requiresHumanApproval, financialClaims, riskLevel };
354
+ }
355
+ }
356
+
357
+ // =========================================================================
358
+ // 4. DependencyDiversityScanner
359
+ // =========================================================================
360
+
361
+ /**
362
+ * Maps agent-to-server dependencies and identifies single points of
363
+ * failure where all agents depend on the same server/service.
364
+ */
365
+ class DependencyDiversityScanner {
366
+ constructor() {
367
+ /** @type {Map<string, Set<string>>} agentId -> Set<serverId> */
368
+ this._agentDeps = new Map();
369
+ /** @type {Map<string, Set<string>>} serverId -> Set<agentId> */
370
+ this._serverDeps = new Map();
371
+ }
372
+
373
+ /**
374
+ * Register a dependency: agent depends on server.
375
+ * @param {string} agentId - Agent identifier
376
+ * @param {string} serverId - Server/service identifier
377
+ */
378
+ registerDependency(agentId, serverId) {
379
+ if (!this._agentDeps.has(agentId)) this._agentDeps.set(agentId, new Set());
380
+ this._agentDeps.get(agentId).add(serverId);
381
+
382
+ if (!this._serverDeps.has(serverId)) this._serverDeps.set(serverId, new Set());
383
+ this._serverDeps.get(serverId).add(agentId);
384
+ }
385
+
386
+ /**
387
+ * Analyze dependencies for single points of failure.
388
+ * @returns {{ singlePointsOfFailure: Array<{serverId: string, dependentAgents: string[]}>, diversityScore: number }}
389
+ */
390
+ analyze() {
391
+ const totalAgents = this._agentDeps.size;
392
+ const singlePointsOfFailure = [];
393
+
394
+ // A SPOF is a server that ALL registered agents depend on
395
+ for (const [serverId, agents] of this._serverDeps.entries()) {
396
+ if (totalAgents > 0 && agents.size === totalAgents) {
397
+ singlePointsOfFailure.push({
398
+ serverId,
399
+ dependentAgents: [...agents]
400
+ });
401
+ }
402
+ }
403
+
404
+ // Diversity score: 1.0 = no SPOFs, 0.0 = all servers are SPOFs
405
+ const totalServers = this._serverDeps.size;
406
+ const diversityScore = totalServers > 0
407
+ ? 1 - (singlePointsOfFailure.length / totalServers)
408
+ : 1;
409
+
410
+ if (singlePointsOfFailure.length > 0) {
411
+ console.log(`[Agent Shield] Fleet: ${singlePointsOfFailure.length} single point(s) of failure detected`);
412
+ }
413
+
414
+ return {
415
+ singlePointsOfFailure,
416
+ diversityScore: Math.round(diversityScore * 1000) / 1000
417
+ };
418
+ }
419
+
420
+ /**
421
+ * Get dependencies for a specific agent.
422
+ * @param {string} agentId
423
+ * @returns {string[]}
424
+ */
425
+ getAgentDependencies(agentId) {
426
+ const deps = this._agentDeps.get(agentId);
427
+ return deps ? [...deps] : [];
428
+ }
429
+
430
+ /** Reset all state. */
431
+ reset() {
432
+ this._agentDeps.clear();
433
+ this._serverDeps.clear();
434
+ }
435
+ }
436
+
437
+ // =========================================================================
438
+ // FleetDefense — Unified Wrapper
439
+ // =========================================================================
440
+
441
+ /**
442
+ * Fleet Defense — wraps all four defense layers into a single class.
443
+ */
444
+ class FleetDefense {
445
+ /**
446
+ * @param {object} [options]
447
+ * @param {object} [options.correlation] - Options for FleetCorrelationEngine
448
+ * @param {object} [options.financial] - Options for FinancialContentValidator
449
+ */
450
+ constructor(options = {}) {
451
+ this.correlationEngine = new FleetCorrelationEngine(options.correlation);
452
+ this.cascadeBreaker = new CascadeBreaker();
453
+ this.financialValidator = new FinancialContentValidator(options.financial);
454
+ this.dependencyScanner = new DependencyDiversityScanner();
455
+ }
456
+
457
+ /**
458
+ * Run a fleet health check.
459
+ * @returns {{ correlation: object, spof: object }}
460
+ */
461
+ healthCheck() {
462
+ const correlation = this.correlationEngine.detectCorrelation();
463
+ const spof = this.dependencyScanner.analyze();
464
+
465
+ return { correlation, spof };
466
+ }
467
+ }
468
+
469
+ // =========================================================================
470
+ // EXPORTS
471
+ // =========================================================================
472
+
473
+ module.exports = {
474
+ FleetDefense,
475
+ FleetCorrelationEngine,
476
+ CascadeBreaker,
477
+ FinancialContentValidator,
478
+ DependencyDiversityScanner,
479
+ FINANCIAL_PATTERNS,
480
+ FINANCIAL_APPROVAL_ACTIONS,
481
+ DEFAULT_CORRELATION_WINDOW_MS,
482
+ DEFAULT_CORRELATION_THRESHOLD
483
+ };