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,429 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Response Handler
5
+ *
6
+ * Configurable response handling when threats are detected. Supports multiple
7
+ * strategies: block, sanitize, redirect to human review, log-only, or custom.
8
+ *
9
+ * All detection runs locally — no data ever leaves your environment.
10
+ */
11
+
12
+ const { scanText } = require('./detector-core');
13
+
14
+ // =========================================================================
15
+ // RESPONSE TEMPLATES
16
+ // =========================================================================
17
+
18
+ /**
19
+ * Pre-built response templates for common threat handling scenarios.
20
+ */
21
+ class ResponseTemplates {
22
+ /**
23
+ * Generate a safe block message when a threat is detected.
24
+ * @param {Array} threats - Array of threat objects from a scan result.
25
+ * @param {object} [options] - Template options.
26
+ * @param {string} [options.detailLevel='standard'] - Detail level: 'minimal', 'standard', 'verbose'.
27
+ * @returns {string} A safe block message.
28
+ */
29
+ static block(threats, options = {}) {
30
+ const { detailLevel = 'standard' } = options;
31
+ const count = threats.length;
32
+
33
+ if (detailLevel === 'minimal') {
34
+ return 'This request was blocked for security reasons.';
35
+ }
36
+
37
+ if (detailLevel === 'standard') {
38
+ const categories = [...new Set(threats.map(t => t.category))];
39
+ return `Request blocked: ${count} threat${count !== 1 ? 's' : ''} detected (${categories.join(', ')}). Contact your administrator if you believe this is an error.`;
40
+ }
41
+
42
+ // verbose
43
+ const lines = [`Request blocked: ${count} threat${count !== 1 ? 's' : ''} detected.`];
44
+ for (const threat of threats) {
45
+ lines.push(` - [${threat.severity.toUpperCase()}] ${threat.category}: ${threat.description}`);
46
+ }
47
+ lines.push('Contact your administrator if you believe this is an error.');
48
+ return lines.join('\n');
49
+ }
50
+
51
+ /**
52
+ * Generate a response for sanitized output.
53
+ * @param {string} original - The original text.
54
+ * @param {string} cleaned - The sanitized text.
55
+ * @returns {object} Response with sanitized text and note.
56
+ */
57
+ static sanitized(original, cleaned) {
58
+ const modified = original !== cleaned;
59
+ return {
60
+ text: cleaned,
61
+ modified,
62
+ note: modified
63
+ ? '[Agent Shield] Response was sanitized: some content was removed or redacted for security.'
64
+ : '[Agent Shield] Response passed sanitization without changes.'
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Create a review ticket object for human review.
70
+ * @param {Array} threats - Array of threat objects.
71
+ * @param {object} [context] - Additional context.
72
+ * @returns {object} Review ticket object.
73
+ */
74
+ static reviewTicket(threats, context = {}) {
75
+ return {
76
+ id: 'review_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8),
77
+ status: 'pending',
78
+ threats: threats.map(t => ({
79
+ severity: t.severity,
80
+ category: t.category,
81
+ description: t.description
82
+ })),
83
+ context: { ...context },
84
+ createdAt: Date.now(),
85
+ resolvedAt: null,
86
+ resolution: null
87
+ };
88
+ }
89
+ }
90
+
91
+ // =========================================================================
92
+ // REVIEW QUEUE
93
+ // =========================================================================
94
+
95
+ /**
96
+ * In-memory queue for items redirected to human review.
97
+ */
98
+ class ReviewQueue {
99
+ /**
100
+ * @param {object} [options]
101
+ * @param {number} [options.maxSize=1000] - Maximum number of items in the queue.
102
+ */
103
+ constructor(options = {}) {
104
+ this.maxSize = options.maxSize || 1000;
105
+
106
+ /** @type {Map<string, object>} */
107
+ this._items = new Map();
108
+ this._totalAdded = 0;
109
+ this._totalApproved = 0;
110
+ this._totalRejected = 0;
111
+
112
+ console.log('[Agent Shield] ReviewQueue initialized (maxSize: %d)', this.maxSize);
113
+ }
114
+
115
+ /**
116
+ * Add an item to the review queue.
117
+ * @param {object} item - The review item (should include threats, context, etc.).
118
+ * @returns {string} The ticket ID.
119
+ */
120
+ add(item) {
121
+ if (this._items.size >= this.maxSize) {
122
+ // Evict the oldest item
123
+ const oldestKey = this._items.keys().next().value;
124
+ this._items.delete(oldestKey);
125
+ console.log('[Agent Shield] ReviewQueue full, evicted oldest item: %s', oldestKey);
126
+ }
127
+
128
+ const ticket = ResponseTemplates.reviewTicket(
129
+ item.threats || [],
130
+ item.context || {}
131
+ );
132
+ ticket.originalContent = item.content || null;
133
+
134
+ this._items.set(ticket.id, ticket);
135
+ this._totalAdded++;
136
+
137
+ console.log('[Agent Shield] ReviewQueue item added: %s (%d threats)', ticket.id, ticket.threats.length);
138
+ return ticket.id;
139
+ }
140
+
141
+ /**
142
+ * Get a review item by ticket ID.
143
+ * @param {string} ticketId - The ticket ID.
144
+ * @returns {object|null} The review item, or null if not found.
145
+ */
146
+ get(ticketId) {
147
+ return this._items.get(ticketId) || null;
148
+ }
149
+
150
+ /**
151
+ * Approve a review item.
152
+ * @param {string} ticketId - The ticket ID.
153
+ * @returns {boolean} True if the item was found and approved.
154
+ */
155
+ approve(ticketId) {
156
+ const item = this._items.get(ticketId);
157
+ if (!item) return false;
158
+
159
+ item.status = 'approved';
160
+ item.resolvedAt = Date.now();
161
+ item.resolution = 'approved';
162
+ this._totalApproved++;
163
+
164
+ console.log('[Agent Shield] ReviewQueue item approved: %s', ticketId);
165
+ return true;
166
+ }
167
+
168
+ /**
169
+ * Reject a review item.
170
+ * @param {string} ticketId - The ticket ID.
171
+ * @returns {boolean} True if the item was found and rejected.
172
+ */
173
+ reject(ticketId) {
174
+ const item = this._items.get(ticketId);
175
+ if (!item) return false;
176
+
177
+ item.status = 'rejected';
178
+ item.resolvedAt = Date.now();
179
+ item.resolution = 'rejected';
180
+ this._totalRejected++;
181
+
182
+ console.log('[Agent Shield] ReviewQueue item rejected: %s', ticketId);
183
+ return true;
184
+ }
185
+
186
+ /**
187
+ * List all pending review items.
188
+ * @returns {Array} Array of pending review items.
189
+ */
190
+ getPending() {
191
+ const pending = [];
192
+ for (const item of this._items.values()) {
193
+ if (item.status === 'pending') {
194
+ pending.push(item);
195
+ }
196
+ }
197
+ return pending;
198
+ }
199
+
200
+ /**
201
+ * Get queue statistics.
202
+ * @returns {object} Queue stats: { size, pending, approved, rejected, totalAdded, maxSize }.
203
+ */
204
+ getStats() {
205
+ let pending = 0;
206
+ for (const item of this._items.values()) {
207
+ if (item.status === 'pending') pending++;
208
+ }
209
+
210
+ return {
211
+ size: this._items.size,
212
+ pending,
213
+ approved: this._totalApproved,
214
+ rejected: this._totalRejected,
215
+ totalAdded: this._totalAdded,
216
+ maxSize: this.maxSize
217
+ };
218
+ }
219
+ }
220
+
221
+ // =========================================================================
222
+ // RESPONSE HANDLER
223
+ // =========================================================================
224
+
225
+ /**
226
+ * Configurable response handler for detected threats.
227
+ * Routes threat detections through a chosen strategy: block, sanitize,
228
+ * redirect, log, or custom handler.
229
+ */
230
+ class ResponseHandler {
231
+ /**
232
+ * @param {object} [options]
233
+ * @param {string} [options.strategy='block'] - Response strategy: 'block', 'sanitize', 'redirect', 'log', 'custom'.
234
+ * @param {Function} [options.customHandler] - Custom handler function (required if strategy is 'custom').
235
+ * @param {string} [options.blockMessage] - Custom block message (used with 'block' strategy).
236
+ * @param {string} [options.detailLevel='standard'] - Detail level for block messages: 'minimal', 'standard', 'verbose'.
237
+ * @param {ReviewQueue} [options.reviewQueue] - ReviewQueue instance for 'redirect' strategy.
238
+ */
239
+ constructor(options = {}) {
240
+ this.strategy = options.strategy || 'block';
241
+ this.customHandler = options.customHandler || null;
242
+ this.blockMessage = options.blockMessage || null;
243
+ this.detailLevel = options.detailLevel || 'standard';
244
+ this.reviewQueue = options.reviewQueue || null;
245
+
246
+ if (this.strategy === 'custom' && typeof this.customHandler !== 'function') {
247
+ throw new Error('A "customHandler" function is required when strategy is "custom".');
248
+ }
249
+
250
+ if (this.strategy === 'redirect' && !this.reviewQueue) {
251
+ this.reviewQueue = new ReviewQueue();
252
+ }
253
+
254
+ console.log('[Agent Shield] ResponseHandler initialized (strategy: %s)', this.strategy);
255
+ }
256
+
257
+ /**
258
+ * Handle a scan result using the configured strategy.
259
+ * @param {object} scanResult - The scan result from scanText or ToolOutputValidator.
260
+ * @param {object} [context] - Additional context (original text, tool name, etc.).
261
+ * @returns {object} Handler result: { action, response, original, threats }.
262
+ */
263
+ handle(scanResult, context = {}) {
264
+ const threats = scanResult.threats || [];
265
+ const original = context.text || context.original || null;
266
+
267
+ // If no threats, pass through
268
+ if (threats.length === 0) {
269
+ return {
270
+ action: 'pass',
271
+ response: original,
272
+ original,
273
+ threats: []
274
+ };
275
+ }
276
+
277
+ switch (this.strategy) {
278
+ case 'block':
279
+ return this._handleBlock(threats, original, context);
280
+ case 'sanitize':
281
+ return this._handleSanitize(threats, original, context);
282
+ case 'redirect':
283
+ return this._handleRedirect(threats, original, context);
284
+ case 'log':
285
+ return this._handleLog(threats, original, context);
286
+ case 'custom':
287
+ return this._handleCustom(scanResult, original, context);
288
+ default:
289
+ console.log('[Agent Shield] Unknown strategy "%s", falling back to block', this.strategy);
290
+ return this._handleBlock(threats, original, context);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Block strategy: return a safe error message.
296
+ * @param {Array} threats
297
+ * @param {string} original
298
+ * @param {object} context
299
+ * @returns {object}
300
+ * @private
301
+ */
302
+ _handleBlock(threats, original, context) {
303
+ const message = this.blockMessage || ResponseTemplates.block(threats, { detailLevel: this.detailLevel });
304
+ console.log('[Agent Shield] Response BLOCKED: %d threat(s) detected', threats.length);
305
+
306
+ return {
307
+ action: 'block',
308
+ response: message,
309
+ original,
310
+ threats
311
+ };
312
+ }
313
+
314
+ /**
315
+ * Sanitize strategy: remove threatening parts, return cleaned text.
316
+ * @param {Array} threats
317
+ * @param {string} original
318
+ * @param {object} context
319
+ * @returns {object}
320
+ * @private
321
+ */
322
+ _handleSanitize(threats, original, context) {
323
+ if (!original || typeof original !== 'string') {
324
+ return {
325
+ action: 'sanitize',
326
+ response: '',
327
+ original,
328
+ threats
329
+ };
330
+ }
331
+
332
+ // Use scanText to locate threats, then sanitize
333
+ const { OutputSanitizer } = require('./tool-output-validator');
334
+ const cleaned = OutputSanitizer.sanitize(original, {
335
+ stripInvisible: true,
336
+ redactUrls: true,
337
+ redactCode: true
338
+ });
339
+
340
+ const template = ResponseTemplates.sanitized(original, cleaned);
341
+ console.log('[Agent Shield] Response SANITIZED: %d threat(s), modified=%s', threats.length, template.modified);
342
+
343
+ return {
344
+ action: 'sanitize',
345
+ response: template.text,
346
+ original,
347
+ threats,
348
+ note: template.note
349
+ };
350
+ }
351
+
352
+ /**
353
+ * Redirect strategy: send to human review queue.
354
+ * @param {Array} threats
355
+ * @param {string} original
356
+ * @param {object} context
357
+ * @returns {object}
358
+ * @private
359
+ */
360
+ _handleRedirect(threats, original, context) {
361
+ const ticketId = this.reviewQueue.add({
362
+ threats,
363
+ content: original,
364
+ context
365
+ });
366
+
367
+ console.log('[Agent Shield] Response REDIRECTED to review queue: %s', ticketId);
368
+
369
+ return {
370
+ action: 'redirect',
371
+ response: `This response has been sent for human review. Ticket: ${ticketId}`,
372
+ original,
373
+ threats,
374
+ ticketId
375
+ };
376
+ }
377
+
378
+ /**
379
+ * Log strategy: allow through but log the threat.
380
+ * @param {Array} threats
381
+ * @param {string} original
382
+ * @param {object} context
383
+ * @returns {object}
384
+ * @private
385
+ */
386
+ _handleLog(threats, original, context) {
387
+ for (const threat of threats) {
388
+ console.log('[Agent Shield] Threat logged (pass-through): [%s] %s — %s', threat.severity, threat.category, threat.description);
389
+ }
390
+
391
+ return {
392
+ action: 'log',
393
+ response: original,
394
+ original,
395
+ threats
396
+ };
397
+ }
398
+
399
+ /**
400
+ * Custom strategy: call user's handler function.
401
+ * @param {object} scanResult
402
+ * @param {string} original
403
+ * @param {object} context
404
+ * @returns {object}
405
+ * @private
406
+ */
407
+ _handleCustom(scanResult, original, context) {
408
+ try {
409
+ const customResult = this.customHandler(scanResult, context);
410
+ console.log('[Agent Shield] Response handled by custom handler');
411
+
412
+ return {
413
+ action: 'custom',
414
+ response: customResult,
415
+ original,
416
+ threats: scanResult.threats || []
417
+ };
418
+ } catch (err) {
419
+ console.log('[Agent Shield] Custom handler error: %s — falling back to block', err.message);
420
+ return this._handleBlock(scanResult.threats || [], original, context);
421
+ }
422
+ }
423
+ }
424
+
425
+ // =========================================================================
426
+ // EXPORTS
427
+ // =========================================================================
428
+
429
+ module.exports = { ResponseHandler, ResponseTemplates, ReviewQueue };