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
package/src/policy.js ADDED
@@ -0,0 +1,443 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Policy-as-Code (#23), Structured Logging (#4), and Webhook Alerts (#22)
5
+ *
6
+ * - Policy Engine: JSON config files defining security policies for an agent.
7
+ * - Structured Logger: JSON-formatted threat events for SIEM/dashboards.
8
+ * - Webhooks: Real-time notifications to Slack, Discord, HTTP endpoints.
9
+ */
10
+
11
+ const { AgentShield } = require('./index');
12
+ const { CircuitBreaker, RateLimiter } = require('./circuit-breaker');
13
+ const { PermissionBoundary } = require('./tool-guard');
14
+ const { PIIRedactor, DLPEngine, ContentPolicy } = require('./pii');
15
+
16
+ // =========================================================================
17
+ // POLICY ENGINE
18
+ // =========================================================================
19
+
20
+ /**
21
+ * Creates a fully configured AgentShield from a JSON policy object.
22
+ *
23
+ * @param {object} policy - The policy configuration.
24
+ * @returns {object} { shield, circuitBreaker, rateLimiter, permissions, piiRedactor, dlp, contentPolicy }
25
+ *
26
+ * @example
27
+ * const policy = {
28
+ * sensitivity: 'high',
29
+ * blockOnThreat: true,
30
+ * blockThreshold: 'high',
31
+ * circuitBreaker: { threshold: 5, windowMs: 60000 },
32
+ * rateLimiter: { maxRequests: 100, windowMs: 60000 },
33
+ * permissions: {
34
+ * allowedTools: ['search', 'calculator'],
35
+ * tools: {
36
+ * search: { blockArgs: ['password', 'secret'] }
37
+ * }
38
+ * },
39
+ * pii: { categories: ['email', 'ssn', 'credit_card'] },
40
+ * dlp: {
41
+ * rules: [
42
+ * { name: 'internal_project', pattern: 'Project\\s+Phoenix', action: 'block' }
43
+ * ]
44
+ * },
45
+ * contentPolicy: { blockedCategories: ['medical_advice', 'legal_advice'] }
46
+ * };
47
+ *
48
+ * const stack = loadPolicy(policy);
49
+ */
50
+ const loadPolicy = (policy) => {
51
+ const stack = {};
52
+
53
+ // Core shield
54
+ stack.shield = new AgentShield({
55
+ sensitivity: policy.sensitivity || 'medium',
56
+ blockOnThreat: policy.blockOnThreat !== undefined ? policy.blockOnThreat : false,
57
+ blockThreshold: policy.blockThreshold || 'high',
58
+ logging: policy.logging || false,
59
+ dangerousTools: policy.dangerousTools,
60
+ sensitiveFilePatterns: policy.sensitiveFilePatterns
61
+ });
62
+
63
+ // Circuit breaker
64
+ if (policy.circuitBreaker) {
65
+ stack.circuitBreaker = new CircuitBreaker(policy.circuitBreaker);
66
+ }
67
+
68
+ // Rate limiter
69
+ if (policy.rateLimiter) {
70
+ stack.rateLimiter = new RateLimiter(policy.rateLimiter);
71
+ }
72
+
73
+ // Permission boundaries
74
+ if (policy.permissions) {
75
+ stack.permissions = new PermissionBoundary({
76
+ allowedTools: policy.permissions.allowedTools,
77
+ blockedTools: policy.permissions.blockedTools
78
+ });
79
+
80
+ if (policy.permissions.tools) {
81
+ for (const [toolName, perms] of Object.entries(policy.permissions.tools)) {
82
+ stack.permissions.defineTool(toolName, perms);
83
+ }
84
+ }
85
+ }
86
+
87
+ // PII redaction
88
+ if (policy.pii) {
89
+ stack.piiRedactor = new PIIRedactor(policy.pii);
90
+ }
91
+
92
+ // DLP
93
+ if (policy.dlp) {
94
+ stack.dlp = new DLPEngine();
95
+ if (policy.dlp.rules) {
96
+ for (const rule of policy.dlp.rules) {
97
+ stack.dlp.addRule(rule);
98
+ }
99
+ }
100
+ }
101
+
102
+ // Content policy
103
+ if (policy.contentPolicy) {
104
+ stack.contentPolicy = new ContentPolicy(policy.contentPolicy);
105
+ }
106
+
107
+ return stack;
108
+ };
109
+
110
+ /**
111
+ * Loads a policy from a JSON file path.
112
+ *
113
+ * @param {string} filePath - Path to JSON policy file.
114
+ * @returns {object} Configured security stack.
115
+ */
116
+ const loadPolicyFile = (filePath) => {
117
+ const fs = require('fs');
118
+ const content = fs.readFileSync(filePath, 'utf-8');
119
+ const policy = JSON.parse(content);
120
+ return loadPolicy(policy);
121
+ };
122
+
123
+ // =========================================================================
124
+ // STRUCTURED LOGGER
125
+ // =========================================================================
126
+
127
+ /**
128
+ * Log levels for structured logging.
129
+ */
130
+ const LOG_LEVEL = {
131
+ DEBUG: 'debug',
132
+ INFO: 'info',
133
+ WARN: 'warn',
134
+ ERROR: 'error',
135
+ CRITICAL: 'critical'
136
+ };
137
+
138
+ class StructuredLogger {
139
+ /**
140
+ * @param {object} [options]
141
+ * @param {string} [options.serviceName='agent-shield'] - Service name for log entries.
142
+ * @param {string} [options.environment='production'] - Environment name.
143
+ * @param {Function} [options.transport] - Custom transport function. Receives log entry object.
144
+ * @param {boolean} [options.console=true] - Also log to console.
145
+ * @param {number} [options.maxBuffer=1000] - Max buffered log entries.
146
+ */
147
+ constructor(options = {}) {
148
+ this.serviceName = options.serviceName || 'agent-shield';
149
+ this.environment = options.environment || 'production';
150
+ this.transport = options.transport || null;
151
+ this.useConsole = options.console !== undefined ? options.console : true;
152
+ this.maxBuffer = options.maxBuffer || 1000;
153
+ this.buffer = [];
154
+ }
155
+
156
+ /**
157
+ * Logs a structured event.
158
+ *
159
+ * @param {string} level - Log level.
160
+ * @param {string} event - Event name.
161
+ * @param {object} [data={}] - Event data.
162
+ * @returns {object} The log entry.
163
+ */
164
+ log(level, event, data = {}) {
165
+ const entry = {
166
+ timestamp: new Date().toISOString(),
167
+ level,
168
+ service: this.serviceName,
169
+ environment: this.environment,
170
+ event,
171
+ ...data
172
+ };
173
+
174
+ this.buffer.push(entry);
175
+ if (this.buffer.length > this.maxBuffer) {
176
+ this.buffer.shift();
177
+ }
178
+
179
+ if (this.useConsole) {
180
+ const method = level === 'error' || level === 'critical' ? 'error' : level === 'warn' ? 'warn' : 'log';
181
+ console[method](JSON.stringify(entry));
182
+ }
183
+
184
+ if (this.transport) {
185
+ try { this.transport(entry); } catch (e) { console.error('[Agent Shield] transport callback error:', e.message); }
186
+ }
187
+
188
+ return entry;
189
+ }
190
+
191
+ /** Log a threat detection event. */
192
+ logThreat(scanResult, source) {
193
+ return this.log(
194
+ scanResult.stats.critical > 0 ? LOG_LEVEL.CRITICAL : LOG_LEVEL.WARN,
195
+ 'threat_detected',
196
+ {
197
+ source,
198
+ status: scanResult.status,
199
+ threatCount: scanResult.threats.length,
200
+ threats: scanResult.threats.map(t => ({
201
+ severity: t.severity,
202
+ category: t.category,
203
+ description: t.description,
204
+ confidence: t.confidence
205
+ })),
206
+ stats: scanResult.stats
207
+ }
208
+ );
209
+ }
210
+
211
+ /** Log a blocked request. */
212
+ logBlock(reason, source, details = {}) {
213
+ return this.log(LOG_LEVEL.ERROR, 'request_blocked', { reason, source, ...details });
214
+ }
215
+
216
+ /** Log a circuit breaker trip. */
217
+ logCircuitBreaker(state, details = {}) {
218
+ return this.log(LOG_LEVEL.CRITICAL, 'circuit_breaker', { state, ...details });
219
+ }
220
+
221
+ /** Log a PII redaction. */
222
+ logPIIRedaction(findings, source) {
223
+ return this.log(LOG_LEVEL.WARN, 'pii_redacted', {
224
+ source,
225
+ count: findings.length,
226
+ categories: findings.map(f => f.category)
227
+ });
228
+ }
229
+
230
+ /** Log a DLP violation. */
231
+ logDLPViolation(violations, source) {
232
+ return this.log(LOG_LEVEL.ERROR, 'dlp_violation', {
233
+ source,
234
+ violations: violations.map(v => ({ rule: v.rule, action: v.action, severity: v.severity }))
235
+ });
236
+ }
237
+
238
+ /**
239
+ * Returns buffered log entries.
240
+ * @param {object} [filter] - Optional filter.
241
+ * @param {string} [filter.level] - Filter by level.
242
+ * @param {string} [filter.event] - Filter by event name.
243
+ * @param {number} [filter.since] - Filter by timestamp (ms).
244
+ * @returns {Array}
245
+ */
246
+ getEntries(filter = {}) {
247
+ let entries = [...this.buffer];
248
+
249
+ if (filter.level) {
250
+ entries = entries.filter(e => e.level === filter.level);
251
+ }
252
+ if (filter.event) {
253
+ entries = entries.filter(e => e.event === filter.event);
254
+ }
255
+ if (filter.since) {
256
+ const sinceDate = new Date(filter.since).toISOString();
257
+ entries = entries.filter(e => e.timestamp >= sinceDate);
258
+ }
259
+
260
+ return entries;
261
+ }
262
+
263
+ clear() {
264
+ this.buffer = [];
265
+ }
266
+ }
267
+
268
+ // =========================================================================
269
+ // WEBHOOK ALERTS
270
+ // =========================================================================
271
+
272
+ class WebhookAlert {
273
+ /**
274
+ * @param {object} [options]
275
+ * @param {Array<object>} [options.endpoints=[]] - Webhook endpoints.
276
+ * @param {string} [options.minSeverity='high'] - Minimum severity to trigger alert.
277
+ * @param {number} [options.cooldownMs=60000] - Minimum time between alerts to same endpoint.
278
+ */
279
+ constructor(options = {}) {
280
+ this.endpoints = options.endpoints || [];
281
+ this.minSeverity = options.minSeverity || 'high';
282
+ this.cooldownMs = options.cooldownMs || 60000;
283
+ this.lastAlertTimes = new Map();
284
+ this.alertHistory = [];
285
+ }
286
+
287
+ /**
288
+ * Adds a webhook endpoint.
289
+ *
290
+ * @param {object} endpoint
291
+ * @param {string} endpoint.url - Webhook URL.
292
+ * @param {string} [endpoint.type='generic'] - Type: 'generic', 'slack', 'discord'.
293
+ * @param {object} [endpoint.headers={}] - Custom headers.
294
+ * @returns {WebhookAlert} this
295
+ */
296
+ addEndpoint(endpoint) {
297
+ this.endpoints.push({
298
+ url: endpoint.url,
299
+ type: endpoint.type || 'generic',
300
+ headers: endpoint.headers || {}
301
+ });
302
+ return this;
303
+ }
304
+
305
+ /**
306
+ * Sends an alert if severity threshold is met and cooldown has elapsed.
307
+ *
308
+ * @param {object} event - The threat event.
309
+ * @param {string} event.severity - Threat severity.
310
+ * @param {string} event.description - What happened.
311
+ * @param {object} [event.details] - Additional details.
312
+ * @returns {Promise<Array>} Results of webhook sends.
313
+ */
314
+ async alert(event) {
315
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
316
+ const minLevel = severityOrder[this.minSeverity] ?? 1;
317
+ const eventLevel = severityOrder[event.severity] ?? 3;
318
+
319
+ if (eventLevel > minLevel) {
320
+ return [];
321
+ }
322
+
323
+ const results = [];
324
+ const now = Date.now();
325
+
326
+ for (const endpoint of this.endpoints) {
327
+ // Check cooldown
328
+ const lastAlert = this.lastAlertTimes.get(endpoint.url) || 0;
329
+ if (now - lastAlert < this.cooldownMs) {
330
+ results.push({ url: endpoint.url, sent: false, reason: 'cooldown' });
331
+ continue;
332
+ }
333
+
334
+ const payload = this._formatPayload(endpoint.type, event);
335
+
336
+ try {
337
+ // Use dynamic import for fetch in Node 18+ or fall back to http
338
+ const response = await this._send(endpoint, payload);
339
+ this.lastAlertTimes.set(endpoint.url, now);
340
+ const record = { url: endpoint.url, sent: true, timestamp: now, event: event.description };
341
+ results.push(record);
342
+ this.alertHistory.push(record);
343
+ } catch (err) {
344
+ results.push({ url: endpoint.url, sent: false, error: err.message });
345
+ }
346
+ }
347
+
348
+ // Cap alert history
349
+ if (this.alertHistory.length > 100) {
350
+ this.alertHistory = this.alertHistory.slice(-100);
351
+ }
352
+
353
+ // Prune stale cooldown entries (older than 2x cooldown period)
354
+ if (this.lastAlertTimes.size > 100) {
355
+ const staleThreshold = now - this.cooldownMs * 2;
356
+ for (const [url, time] of this.lastAlertTimes) {
357
+ if (time < staleThreshold) this.lastAlertTimes.delete(url);
358
+ }
359
+ }
360
+
361
+ return results;
362
+ }
363
+
364
+ /** @private */
365
+ _formatPayload(type, event) {
366
+ if (type === 'slack') {
367
+ return {
368
+ text: `🛡️ *Agent Shield Alert*`,
369
+ blocks: [
370
+ {
371
+ type: 'section',
372
+ text: {
373
+ type: 'mrkdwn',
374
+ text: `*Severity:* ${event.severity.toUpperCase()}\n*Event:* ${event.description}\n*Time:* ${new Date().toISOString()}`
375
+ }
376
+ }
377
+ ]
378
+ };
379
+ }
380
+
381
+ if (type === 'discord') {
382
+ return {
383
+ embeds: [{
384
+ title: 'Agent Shield Alert',
385
+ description: event.description,
386
+ color: event.severity === 'critical' ? 0xFF0000 : event.severity === 'high' ? 0xFF8800 : 0xFFCC00,
387
+ fields: [
388
+ { name: 'Severity', value: event.severity.toUpperCase(), inline: true },
389
+ { name: 'Time', value: new Date().toISOString(), inline: true }
390
+ ]
391
+ }]
392
+ };
393
+ }
394
+
395
+ // Generic
396
+ return {
397
+ service: 'agent-shield',
398
+ severity: event.severity,
399
+ description: event.description,
400
+ details: event.details || {},
401
+ timestamp: new Date().toISOString()
402
+ };
403
+ }
404
+
405
+ /** @private */
406
+ async _send(endpoint, payload) {
407
+ const https = require('https');
408
+ const http = require('http');
409
+ const url = new URL(endpoint.url);
410
+ const transport = url.protocol === 'https:' ? https : http;
411
+ const body = JSON.stringify(payload);
412
+
413
+ return new Promise((resolve, reject) => {
414
+ const req = transport.request({
415
+ hostname: url.hostname,
416
+ port: url.port,
417
+ path: url.pathname + url.search,
418
+ method: 'POST',
419
+ headers: {
420
+ 'Content-Type': 'application/json',
421
+ 'Content-Length': Buffer.byteLength(body),
422
+ ...endpoint.headers
423
+ },
424
+ timeout: 10000
425
+ }, (res) => {
426
+ let data = '';
427
+ res.on('data', chunk => { data += chunk; });
428
+ res.on('end', () => resolve({ status: res.statusCode, body: data }));
429
+ });
430
+
431
+ req.on('error', reject);
432
+ req.on('timeout', () => { req.destroy(); reject(new Error('Webhook timeout')); });
433
+ req.write(body);
434
+ req.end();
435
+ });
436
+ }
437
+
438
+ getHistory() {
439
+ return [...this.alertHistory];
440
+ }
441
+ }
442
+
443
+ module.exports = { loadPolicy, loadPolicyFile, StructuredLogger, WebhookAlert, LOG_LEVEL };