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,321 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Circuit Breaker (#31), Shadow Mode (#33), and Rate Limiting (#5)
5
+ *
6
+ * - Circuit Breaker: Auto-shuts down an agent after too many threats in a time window.
7
+ * - Shadow Mode: Detection-only mode — logs everything, blocks nothing.
8
+ * - Rate Limiting: Tracks input patterns and flags anomalous spikes.
9
+ */
10
+
11
+ /**
12
+ * Circuit breaker states.
13
+ */
14
+ const STATE = {
15
+ CLOSED: 'closed', // Normal operation
16
+ OPEN: 'open', // Tripped — all requests blocked
17
+ HALF_OPEN: 'half_open' // Testing if safe to resume
18
+ };
19
+
20
+ class CircuitBreaker {
21
+ /**
22
+ * @param {object} [options]
23
+ * @param {number} [options.threshold=5] - Number of threats to trip the breaker.
24
+ * @param {number} [options.windowMs=60000] - Time window in ms (default: 1 minute).
25
+ * @param {number} [options.cooldownMs=300000] - Cooldown before half-open (default: 5 minutes).
26
+ * @param {Function} [options.onTrip] - Callback when breaker trips.
27
+ * @param {Function} [options.onReset] - Callback when breaker resets.
28
+ */
29
+ constructor(options = {}) {
30
+ this.threshold = options.threshold || 5;
31
+ this.windowMs = options.windowMs || 60000;
32
+ this.cooldownMs = options.cooldownMs || 300000;
33
+ this.onTrip = options.onTrip || null;
34
+ this.onReset = options.onReset || null;
35
+
36
+ this.state = STATE.CLOSED;
37
+ this.threatTimestamps = [];
38
+ this.trippedAt = null;
39
+ }
40
+
41
+ /**
42
+ * Records a threat event. Trips the breaker if threshold is exceeded.
43
+ * @param {number} [count=1] - Number of threats to record.
44
+ */
45
+ recordThreat(count = 1) {
46
+ const now = Date.now();
47
+ for (let i = 0; i < count; i++) {
48
+ this.threatTimestamps.push(now);
49
+ }
50
+
51
+ // Prune old timestamps outside the window
52
+ const cutoff = now - this.windowMs;
53
+ this.threatTimestamps = this.threatTimestamps.filter(t => t > cutoff);
54
+
55
+ if (this.state === STATE.CLOSED && this.threatTimestamps.length >= this.threshold) {
56
+ this._trip();
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Checks if the breaker allows a request through.
62
+ * @returns {object} { allowed: boolean, state: string, reason?: string }
63
+ */
64
+ check() {
65
+ if (this.state === STATE.CLOSED) {
66
+ return { allowed: true, state: this.state };
67
+ }
68
+
69
+ if (this.state === STATE.OPEN) {
70
+ const elapsed = Date.now() - this.trippedAt;
71
+ if (elapsed >= this.cooldownMs) {
72
+ this.state = STATE.HALF_OPEN;
73
+ return { allowed: true, state: this.state, reason: 'Testing after cooldown' };
74
+ }
75
+ const remainingMs = this.cooldownMs - elapsed;
76
+ return {
77
+ allowed: false,
78
+ state: this.state,
79
+ reason: `Circuit breaker tripped. Resumes in ${Math.ceil(remainingMs / 1000)}s.`
80
+ };
81
+ }
82
+
83
+ // HALF_OPEN: allow one request to test
84
+ return { allowed: true, state: this.state, reason: 'Half-open test request' };
85
+ }
86
+
87
+ /**
88
+ * Reports the result of a half-open test request.
89
+ * @param {boolean} safe - Whether the test request was safe.
90
+ */
91
+ reportTestResult(safe) {
92
+ if (this.state !== STATE.HALF_OPEN) return;
93
+
94
+ if (safe) {
95
+ this._reset();
96
+ } else {
97
+ this._trip();
98
+ }
99
+ }
100
+
101
+ /** @private */
102
+ _trip() {
103
+ this.state = STATE.OPEN;
104
+ this.trippedAt = Date.now();
105
+ if (this.onTrip) {
106
+ try {
107
+ this.onTrip({
108
+ state: STATE.OPEN,
109
+ threatCount: this.threatTimestamps.length,
110
+ timestamp: this.trippedAt
111
+ });
112
+ } catch (err) {
113
+ console.error('[Agent Shield] onTrip callback error:', err.message);
114
+ }
115
+ }
116
+ }
117
+
118
+ /** @private */
119
+ _reset() {
120
+ this.state = STATE.CLOSED;
121
+ this.threatTimestamps = [];
122
+ this.trippedAt = null;
123
+ if (this.onReset) {
124
+ try {
125
+ this.onReset({ state: STATE.CLOSED, timestamp: Date.now() });
126
+ } catch (err) {
127
+ console.error('[Agent Shield] onReset callback error:', err.message);
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Manually reset the breaker.
134
+ */
135
+ reset() {
136
+ this._reset();
137
+ }
138
+
139
+ /**
140
+ * Returns current breaker status.
141
+ * @returns {object}
142
+ */
143
+ getStatus() {
144
+ return {
145
+ state: this.state,
146
+ recentThreats: this.threatTimestamps.length,
147
+ threshold: this.threshold,
148
+ trippedAt: this.trippedAt
149
+ };
150
+ }
151
+ }
152
+
153
+ // =========================================================================
154
+ // SHADOW MODE
155
+ // =========================================================================
156
+
157
+ /**
158
+ * Wraps an AgentShield instance in shadow mode.
159
+ * Logs all detections but never blocks. Perfect for evaluation.
160
+ *
161
+ * @param {object} shield - An AgentShield instance.
162
+ * @param {object} [options]
163
+ * @param {Function} [options.logger] - Custom log function. Defaults to console.log.
164
+ * @returns {object} - A shadow-mode wrapped shield with the same API.
165
+ */
166
+ const shadowMode = (shield, options = {}) => {
167
+ const logger = options.logger || console.log;
168
+ const log = [];
169
+
170
+ const wrap = (methodName, original) => {
171
+ return function (...args) {
172
+ const result = original.apply(shield, args);
173
+
174
+ // If it's a promise (async methods like from middleware), handle accordingly
175
+ if (result && typeof result.then === 'function') {
176
+ return result.then(res => {
177
+ const entry = { method: methodName, result: res, timestamp: Date.now() };
178
+ log.push(entry);
179
+ if (log.length > 1000) log.shift();
180
+ if (res.threats && res.threats.length > 0) {
181
+ try { logger(`[Agent Shield Shadow] ${methodName}: ${res.threats.length} threat(s) detected (not blocked)`, res.threats.map(t => t.description)); } catch (e) { /* logger error */ }
182
+ }
183
+ // Never block in shadow mode
184
+ if ('blocked' in res) res.blocked = false;
185
+ return res;
186
+ });
187
+ }
188
+
189
+ const entry = { method: methodName, result, timestamp: Date.now() };
190
+ log.push(entry);
191
+ if (log.length > 1000) log.shift();
192
+
193
+ if (result.threats && result.threats.length > 0) {
194
+ try { logger(`[Agent Shield Shadow] ${methodName}: ${result.threats.length} threat(s) detected (not blocked)`, result.threats.map(t => t.description)); } catch (e) { /* logger error */ }
195
+ }
196
+
197
+ // Never block in shadow mode
198
+ if ('blocked' in result) result.blocked = false;
199
+ return result;
200
+ };
201
+ };
202
+
203
+ return {
204
+ scan: wrap('scan', shield.scan),
205
+ scanInput: wrap('scanInput', shield.scanInput),
206
+ scanOutput: wrap('scanOutput', shield.scanOutput),
207
+ scanToolCall: wrap('scanToolCall', shield.scanToolCall),
208
+ scanBatch: wrap('scanBatch', shield.scanBatch),
209
+ getStats: () => shield.getStats(),
210
+ getLog: () => [...log],
211
+ isShadowMode: true
212
+ };
213
+ };
214
+
215
+ // =========================================================================
216
+ // RATE LIMITER
217
+ // =========================================================================
218
+
219
+ class RateLimiter {
220
+ /**
221
+ * @param {object} [options]
222
+ * @param {number} [options.maxRequests=100] - Max requests per window.
223
+ * @param {number} [options.windowMs=60000] - Window size in ms.
224
+ * @param {number} [options.maxThreatsPerWindow=10] - Max threats before flagging anomaly.
225
+ * @param {Function} [options.onLimit] - Callback when rate limit hit.
226
+ * @param {Function} [options.onAnomaly] - Callback when anomaly detected.
227
+ */
228
+ constructor(options = {}) {
229
+ this.maxRequests = options.maxRequests || 100;
230
+ this.windowMs = options.windowMs || 60000;
231
+ this.maxThreatsPerWindow = options.maxThreatsPerWindow || 10;
232
+ this.onLimit = options.onLimit || null;
233
+ this.onAnomaly = options.onAnomaly || null;
234
+
235
+ this.requestTimestamps = [];
236
+ this.threatTimestamps = [];
237
+ }
238
+
239
+ /**
240
+ * Records a request. Returns whether it's allowed.
241
+ * @returns {object} { allowed: boolean, remaining: number, reason?: string }
242
+ */
243
+ recordRequest() {
244
+ const now = Date.now();
245
+ const cutoff = now - this.windowMs;
246
+
247
+ this.requestTimestamps = this.requestTimestamps.filter(t => t > cutoff);
248
+ this.requestTimestamps.push(now);
249
+
250
+ if (this.requestTimestamps.length > this.maxRequests) {
251
+ if (this.onLimit) {
252
+ try {
253
+ this.onLimit({ count: this.requestTimestamps.length, windowMs: this.windowMs });
254
+ } catch (err) {
255
+ console.error('[Agent Shield] onLimit callback error:', err.message);
256
+ }
257
+ }
258
+ return {
259
+ allowed: false,
260
+ remaining: 0,
261
+ reason: `Rate limit exceeded: ${this.requestTimestamps.length}/${this.maxRequests} requests in ${this.windowMs / 1000}s`
262
+ };
263
+ }
264
+
265
+ return {
266
+ allowed: true,
267
+ remaining: this.maxRequests - this.requestTimestamps.length
268
+ };
269
+ }
270
+
271
+ /**
272
+ * Records threat detections. Flags anomalies if spike detected.
273
+ * @param {number} [count=1] - Number of threats.
274
+ * @returns {object} { anomaly: boolean, threatCount: number }
275
+ */
276
+ recordThreat(count = 1) {
277
+ const now = Date.now();
278
+ const cutoff = now - this.windowMs;
279
+
280
+ for (let i = 0; i < count; i++) {
281
+ this.threatTimestamps.push(now);
282
+ }
283
+ this.threatTimestamps = this.threatTimestamps.filter(t => t > cutoff);
284
+
285
+ const isAnomaly = this.threatTimestamps.length >= this.maxThreatsPerWindow;
286
+ if (isAnomaly && this.onAnomaly) {
287
+ try {
288
+ this.onAnomaly({ threatCount: this.threatTimestamps.length, windowMs: this.windowMs });
289
+ } catch (err) {
290
+ console.error('[Agent Shield] onAnomaly callback error:', err.message);
291
+ }
292
+ }
293
+
294
+ return {
295
+ anomaly: isAnomaly,
296
+ threatCount: this.threatTimestamps.length
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Returns current rate limiter status.
302
+ * @returns {object}
303
+ */
304
+ getStatus() {
305
+ const now = Date.now();
306
+ const cutoff = now - this.windowMs;
307
+ return {
308
+ requests: this.requestTimestamps.filter(t => t > cutoff).length,
309
+ maxRequests: this.maxRequests,
310
+ threats: this.threatTimestamps.filter(t => t > cutoff).length,
311
+ maxThreatsPerWindow: this.maxThreatsPerWindow
312
+ };
313
+ }
314
+
315
+ reset() {
316
+ this.requestTimestamps = [];
317
+ this.threatTimestamps = [];
318
+ }
319
+ }
320
+
321
+ module.exports = { CircuitBreaker, shadowMode, RateLimiter, STATE };