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,349 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Plugin System
5
+ *
6
+ * Lets users write custom detectors as lightweight plugin objects.
7
+ * Plugins are simple objects with a detect() method that returns an array
8
+ * of threat findings. All detection runs locally — no data ever leaves
9
+ * your environment.
10
+ */
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+
15
+ // =========================================================================
16
+ // HELPERS
17
+ // =========================================================================
18
+
19
+ /**
20
+ * Get current time in ms.
21
+ * @returns {number}
22
+ */
23
+ const now = () => {
24
+ if (typeof performance !== 'undefined' && performance.now) {
25
+ return performance.now();
26
+ }
27
+ return Date.now();
28
+ };
29
+
30
+ // =========================================================================
31
+ // PLUGIN SANDBOX
32
+ // =========================================================================
33
+
34
+ /**
35
+ * Runs plugins with timeout protection and error isolation.
36
+ * Prevents a misbehaving plugin from crashing the host agent.
37
+ */
38
+ class PluginSandbox {
39
+ /**
40
+ * @param {object} [options]
41
+ * @param {number} [options.timeoutMs=100] - Maximum execution time per plugin in ms
42
+ */
43
+ constructor(options = {}) {
44
+ this.timeoutMs = options.timeoutMs || 100;
45
+ }
46
+
47
+ /**
48
+ * Execute a plugin's detect() method with timeout and error isolation.
49
+ * @param {object} plugin - Plugin object with detect() method
50
+ * @param {string} text - Text to scan
51
+ * @param {object} [options] - Options passed to detect()
52
+ * @returns {{results: Array, error: string|null, durationMs: number}}
53
+ */
54
+ run(plugin, text, options = {}) {
55
+ const start = now();
56
+ let results = [];
57
+ let error = null;
58
+
59
+ try {
60
+ // Run detection synchronously with a time check after completion.
61
+ // True preemptive timeout would require worker_threads, but for a
62
+ // lightweight zero-dependency SDK we keep it simple: run, measure,
63
+ // and flag if it exceeded the budget.
64
+ const output = plugin.detect(text, options);
65
+ const durationMs = now() - start;
66
+
67
+ if (durationMs > this.timeoutMs) {
68
+ console.log(`[Agent Shield] Plugin "${plugin.name}" exceeded timeout (${durationMs.toFixed(1)}ms > ${this.timeoutMs}ms)`);
69
+ }
70
+
71
+ if (Array.isArray(output)) {
72
+ results = output;
73
+ }
74
+
75
+ return { results, error: null, durationMs };
76
+ } catch (err) {
77
+ const durationMs = now() - start;
78
+ error = err.message || String(err);
79
+ console.log(`[Agent Shield] Plugin "${plugin.name}" threw an error: ${error}`);
80
+ return { results: [], error, durationMs };
81
+ }
82
+ }
83
+ }
84
+
85
+ // =========================================================================
86
+ // PLUGIN TEMPLATE
87
+ // =========================================================================
88
+
89
+ /**
90
+ * Helper class to create well-formed plugins from patterns or functions.
91
+ */
92
+ class PluginTemplate {
93
+ /**
94
+ * Create a plugin from pattern definitions (detector-core format).
95
+ * @param {object} config
96
+ * @param {string} config.name - Plugin name
97
+ * @param {string} [config.version='1.0.0'] - Plugin version
98
+ * @param {Array<{regex: RegExp, severity: string, category: string, description: string, detail: string}>} config.patterns
99
+ * @returns {object} A valid plugin object
100
+ */
101
+ static create({ name, version = '1.0.0', patterns = [] }) {
102
+ return {
103
+ name,
104
+ version,
105
+ detect(text) {
106
+ const findings = [];
107
+ for (const pattern of patterns) {
108
+ if (pattern.regex && pattern.regex.test(text)) {
109
+ findings.push({
110
+ severity: pattern.severity || 'medium',
111
+ category: pattern.category || name,
112
+ description: pattern.description || 'Pattern match detected',
113
+ detail: pattern.detail || ''
114
+ });
115
+ }
116
+ }
117
+ return findings;
118
+ }
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Wrap a bare detection function as a plugin object.
124
+ * @param {object} config
125
+ * @param {string} config.name - Plugin name
126
+ * @param {string} [config.version='1.0.0'] - Plugin version
127
+ * @param {function} config.detect - Detection function (text, options) => Array
128
+ * @returns {object} A valid plugin object
129
+ */
130
+ static createFromFunction({ name, version = '1.0.0', detect }) {
131
+ return { name, version, detect };
132
+ }
133
+
134
+ /**
135
+ * Validate that a plugin object has the required fields and correct types.
136
+ * @param {object} plugin - Plugin object to validate
137
+ * @returns {{valid: boolean, errors: string[]}}
138
+ */
139
+ static validate(plugin) {
140
+ const errors = [];
141
+
142
+ if (!plugin || typeof plugin !== 'object') {
143
+ return { valid: false, errors: ['Plugin must be a non-null object'] };
144
+ }
145
+ if (typeof plugin.name !== 'string' || plugin.name.length === 0) {
146
+ errors.push('Plugin must have a non-empty string "name" property');
147
+ }
148
+ if (typeof plugin.detect !== 'function') {
149
+ errors.push('Plugin must have a "detect" function');
150
+ }
151
+ if (plugin.version !== undefined && typeof plugin.version !== 'string') {
152
+ errors.push('Plugin "version" must be a string if provided');
153
+ }
154
+
155
+ return { valid: errors.length === 0, errors };
156
+ }
157
+ }
158
+
159
+ // =========================================================================
160
+ // PLUGIN MANAGER
161
+ // =========================================================================
162
+
163
+ /**
164
+ * Manages the lifecycle of detector plugins: registration, toggling,
165
+ * scanning, and per-plugin statistics.
166
+ */
167
+ class PluginManager {
168
+ /**
169
+ * Initialize an empty plugin registry.
170
+ */
171
+ constructor() {
172
+ /** @type {Map<string, {plugin: object, enabled: boolean, stats: {scans: number, threats: number, totalMs: number}}>} */
173
+ this._registry = new Map();
174
+ this._sandbox = new PluginSandbox();
175
+ }
176
+
177
+ /**
178
+ * Register a plugin object.
179
+ * @param {object} plugin - Plugin with name, version, and detect()
180
+ * @throws {Error} If plugin is invalid or name is already registered
181
+ */
182
+ register(plugin) {
183
+ const { valid, errors } = PluginTemplate.validate(plugin);
184
+ if (!valid) {
185
+ throw new Error(`[Agent Shield] Invalid plugin: ${errors.join('; ')}`);
186
+ }
187
+ if (this._registry.has(plugin.name)) {
188
+ throw new Error(`[Agent Shield] Plugin "${plugin.name}" is already registered`);
189
+ }
190
+
191
+ this._registry.set(plugin.name, {
192
+ plugin,
193
+ enabled: true,
194
+ stats: { scans: 0, threats: 0, totalMs: 0 }
195
+ });
196
+
197
+ console.log(`[Agent Shield] Registered plugin "${plugin.name}" v${plugin.version || 'unknown'}`);
198
+ }
199
+
200
+ /**
201
+ * Load and register a plugin from a .js file.
202
+ * @param {string} filePath - Absolute or relative path to a .js plugin file
203
+ */
204
+ registerFromFile(filePath) {
205
+ const resolved = path.resolve(filePath);
206
+ const plugin = require(resolved);
207
+ this.register(plugin);
208
+ }
209
+
210
+ /**
211
+ * Load all .js files from a directory as plugins.
212
+ * Skips files that fail to load and logs a warning.
213
+ * @param {string} dirPath - Path to directory containing plugin .js files
214
+ */
215
+ loadDirectory(dirPath) {
216
+ const resolved = path.resolve(dirPath);
217
+ let files;
218
+ try {
219
+ files = fs.readdirSync(resolved);
220
+ } catch (err) {
221
+ console.log(`[Agent Shield] Could not read plugin directory "${resolved}": ${err.message}`);
222
+ return;
223
+ }
224
+
225
+ const jsFiles = files.filter(f => f.endsWith('.js'));
226
+ for (const file of jsFiles) {
227
+ try {
228
+ this.registerFromFile(path.join(resolved, file));
229
+ } catch (err) {
230
+ console.log(`[Agent Shield] Failed to load plugin from "${file}": ${err.message}`);
231
+ }
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Remove a plugin from the registry.
237
+ * @param {string} name - Plugin name
238
+ * @returns {boolean} True if the plugin was found and removed
239
+ */
240
+ unregister(name) {
241
+ const removed = this._registry.delete(name);
242
+ if (removed) {
243
+ console.log(`[Agent Shield] Unregistered plugin "${name}"`);
244
+ }
245
+ return removed;
246
+ }
247
+
248
+ /**
249
+ * List all registered plugins.
250
+ * @returns {Array<{name: string, version: string, enabled: boolean}>}
251
+ */
252
+ list() {
253
+ const result = [];
254
+ for (const [name, entry] of this._registry) {
255
+ result.push({
256
+ name,
257
+ version: entry.plugin.version || 'unknown',
258
+ enabled: entry.enabled
259
+ });
260
+ }
261
+ return result;
262
+ }
263
+
264
+ /**
265
+ * Enable a registered plugin.
266
+ * @param {string} name - Plugin name
267
+ * @throws {Error} If plugin is not registered
268
+ */
269
+ enable(name) {
270
+ const entry = this._registry.get(name);
271
+ if (!entry) {
272
+ throw new Error(`[Agent Shield] Plugin "${name}" is not registered`);
273
+ }
274
+ entry.enabled = true;
275
+ console.log(`[Agent Shield] Enabled plugin "${name}"`);
276
+ }
277
+
278
+ /**
279
+ * Disable a registered plugin.
280
+ * @param {string} name - Plugin name
281
+ * @throws {Error} If plugin is not registered
282
+ */
283
+ disable(name) {
284
+ const entry = this._registry.get(name);
285
+ if (!entry) {
286
+ throw new Error(`[Agent Shield] Plugin "${name}" is not registered`);
287
+ }
288
+ entry.enabled = false;
289
+ console.log(`[Agent Shield] Disabled plugin "${name}"`);
290
+ }
291
+
292
+ /**
293
+ * Run all enabled plugins against the given text and merge results.
294
+ * @param {string} text - Text to scan
295
+ * @param {object} [options] - Options passed to each plugin's detect()
296
+ * @returns {Array<{severity: string, category: string, description: string, detail: string, plugin: string}>}
297
+ */
298
+ scan(text, options = {}) {
299
+ const merged = [];
300
+
301
+ for (const [name, entry] of this._registry) {
302
+ if (!entry.enabled) continue;
303
+
304
+ const { results, error, durationMs } = this._sandbox.run(entry.plugin, text, options);
305
+
306
+ entry.stats.scans += 1;
307
+ entry.stats.totalMs += durationMs;
308
+
309
+ if (!error) {
310
+ for (const finding of results) {
311
+ merged.push({
312
+ severity: finding.severity,
313
+ category: finding.category,
314
+ description: finding.description,
315
+ detail: finding.detail || '',
316
+ plugin: name
317
+ });
318
+ }
319
+ entry.stats.threats += results.length;
320
+ }
321
+ }
322
+
323
+ return merged;
324
+ }
325
+
326
+ /**
327
+ * Get per-plugin scan statistics.
328
+ * @returns {Array<{name: string, scans: number, threats: number, avgMs: number}>}
329
+ */
330
+ getStats() {
331
+ const result = [];
332
+ for (const [name, entry] of this._registry) {
333
+ const { scans, threats, totalMs } = entry.stats;
334
+ result.push({
335
+ name,
336
+ scans,
337
+ threats,
338
+ avgMs: scans > 0 ? Math.round((totalMs / scans) * 100) / 100 : 0
339
+ });
340
+ }
341
+ return result;
342
+ }
343
+ }
344
+
345
+ // =========================================================================
346
+ // EXPORTS
347
+ // =========================================================================
348
+
349
+ module.exports = { PluginManager, PluginTemplate, PluginSandbox };