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,330 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Observability Module
5
+ *
6
+ * Production-grade observability: Prometheus metrics export, structured JSON
7
+ * logging (Datadog/Splunk/ELK), and in-memory metrics aggregation.
8
+ * All processing runs locally — no data ever leaves your environment.
9
+ */
10
+
11
+ // -- PrometheusExporter ------------------------------------------------------
12
+
13
+ /** Exports metrics in Prometheus exposition format. */
14
+ class PrometheusExporter {
15
+ /** @param {object} [options] @param {string} [options.prefix=''] */
16
+ constructor(options = {}) {
17
+ this._prefix = options.prefix || '';
18
+ this._counters = new Map(); // Map<name, Map<labelKey, number>>
19
+ this._histograms = new Map(); // Map<name, Map<labelKey, number[]>>
20
+ this._gauges = new Map(); // Map<name, Map<labelKey, number>>
21
+ }
22
+
23
+ /** @param {object} labels @returns {string} */
24
+ _labelKey(labels) {
25
+ const keys = Object.keys(labels).sort();
26
+ return keys.length === 0 ? '' : keys.map(k => `${k}="${labels[k]}"`).join(',');
27
+ }
28
+
29
+ _fmt(key) { return key ? `{${key}}` : ''; }
30
+
31
+ /**
32
+ * Increment a counter.
33
+ * @param {string} name @param {object} [labels={}] @param {number} [value=1]
34
+ */
35
+ increment(name, labels = {}, value = 1) {
36
+ const full = this._prefix + name;
37
+ if (!this._counters.has(full)) this._counters.set(full, new Map());
38
+ const key = this._labelKey(labels);
39
+ const map = this._counters.get(full);
40
+ map.set(key, (map.get(key) || 0) + value);
41
+ }
42
+
43
+ /**
44
+ * Observe a histogram value.
45
+ * @param {string} name @param {number} value @param {object} [labels={}]
46
+ */
47
+ observe(name, value, labels = {}) {
48
+ const full = this._prefix + name;
49
+ if (!this._histograms.has(full)) this._histograms.set(full, new Map());
50
+ const key = this._labelKey(labels);
51
+ const map = this._histograms.get(full);
52
+ if (!map.has(key)) map.set(key, []);
53
+ map.get(key).push(value);
54
+ }
55
+
56
+ /**
57
+ * Set a gauge value.
58
+ * @param {string} name @param {number} value @param {object} [labels={}]
59
+ */
60
+ set(name, value, labels = {}) {
61
+ const full = this._prefix + name;
62
+ if (!this._gauges.has(full)) this._gauges.set(full, new Map());
63
+ this._gauges.get(full).set(this._labelKey(labels), value);
64
+ }
65
+
66
+ /**
67
+ * Returns all metrics in Prometheus exposition text format.
68
+ * @returns {string}
69
+ */
70
+ metrics() {
71
+ const lines = [];
72
+ for (const [name, map] of this._counters) {
73
+ lines.push(`# TYPE ${name} counter`);
74
+ for (const [key, val] of map) {
75
+ lines.push(`${name}${this._fmt(key)} ${val}`);
76
+ }
77
+ }
78
+ for (const [name, map] of this._histograms) {
79
+ lines.push(`# TYPE ${name} histogram`);
80
+ for (const [key, values] of map) {
81
+ const sorted = values.slice().sort((a, b) => a - b);
82
+ const sum = values.reduce((a, b) => a + b, 0);
83
+ const count = values.length;
84
+ const extra = key ? `,${key}` : '';
85
+ const buckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
86
+ for (const b of buckets) {
87
+ lines.push(`${name}_bucket{le="${b}"${extra}} ${sorted.filter(v => v <= b).length}`);
88
+ }
89
+ lines.push(`${name}_bucket{le="+Inf"${extra}} ${count}`);
90
+ lines.push(`${name}_sum${this._fmt(key)} ${sum}`);
91
+ lines.push(`${name}_count${this._fmt(key)} ${count}`);
92
+ }
93
+ }
94
+ for (const [name, map] of this._gauges) {
95
+ lines.push(`# TYPE ${name} gauge`);
96
+ for (const [key, val] of map) {
97
+ lines.push(`${name}${this._fmt(key)} ${val}`);
98
+ }
99
+ }
100
+ return lines.join('\n') + '\n';
101
+ }
102
+
103
+ /**
104
+ * Wraps an AgentShield instance to auto-record metrics on every scan.
105
+ * Pre-built metrics: shield_scans_total, shield_threats_total,
106
+ * shield_scan_duration_seconds, shield_blocks_total.
107
+ * @param {object} shield - An AgentShield instance.
108
+ * @returns {object} The same shield instance (mutated).
109
+ */
110
+ wrapShield(shield) {
111
+ const exporter = this;
112
+ const origScan = shield.scan.bind(shield);
113
+ shield.scan = function wrappedScan(text, options = {}) {
114
+ const start = Date.now();
115
+ const result = origScan(text, options);
116
+ const durationSec = (Date.now() - start) / 1000;
117
+ const source = options.source || 'unknown';
118
+ exporter.increment('shield_scans_total', { source });
119
+ exporter.observe('shield_scan_duration_seconds', durationSec, { source });
120
+ if (result.threats && result.threats.length > 0) {
121
+ for (const threat of result.threats) {
122
+ exporter.increment('shield_threats_total', {
123
+ severity: threat.severity || 'unknown',
124
+ category: threat.category || 'unknown'
125
+ });
126
+ }
127
+ }
128
+ if (result.blocked) {
129
+ exporter.increment('shield_blocks_total', { source });
130
+ }
131
+ return result;
132
+ };
133
+ console.log('[Agent Shield] Prometheus metrics wired to shield instance');
134
+ return shield;
135
+ }
136
+ }
137
+
138
+ // -- DatadogLogger -----------------------------------------------------------
139
+
140
+ /**
141
+ * Structured JSON logger formatted for Datadog, Splunk, and ELK ingestion.
142
+ * Buffers log entries and flushes to stdout.
143
+ */
144
+ class DatadogLogger {
145
+ /**
146
+ * @param {object} [options]
147
+ * @param {string} [options.service='agent-shield'] - Service name.
148
+ * @param {string} [options.env='production'] - Environment name.
149
+ * @param {string} [options.version='1.0.0'] - Service version.
150
+ * @param {number} [options.maxSize=100] - Max buffer size before auto-flush.
151
+ * @param {function} [options.writer] - Custom writer fn (default: stdout).
152
+ */
153
+ constructor(options = {}) {
154
+ this.service = options.service || 'agent-shield';
155
+ this.env = options.env || 'production';
156
+ this.version = options.version || '1.0.0';
157
+ this.maxSize = options.maxSize || 100;
158
+ this._writer = options.writer || ((line) => process.stdout.write(line + '\n'));
159
+ this._buffer = [];
160
+ }
161
+
162
+ /** @returns {string} Pseudo-random 64-bit hex ID. */
163
+ _randomId() {
164
+ let id = '';
165
+ for (let i = 0; i < 16; i++) id += Math.floor(Math.random() * 16).toString(16);
166
+ return id;
167
+ }
168
+
169
+ /**
170
+ * Emit a structured JSON log entry.
171
+ * @param {string} event - Event name.
172
+ * @param {object} [data={}] - Additional data fields.
173
+ * @param {string} [level='info'] - Log level.
174
+ */
175
+ log(event, data = {}, level = 'info') {
176
+ const entry = {
177
+ timestamp: new Date().toISOString(),
178
+ level,
179
+ service: this.service,
180
+ env: this.env,
181
+ version: this.version,
182
+ 'dd.trace_id': data['dd.trace_id'] || this._randomId(),
183
+ 'dd.span_id': data['dd.span_id'] || this._randomId(),
184
+ event,
185
+ ...data
186
+ };
187
+ this._buffer.push(entry);
188
+ if (this._buffer.length >= this.maxSize) this.flush();
189
+ }
190
+
191
+ /**
192
+ * Log a scan result.
193
+ * @param {object} result - Scan result from AgentShield.scan().
194
+ * @param {object} [metadata={}]
195
+ */
196
+ logScan(result, metadata = {}) {
197
+ this.log('shield.scan', {
198
+ status: result.status,
199
+ threatCount: result.threats ? result.threats.length : 0,
200
+ source: result.source || 'unknown',
201
+ blocked: !!result.blocked,
202
+ ...metadata
203
+ }, 'info');
204
+ }
205
+
206
+ /**
207
+ * Log a detected threat.
208
+ * @param {object} threat - Threat object.
209
+ * @param {object} [metadata={}]
210
+ */
211
+ logThreat(threat, metadata = {}) {
212
+ this.log('shield.threat', {
213
+ severity: threat.severity || 'unknown',
214
+ category: threat.category || 'unknown',
215
+ description: threat.description || '',
216
+ ...metadata
217
+ }, 'warn');
218
+ }
219
+
220
+ /**
221
+ * Log a blocked request.
222
+ * @param {string} reason - Reason for the block.
223
+ * @param {object} [metadata={}]
224
+ */
225
+ logBlock(reason, metadata = {}) {
226
+ this.log('shield.block', { reason, ...metadata }, 'error');
227
+ }
228
+
229
+ /** Flush the buffer: write all entries to stdout and clear. */
230
+ flush() {
231
+ for (const entry of this._buffer) this._writer(JSON.stringify(entry));
232
+ this._buffer = [];
233
+ }
234
+
235
+ /** @returns {number} Current buffer length. */
236
+ get bufferSize() { return this._buffer.length; }
237
+ }
238
+
239
+ // -- MetricsCollector --------------------------------------------------------
240
+
241
+ /**
242
+ * In-memory metrics aggregation for dashboards.
243
+ * Records scan/threat/block events and computes windowed summaries.
244
+ */
245
+ class MetricsCollector {
246
+ /**
247
+ * @param {object} [options]
248
+ * @param {number} [options.ttl=3600000] - TTL for events in ms (default 1h).
249
+ * @param {number} [options.pruneInterval=60000] - Auto-prune interval in ms.
250
+ */
251
+ constructor(options = {}) {
252
+ this.ttl = options.ttl || 3600000;
253
+ this._events = [];
254
+ this._pruneInterval = options.pruneInterval || 60000;
255
+ this._timer = setInterval(() => this._prune(), this._pruneInterval);
256
+ if (this._timer.unref) this._timer.unref();
257
+ }
258
+
259
+ /**
260
+ * Record an event.
261
+ * @param {object} event
262
+ * @param {string} event.type - One of 'scan', 'threat', 'block'.
263
+ * @param {number} [event.duration] - Scan duration in ms.
264
+ * @param {string} [event.category] - Threat category.
265
+ * @param {string} [event.severity] - Threat severity.
266
+ */
267
+ record(event) {
268
+ this._events.push({ ...event, timestamp: event.timestamp || Date.now() });
269
+ }
270
+
271
+ /** Remove events older than the TTL. */
272
+ _prune() {
273
+ const cutoff = Date.now() - this.ttl;
274
+ this._events = this._events.filter(e => e.timestamp >= cutoff);
275
+ }
276
+
277
+ /** @param {number[]} sorted @param {number} p @returns {number} */
278
+ _percentile(sorted, p) {
279
+ if (sorted.length === 0) return 0;
280
+ return sorted[Math.max(0, Math.ceil(p * sorted.length) - 1)];
281
+ }
282
+
283
+ /**
284
+ * Returns a summary of metrics for a given time window.
285
+ * @param {number} [windowMs=60000] - Window size in ms (default 60s).
286
+ * @returns {object} Summary: scansPerSec, threatsPerSec, p50/p95/p99, topCategories.
287
+ */
288
+ getSummary(windowMs = 60000) {
289
+ const cutoff = Date.now() - windowMs;
290
+ const win = this._events.filter(e => e.timestamp >= cutoff);
291
+ const windowSec = windowMs / 1000;
292
+ const scans = win.filter(e => e.type === 'scan');
293
+ const threats = win.filter(e => e.type === 'threat');
294
+ const blocks = win.filter(e => e.type === 'block');
295
+ const durations = scans
296
+ .filter(e => typeof e.duration === 'number')
297
+ .map(e => e.duration)
298
+ .sort((a, b) => a - b);
299
+ const categoryCounts = {};
300
+ for (const t of threats) {
301
+ const cat = t.category || 'unknown';
302
+ categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
303
+ }
304
+ const topCategories = Object.entries(categoryCounts)
305
+ .sort((a, b) => b[1] - a[1])
306
+ .slice(0, 10)
307
+ .map(([category, count]) => ({ category, count }));
308
+ return {
309
+ scansPerSec: scans.length / windowSec,
310
+ threatsPerSec: threats.length / windowSec,
311
+ blocksPerSec: blocks.length / windowSec,
312
+ totalScans: scans.length,
313
+ totalThreats: threats.length,
314
+ totalBlocks: blocks.length,
315
+ p50: this._percentile(durations, 0.5),
316
+ p95: this._percentile(durations, 0.95),
317
+ p99: this._percentile(durations, 0.99),
318
+ topCategories
319
+ };
320
+ }
321
+
322
+ /** Stop the auto-prune timer. */
323
+ destroy() {
324
+ if (this._timer) { clearInterval(this._timer); this._timer = null; }
325
+ }
326
+ }
327
+
328
+ // -- Exports -----------------------------------------------------------------
329
+
330
+ module.exports = { PrometheusExporter, DatadogLogger, MetricsCollector };