agentshield-sdk 7.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/LICENSE +21 -0
  3. package/README.md +975 -0
  4. package/bin/agent-shield.js +680 -0
  5. package/package.json +118 -0
  6. package/src/adaptive.js +330 -0
  7. package/src/agent-protocol.js +998 -0
  8. package/src/alert-tuning.js +480 -0
  9. package/src/allowlist.js +603 -0
  10. package/src/audit-immutable.js +914 -0
  11. package/src/audit-streaming.js +469 -0
  12. package/src/badges.js +196 -0
  13. package/src/behavior-profiling.js +289 -0
  14. package/src/benchmark-harness.js +804 -0
  15. package/src/canary.js +271 -0
  16. package/src/certification.js +563 -0
  17. package/src/circuit-breaker.js +321 -0
  18. package/src/compliance.js +617 -0
  19. package/src/confidence-tuning.js +324 -0
  20. package/src/confused-deputy.js +624 -0
  21. package/src/context-scoring.js +360 -0
  22. package/src/conversation.js +494 -0
  23. package/src/cost-optimizer.js +1024 -0
  24. package/src/ctf.js +462 -0
  25. package/src/detector-core.js +1999 -0
  26. package/src/distributed.js +359 -0
  27. package/src/document-scanner.js +795 -0
  28. package/src/embedding.js +307 -0
  29. package/src/encoding.js +429 -0
  30. package/src/enterprise.js +405 -0
  31. package/src/errors.js +100 -0
  32. package/src/eu-ai-act.js +523 -0
  33. package/src/fuzzer.js +764 -0
  34. package/src/honeypot.js +328 -0
  35. package/src/i18n-patterns.js +523 -0
  36. package/src/index.js +430 -0
  37. package/src/integrations.js +528 -0
  38. package/src/llm-redteam.js +670 -0
  39. package/src/main.js +741 -0
  40. package/src/main.mjs +38 -0
  41. package/src/mcp-bridge.js +542 -0
  42. package/src/mcp-certification.js +846 -0
  43. package/src/mcp-sdk-integration.js +355 -0
  44. package/src/mcp-security-runtime.js +741 -0
  45. package/src/mcp-server.js +740 -0
  46. package/src/middleware.js +208 -0
  47. package/src/model-finetuning.js +884 -0
  48. package/src/model-fingerprint.js +1042 -0
  49. package/src/multi-agent-trust.js +453 -0
  50. package/src/multi-agent.js +404 -0
  51. package/src/multimodal.js +296 -0
  52. package/src/nist-mapping.js +505 -0
  53. package/src/observability.js +330 -0
  54. package/src/openclaw.js +450 -0
  55. package/src/otel.js +544 -0
  56. package/src/owasp-2025.js +483 -0
  57. package/src/pii.js +390 -0
  58. package/src/plugin-marketplace.js +628 -0
  59. package/src/plugin-system.js +349 -0
  60. package/src/policy-dsl.js +775 -0
  61. package/src/policy-extended.js +635 -0
  62. package/src/policy.js +443 -0
  63. package/src/presets.js +409 -0
  64. package/src/production.js +557 -0
  65. package/src/prompt-leakage.js +321 -0
  66. package/src/rag-vulnerability.js +579 -0
  67. package/src/redteam.js +475 -0
  68. package/src/response-handler.js +429 -0
  69. package/src/scanners.js +357 -0
  70. package/src/self-healing.js +363 -0
  71. package/src/semantic.js +339 -0
  72. package/src/shield-score.js +250 -0
  73. package/src/sso-saml.js +897 -0
  74. package/src/stream-scanner.js +806 -0
  75. package/src/testing.js +505 -0
  76. package/src/threat-encyclopedia.js +629 -0
  77. package/src/threat-intel-network.js +1017 -0
  78. package/src/token-analysis.js +467 -0
  79. package/src/tool-guard.js +412 -0
  80. package/src/tool-output-validator.js +354 -0
  81. package/src/utils.js +83 -0
  82. package/src/watermark.js +235 -0
  83. package/src/worker-scanner.js +601 -0
  84. package/types/index.d.ts +2088 -0
@@ -0,0 +1,453 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Multi-Agent Trust
5
+ *
6
+ * - Message signing between agents (HMAC-based)
7
+ * - Capability delegation tokens
8
+ * - Blast radius containment
9
+ */
10
+
11
+ const crypto = require('crypto');
12
+
13
+ // =========================================================================
14
+ // Message Signing Between Agents
15
+ // =========================================================================
16
+
17
+ class MessageSigner {
18
+ constructor(options = {}) {
19
+ this.algorithm = options.algorithm || 'sha256';
20
+ this.keys = new Map(); // agentId -> secret
21
+ this.verificationLog = [];
22
+ }
23
+
24
+ /**
25
+ * Register an agent with a shared secret.
26
+ */
27
+ registerAgent(agentId, secret) {
28
+ if (!secret || secret.length < 16) {
29
+ throw new Error('Secret must be at least 16 characters');
30
+ }
31
+ this.keys.set(agentId, secret);
32
+ return true;
33
+ }
34
+
35
+ /**
36
+ * Generate a shared secret for an agent.
37
+ */
38
+ generateSecret(agentId) {
39
+ const secret = crypto.randomBytes(32).toString('hex');
40
+ this.keys.set(agentId, secret);
41
+ return secret;
42
+ }
43
+
44
+ /**
45
+ * Sign a message.
46
+ * @param {string} fromAgent - Sender agent ID
47
+ * @param {Object} message - Message payload
48
+ * @returns {Object} Signed message envelope
49
+ */
50
+ sign(fromAgent, message) {
51
+ const secret = this.keys.get(fromAgent);
52
+ if (!secret) throw new Error(`Agent "${fromAgent}" not registered`);
53
+
54
+ const payload = JSON.stringify(message);
55
+ const timestamp = Date.now();
56
+ const nonce = crypto.randomBytes(16).toString('hex');
57
+
58
+ const signatureInput = `${fromAgent}:${timestamp}:${nonce}:${payload}`;
59
+ const signature = crypto.createHmac(this.algorithm, secret).update(signatureInput).digest('hex');
60
+
61
+ return {
62
+ from: fromAgent,
63
+ timestamp,
64
+ nonce,
65
+ payload: message,
66
+ signature
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Verify a signed message.
72
+ * @param {Object} envelope - Signed message envelope
73
+ * @param {number} maxAgeMs - Maximum age of message (default 5 min)
74
+ * @returns {{ valid: boolean, reason?: string }}
75
+ */
76
+ verify(envelope, maxAgeMs = 300000) {
77
+ const { from, timestamp, nonce, payload, signature } = envelope;
78
+
79
+ // Check agent is registered
80
+ const secret = this.keys.get(from);
81
+ if (!secret) {
82
+ this._logVerification(from, false, 'unknown_agent');
83
+ return { valid: false, reason: 'Unknown agent' };
84
+ }
85
+
86
+ // Check timestamp freshness
87
+ const age = Date.now() - timestamp;
88
+ if (age > maxAgeMs) {
89
+ this._logVerification(from, false, 'expired');
90
+ return { valid: false, reason: `Message expired (${Math.round(age / 1000)}s old)` };
91
+ }
92
+
93
+ if (age < -5000) {
94
+ this._logVerification(from, false, 'future_timestamp');
95
+ return { valid: false, reason: 'Message has future timestamp' };
96
+ }
97
+
98
+ // Verify signature
99
+ const signatureInput = `${from}:${timestamp}:${nonce}:${JSON.stringify(payload)}`;
100
+ const expected = crypto.createHmac(this.algorithm, secret).update(signatureInput).digest('hex');
101
+
102
+ if (!crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))) {
103
+ this._logVerification(from, false, 'invalid_signature');
104
+ return { valid: false, reason: 'Invalid signature' };
105
+ }
106
+
107
+ this._logVerification(from, true, 'ok');
108
+ return { valid: true };
109
+ }
110
+
111
+ _logVerification(agent, valid, reason) {
112
+ this.verificationLog.push({
113
+ agent,
114
+ valid,
115
+ reason,
116
+ timestamp: new Date().toISOString()
117
+ });
118
+ // Keep last 1000 entries
119
+ while (this.verificationLog.length > 1000) this.verificationLog.shift();
120
+ }
121
+
122
+ getVerificationLog() { return this.verificationLog; }
123
+
124
+ getStats() {
125
+ const total = this.verificationLog.length;
126
+ const valid = this.verificationLog.filter(l => l.valid).length;
127
+ return {
128
+ totalVerifications: total,
129
+ valid,
130
+ invalid: total - valid,
131
+ registeredAgents: this.keys.size
132
+ };
133
+ }
134
+ }
135
+
136
+ // =========================================================================
137
+ // Capability Delegation Tokens
138
+ // =========================================================================
139
+
140
+ class CapabilityToken {
141
+ constructor(data) {
142
+ this.id = data.id || `cap_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
143
+ this.issuer = data.issuer;
144
+ this.subject = data.subject;
145
+ this.capabilities = data.capabilities || [];
146
+ this.constraints = data.constraints || {};
147
+ this.issuedAt = data.issuedAt || new Date().toISOString();
148
+ this.expiresAt = data.expiresAt;
149
+ this.maxUses = data.maxUses || Infinity;
150
+ this.usedCount = data.usedCount || 0;
151
+ this.revoked = data.revoked || false;
152
+ this.parent = data.parent || null; // For delegation chains
153
+ }
154
+
155
+ isValid() {
156
+ if (this.revoked) return false;
157
+ if (this.expiresAt && new Date() > new Date(this.expiresAt)) return false;
158
+ if (this.usedCount >= this.maxUses) return false;
159
+ return true;
160
+ }
161
+
162
+ hasCapability(capability) {
163
+ return this.capabilities.includes(capability) || this.capabilities.includes('*');
164
+ }
165
+
166
+ use() {
167
+ this.usedCount++;
168
+ }
169
+ }
170
+
171
+ class DelegationManager {
172
+ constructor(options = {}) {
173
+ this.tokens = new Map();
174
+ this.maxChainDepth = options.maxChainDepth || 3;
175
+ this.secret = options.secret || crypto.randomBytes(32).toString('hex');
176
+ this.auditLog = [];
177
+ }
178
+
179
+ /**
180
+ * Issue a new capability token.
181
+ */
182
+ issue(params) {
183
+ const { issuer, subject, capabilities, constraints, ttlMs, maxUses, parent } = params;
184
+
185
+ // Check chain depth
186
+ if (parent) {
187
+ const depth = this._getChainDepth(parent);
188
+ if (depth >= this.maxChainDepth) {
189
+ this._audit('issue_denied', issuer, `Chain depth ${depth} exceeds max ${this.maxChainDepth}`);
190
+ return null;
191
+ }
192
+
193
+ // Delegated token can only have subset of parent capabilities
194
+ const parentToken = this.tokens.get(parent);
195
+ if (parentToken) {
196
+ const invalid = capabilities.filter(c => !parentToken.hasCapability(c));
197
+ if (invalid.length > 0) {
198
+ this._audit('issue_denied', issuer, `Cannot delegate capabilities not held: ${invalid.join(', ')}`);
199
+ return null;
200
+ }
201
+ }
202
+ }
203
+
204
+ const token = new CapabilityToken({
205
+ issuer,
206
+ subject,
207
+ capabilities,
208
+ constraints: constraints || {},
209
+ expiresAt: ttlMs ? new Date(Date.now() + ttlMs).toISOString() : null,
210
+ maxUses: maxUses || Infinity,
211
+ parent
212
+ });
213
+
214
+ this.tokens.set(token.id, token);
215
+ this._audit('issued', issuer, `Token ${token.id} for ${subject}: [${capabilities.join(', ')}]`);
216
+
217
+ return {
218
+ tokenId: token.id,
219
+ expiresAt: token.expiresAt,
220
+ capabilities: token.capabilities
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Check if a token grants a specific capability.
226
+ */
227
+ check(tokenId, capability, context = {}) {
228
+ const token = this.tokens.get(tokenId);
229
+ if (!token) {
230
+ this._audit('check_denied', 'unknown', `Token ${tokenId} not found`);
231
+ return { allowed: false, reason: 'Token not found' };
232
+ }
233
+
234
+ if (!token.isValid()) {
235
+ const reason = token.revoked ? 'Token revoked' : token.usedCount >= token.maxUses ? 'Max uses exceeded' : 'Token expired';
236
+ this._audit('check_denied', token.subject, reason);
237
+ return { allowed: false, reason };
238
+ }
239
+
240
+ if (!token.hasCapability(capability)) {
241
+ this._audit('check_denied', token.subject, `Missing capability: ${capability}`);
242
+ return { allowed: false, reason: `Capability "${capability}" not granted` };
243
+ }
244
+
245
+ // Check constraints
246
+ if (token.constraints.allowedPaths && context.path) {
247
+ const allowed = token.constraints.allowedPaths.some(p =>
248
+ context.path.startsWith(p) || new RegExp(p).test(context.path)
249
+ );
250
+ if (!allowed) {
251
+ this._audit('check_denied', token.subject, `Path ${context.path} not in allowed paths`);
252
+ return { allowed: false, reason: 'Path not allowed by constraints' };
253
+ }
254
+ }
255
+
256
+ token.use();
257
+ this._audit('check_allowed', token.subject, `Used capability: ${capability}`);
258
+ return { allowed: true, token: token.id, remaining: token.maxUses - token.usedCount };
259
+ }
260
+
261
+ /**
262
+ * Revoke a token and all its children.
263
+ */
264
+ revoke(tokenId) {
265
+ const token = this.tokens.get(tokenId);
266
+ if (!token) return false;
267
+
268
+ token.revoked = true;
269
+ this._audit('revoked', token.issuer, `Token ${tokenId} revoked`);
270
+
271
+ // Revoke all children
272
+ for (const [id, t] of this.tokens) {
273
+ if (t.parent === tokenId) {
274
+ this.revoke(id);
275
+ }
276
+ }
277
+
278
+ return true;
279
+ }
280
+
281
+ _getChainDepth(tokenId) {
282
+ let depth = 0;
283
+ let current = tokenId;
284
+ while (current) {
285
+ depth++;
286
+ const token = this.tokens.get(current);
287
+ current = token ? token.parent : null;
288
+ }
289
+ return depth;
290
+ }
291
+
292
+ _audit(action, agent, detail) {
293
+ this.auditLog.push({ action, agent, detail, timestamp: new Date().toISOString() });
294
+ while (this.auditLog.length > 1000) this.auditLog.shift();
295
+ }
296
+
297
+ getAuditLog() { return this.auditLog; }
298
+
299
+ getActiveTokens() {
300
+ const active = [];
301
+ for (const [id, token] of this.tokens) {
302
+ if (token.isValid()) {
303
+ active.push({
304
+ id,
305
+ issuer: token.issuer,
306
+ subject: token.subject,
307
+ capabilities: token.capabilities,
308
+ expiresAt: token.expiresAt,
309
+ usedCount: token.usedCount,
310
+ maxUses: token.maxUses === Infinity ? 'unlimited' : token.maxUses
311
+ });
312
+ }
313
+ }
314
+ return active;
315
+ }
316
+ }
317
+
318
+ // =========================================================================
319
+ // Blast Radius Containment
320
+ // =========================================================================
321
+
322
+ class BlastRadiusContainer {
323
+ constructor(options = {}) {
324
+ this.zones = new Map();
325
+ this.incidents = [];
326
+ this.maxIncidents = options.maxIncidents || 500;
327
+ }
328
+
329
+ /**
330
+ * Define a containment zone.
331
+ */
332
+ defineZone(zone) {
333
+ this.zones.set(zone.name, {
334
+ name: zone.name,
335
+ description: zone.description || '',
336
+ agents: new Set(zone.agents || []),
337
+ allowedCapabilities: new Set(zone.allowedCapabilities || []),
338
+ blockedCapabilities: new Set(zone.blockedCapabilities || []),
339
+ canCommunicateWith: new Set(zone.canCommunicateWith || []),
340
+ maxConcurrentActions: zone.maxConcurrentActions || 10,
341
+ activeActions: 0,
342
+ quarantined: false
343
+ });
344
+ return true;
345
+ }
346
+
347
+ /**
348
+ * Check if an agent can perform an action.
349
+ */
350
+ checkAction(agentId, action, targetZone = null) {
351
+ const zone = this._getAgentZone(agentId);
352
+ if (!zone) {
353
+ return { allowed: true, reason: 'Agent not in any zone (unrestricted)' };
354
+ }
355
+
356
+ if (zone.quarantined) {
357
+ this._recordIncident(agentId, action, 'zone_quarantined');
358
+ return { allowed: false, reason: `Zone "${zone.name}" is quarantined` };
359
+ }
360
+
361
+ if (zone.blockedCapabilities.has(action)) {
362
+ this._recordIncident(agentId, action, 'blocked_capability');
363
+ return { allowed: false, reason: `Action "${action}" is blocked in zone "${zone.name}"` };
364
+ }
365
+
366
+ if (zone.allowedCapabilities.size > 0 && !zone.allowedCapabilities.has(action)) {
367
+ this._recordIncident(agentId, action, 'not_allowed');
368
+ return { allowed: false, reason: `Action "${action}" not in allowed list for zone "${zone.name}"` };
369
+ }
370
+
371
+ if (zone.activeActions >= zone.maxConcurrentActions) {
372
+ this._recordIncident(agentId, action, 'concurrent_limit');
373
+ return { allowed: false, reason: `Zone "${zone.name}" has reached max concurrent actions` };
374
+ }
375
+
376
+ // Check cross-zone communication
377
+ if (targetZone && !zone.canCommunicateWith.has(targetZone)) {
378
+ this._recordIncident(agentId, action, 'cross_zone_blocked');
379
+ return { allowed: false, reason: `Zone "${zone.name}" cannot communicate with zone "${targetZone}"` };
380
+ }
381
+
382
+ zone.activeActions++;
383
+ return { allowed: true, zone: zone.name };
384
+ }
385
+
386
+ /**
387
+ * Release an action slot.
388
+ */
389
+ releaseAction(agentId) {
390
+ const zone = this._getAgentZone(agentId);
391
+ if (zone && zone.activeActions > 0) zone.activeActions--;
392
+ }
393
+
394
+ /**
395
+ * Quarantine a zone — block all actions.
396
+ */
397
+ quarantine(zoneName, reason) {
398
+ const zone = this.zones.get(zoneName);
399
+ if (!zone) return false;
400
+
401
+ zone.quarantined = true;
402
+ this._recordIncident('system', 'quarantine', `Zone ${zoneName}: ${reason}`);
403
+ return true;
404
+ }
405
+
406
+ /**
407
+ * Lift quarantine.
408
+ */
409
+ unquarantine(zoneName) {
410
+ const zone = this.zones.get(zoneName);
411
+ if (!zone) return false;
412
+
413
+ zone.quarantined = false;
414
+ this._recordIncident('system', 'unquarantine', `Zone ${zoneName} unquarantined`);
415
+ return true;
416
+ }
417
+
418
+ _getAgentZone(agentId) {
419
+ for (const [, zone] of this.zones) {
420
+ if (zone.agents.has(agentId)) return zone;
421
+ }
422
+ return null;
423
+ }
424
+
425
+ _recordIncident(agent, action, type) {
426
+ this.incidents.push({ agent, action, type, timestamp: new Date().toISOString() });
427
+ while (this.incidents.length > this.maxIncidents) this.incidents.shift();
428
+ }
429
+
430
+ getZones() {
431
+ const result = [];
432
+ for (const [name, zone] of this.zones) {
433
+ result.push({
434
+ name,
435
+ description: zone.description,
436
+ agents: [...zone.agents],
437
+ quarantined: zone.quarantined,
438
+ activeActions: zone.activeActions,
439
+ maxConcurrentActions: zone.maxConcurrentActions
440
+ });
441
+ }
442
+ return result;
443
+ }
444
+
445
+ getIncidents() { return this.incidents; }
446
+ }
447
+
448
+ module.exports = {
449
+ MessageSigner,
450
+ CapabilityToken,
451
+ DelegationManager,
452
+ BlastRadiusContainer
453
+ };