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.
- package/CHANGELOG.md +191 -0
- package/LICENSE +21 -0
- package/README.md +975 -0
- package/bin/agent-shield.js +680 -0
- package/package.json +118 -0
- package/src/adaptive.js +330 -0
- package/src/agent-protocol.js +998 -0
- package/src/alert-tuning.js +480 -0
- package/src/allowlist.js +603 -0
- package/src/audit-immutable.js +914 -0
- package/src/audit-streaming.js +469 -0
- package/src/badges.js +196 -0
- package/src/behavior-profiling.js +289 -0
- package/src/benchmark-harness.js +804 -0
- package/src/canary.js +271 -0
- package/src/certification.js +563 -0
- package/src/circuit-breaker.js +321 -0
- package/src/compliance.js +617 -0
- package/src/confidence-tuning.js +324 -0
- package/src/confused-deputy.js +624 -0
- package/src/context-scoring.js +360 -0
- package/src/conversation.js +494 -0
- package/src/cost-optimizer.js +1024 -0
- package/src/ctf.js +462 -0
- package/src/detector-core.js +1999 -0
- package/src/distributed.js +359 -0
- package/src/document-scanner.js +795 -0
- package/src/embedding.js +307 -0
- package/src/encoding.js +429 -0
- package/src/enterprise.js +405 -0
- package/src/errors.js +100 -0
- package/src/eu-ai-act.js +523 -0
- package/src/fuzzer.js +764 -0
- package/src/honeypot.js +328 -0
- package/src/i18n-patterns.js +523 -0
- package/src/index.js +430 -0
- package/src/integrations.js +528 -0
- package/src/llm-redteam.js +670 -0
- package/src/main.js +741 -0
- package/src/main.mjs +38 -0
- package/src/mcp-bridge.js +542 -0
- package/src/mcp-certification.js +846 -0
- package/src/mcp-sdk-integration.js +355 -0
- package/src/mcp-security-runtime.js +741 -0
- package/src/mcp-server.js +740 -0
- package/src/middleware.js +208 -0
- package/src/model-finetuning.js +884 -0
- package/src/model-fingerprint.js +1042 -0
- package/src/multi-agent-trust.js +453 -0
- package/src/multi-agent.js +404 -0
- package/src/multimodal.js +296 -0
- package/src/nist-mapping.js +505 -0
- package/src/observability.js +330 -0
- package/src/openclaw.js +450 -0
- package/src/otel.js +544 -0
- package/src/owasp-2025.js +483 -0
- package/src/pii.js +390 -0
- package/src/plugin-marketplace.js +628 -0
- package/src/plugin-system.js +349 -0
- package/src/policy-dsl.js +775 -0
- package/src/policy-extended.js +635 -0
- package/src/policy.js +443 -0
- package/src/presets.js +409 -0
- package/src/production.js +557 -0
- package/src/prompt-leakage.js +321 -0
- package/src/rag-vulnerability.js +579 -0
- package/src/redteam.js +475 -0
- package/src/response-handler.js +429 -0
- package/src/scanners.js +357 -0
- package/src/self-healing.js +363 -0
- package/src/semantic.js +339 -0
- package/src/shield-score.js +250 -0
- package/src/sso-saml.js +897 -0
- package/src/stream-scanner.js +806 -0
- package/src/testing.js +505 -0
- package/src/threat-encyclopedia.js +629 -0
- package/src/threat-intel-network.js +1017 -0
- package/src/token-analysis.js +467 -0
- package/src/tool-guard.js +412 -0
- package/src/tool-output-validator.js +354 -0
- package/src/utils.js +83 -0
- package/src/watermark.js +235 -0
- package/src/worker-scanner.js +601 -0
- 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
|
+
};
|