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.
- package/CHANGELOG.md +191 -0
- package/LICENSE +21 -0
- package/README.md +975 -0
- package/bin/agent-shield.js +680 -0
- package/package.json +118 -0
- package/src/adaptive.js +330 -0
- package/src/agent-protocol.js +998 -0
- package/src/alert-tuning.js +480 -0
- package/src/allowlist.js +603 -0
- package/src/audit-immutable.js +914 -0
- package/src/audit-streaming.js +469 -0
- package/src/badges.js +196 -0
- package/src/behavior-profiling.js +289 -0
- package/src/benchmark-harness.js +804 -0
- package/src/canary.js +271 -0
- package/src/certification.js +563 -0
- package/src/circuit-breaker.js +321 -0
- package/src/compliance.js +617 -0
- package/src/confidence-tuning.js +324 -0
- package/src/confused-deputy.js +624 -0
- package/src/context-scoring.js +360 -0
- package/src/conversation.js +494 -0
- package/src/cost-optimizer.js +1024 -0
- package/src/ctf.js +462 -0
- package/src/detector-core.js +1999 -0
- package/src/distributed.js +359 -0
- package/src/document-scanner.js +795 -0
- package/src/embedding.js +307 -0
- package/src/encoding.js +429 -0
- package/src/enterprise.js +405 -0
- package/src/errors.js +100 -0
- package/src/eu-ai-act.js +523 -0
- package/src/fuzzer.js +764 -0
- package/src/honeypot.js +328 -0
- package/src/i18n-patterns.js +523 -0
- package/src/index.js +430 -0
- package/src/integrations.js +528 -0
- package/src/llm-redteam.js +670 -0
- package/src/main.js +741 -0
- package/src/main.mjs +38 -0
- package/src/mcp-bridge.js +542 -0
- package/src/mcp-certification.js +846 -0
- package/src/mcp-sdk-integration.js +355 -0
- package/src/mcp-security-runtime.js +741 -0
- package/src/mcp-server.js +740 -0
- package/src/middleware.js +208 -0
- package/src/model-finetuning.js +884 -0
- package/src/model-fingerprint.js +1042 -0
- package/src/multi-agent-trust.js +453 -0
- package/src/multi-agent.js +404 -0
- package/src/multimodal.js +296 -0
- package/src/nist-mapping.js +505 -0
- package/src/observability.js +330 -0
- package/src/openclaw.js +450 -0
- package/src/otel.js +544 -0
- package/src/owasp-2025.js +483 -0
- package/src/pii.js +390 -0
- package/src/plugin-marketplace.js +628 -0
- package/src/plugin-system.js +349 -0
- package/src/policy-dsl.js +775 -0
- package/src/policy-extended.js +635 -0
- package/src/policy.js +443 -0
- package/src/presets.js +409 -0
- package/src/production.js +557 -0
- package/src/prompt-leakage.js +321 -0
- package/src/rag-vulnerability.js +579 -0
- package/src/redteam.js +475 -0
- package/src/response-handler.js +429 -0
- package/src/scanners.js +357 -0
- package/src/self-healing.js +363 -0
- package/src/semantic.js +339 -0
- package/src/shield-score.js +250 -0
- package/src/sso-saml.js +897 -0
- package/src/stream-scanner.js +806 -0
- package/src/testing.js +505 -0
- package/src/threat-encyclopedia.js +629 -0
- package/src/threat-intel-network.js +1017 -0
- package/src/token-analysis.js +467 -0
- package/src/tool-guard.js +412 -0
- package/src/tool-output-validator.js +354 -0
- package/src/utils.js +83 -0
- package/src/watermark.js +235 -0
- package/src/worker-scanner.js +601 -0
- package/types/index.d.ts +2088 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Shield — Official MCP SDK Integration
|
|
5
|
+
*
|
|
6
|
+
* Drop-in security for any MCP server built with @modelcontextprotocol/sdk.
|
|
7
|
+
* Wraps the standard Server class to add threat scanning, authorization,
|
|
8
|
+
* behavioral monitoring, and audit logging — with zero configuration required.
|
|
9
|
+
*
|
|
10
|
+
* Works with @modelcontextprotocol/sdk v1.x and v2.x.
|
|
11
|
+
*
|
|
12
|
+
* Usage (3 lines):
|
|
13
|
+
*
|
|
14
|
+
* const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
15
|
+
* const { shieldMCPServer } = require('agent-shield');
|
|
16
|
+
*
|
|
17
|
+
* const server = shieldMCPServer(new Server({ name: 'my-server', version: '1.0' }));
|
|
18
|
+
* // All tool calls are now scanned. Injections blocked. Audit trail created.
|
|
19
|
+
*
|
|
20
|
+
* Advanced usage with MCPSecurityRuntime:
|
|
21
|
+
*
|
|
22
|
+
* const server = shieldMCPServer(new Server({ ... }), {
|
|
23
|
+
* signingKey: process.env.SHIELD_KEY,
|
|
24
|
+
* enforceAuth: true,
|
|
25
|
+
* enableBehaviorMonitoring: true,
|
|
26
|
+
* onThreat: (event) => alertSecurityTeam(event),
|
|
27
|
+
* tools: {
|
|
28
|
+
* 'database_query': { scopes: ['db:read'], roles: ['analyst'] },
|
|
29
|
+
* 'file_delete': { scopes: ['fs:write'], roles: ['admin'], requiresHumanApproval: true }
|
|
30
|
+
* }
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* All processing runs locally — no data ever leaves your environment.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const { scanText } = require('./detector-core');
|
|
37
|
+
const { MCPSecurityRuntime } = require('./mcp-security-runtime');
|
|
38
|
+
|
|
39
|
+
const LOG_PREFIX = '[Agent Shield]';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Wraps a @modelcontextprotocol/sdk Server with Agent Shield security.
|
|
43
|
+
*
|
|
44
|
+
* Intercepts tools/call requests to scan arguments for injection attacks,
|
|
45
|
+
* scans tool results before returning, and maintains an audit trail.
|
|
46
|
+
*
|
|
47
|
+
* @param {object} server - @modelcontextprotocol/sdk Server instance
|
|
48
|
+
* @param {object} [options]
|
|
49
|
+
* @param {string} [options.signingKey] - HMAC key for auth context signing
|
|
50
|
+
* @param {boolean} [options.enforceAuth=false] - Require per-user auth (advanced)
|
|
51
|
+
* @param {boolean} [options.enableBehaviorMonitoring=true] - Track behavioral anomalies
|
|
52
|
+
* @param {boolean} [options.scanInputs=true] - Scan tool call arguments
|
|
53
|
+
* @param {boolean} [options.scanOutputs=true] - Scan tool results
|
|
54
|
+
* @param {string} [options.sensitivity='medium'] - Detection sensitivity
|
|
55
|
+
* @param {boolean} [options.blockOnThreat=true] - Block tool calls with detected threats
|
|
56
|
+
* @param {Function} [options.onThreat] - Callback when threat detected
|
|
57
|
+
* @param {Function} [options.onBlock] - Callback when tool call blocked
|
|
58
|
+
* @param {object} [options.tools] - Per-tool security requirements
|
|
59
|
+
* @returns {object} The same server instance, now secured
|
|
60
|
+
*/
|
|
61
|
+
function shieldMCPServer(server, options = {}) {
|
|
62
|
+
if (!server) {
|
|
63
|
+
throw new Error(`${LOG_PREFIX} shieldMCPServer requires a @modelcontextprotocol/sdk Server instance`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const scanInputs = options.scanInputs !== false;
|
|
67
|
+
const scanOutputs = options.scanOutputs !== false;
|
|
68
|
+
const blockOnThreat = options.blockOnThreat !== false;
|
|
69
|
+
const sensitivity = options.sensitivity || 'medium';
|
|
70
|
+
const onThreat = options.onThreat || null;
|
|
71
|
+
const onBlock = options.onBlock || null;
|
|
72
|
+
|
|
73
|
+
// Initialize runtime if auth is enabled
|
|
74
|
+
let runtime = null;
|
|
75
|
+
if (options.enforceAuth || options.signingKey) {
|
|
76
|
+
runtime = new MCPSecurityRuntime({
|
|
77
|
+
signingKey: options.signingKey,
|
|
78
|
+
enforceAuth: options.enforceAuth,
|
|
79
|
+
enableBehaviorMonitoring: options.enableBehaviorMonitoring !== false,
|
|
80
|
+
enableStateMachine: true,
|
|
81
|
+
maxDelegationDepth: options.maxDelegationDepth || 5,
|
|
82
|
+
onThreat: options.onThreat,
|
|
83
|
+
onBlock: options.onBlock,
|
|
84
|
+
onAudit: options.onAudit
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Register tool requirements
|
|
88
|
+
if (options.tools) {
|
|
89
|
+
for (const [toolName, requirements] of Object.entries(options.tools)) {
|
|
90
|
+
runtime.registerTool(toolName, requirements);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Audit log for non-runtime mode
|
|
96
|
+
const auditLog = [];
|
|
97
|
+
const maxAuditEntries = 10000;
|
|
98
|
+
|
|
99
|
+
// Stats
|
|
100
|
+
const stats = {
|
|
101
|
+
toolCallsScanned: 0,
|
|
102
|
+
toolCallsBlocked: 0,
|
|
103
|
+
toolResultsScanned: 0,
|
|
104
|
+
threatsDetected: 0
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Intercept tool handler registration
|
|
108
|
+
const originalSetRequestHandler = server.setRequestHandler
|
|
109
|
+
? server.setRequestHandler.bind(server)
|
|
110
|
+
: null;
|
|
111
|
+
|
|
112
|
+
if (originalSetRequestHandler) {
|
|
113
|
+
server.setRequestHandler = function (schema, handler) {
|
|
114
|
+
// Check if this is a tools/call handler
|
|
115
|
+
const schemaMethod = schema && (schema.method || schema);
|
|
116
|
+
if (schemaMethod === 'tools/call' || (typeof schemaMethod === 'object' && schemaMethod.method === 'tools/call')) {
|
|
117
|
+
const wrappedHandler = async (request, extra) => {
|
|
118
|
+
const toolName = request.params && request.params.name;
|
|
119
|
+
const args = request.params && request.params.arguments;
|
|
120
|
+
|
|
121
|
+
// Scan tool call arguments
|
|
122
|
+
if (scanInputs && args) {
|
|
123
|
+
stats.toolCallsScanned++;
|
|
124
|
+
const argsText = typeof args === 'string' ? args : JSON.stringify(args);
|
|
125
|
+
const scanResult = scanText(argsText, { sensitivity });
|
|
126
|
+
|
|
127
|
+
if (scanResult.threats && scanResult.threats.length > 0) {
|
|
128
|
+
stats.threatsDetected += scanResult.threats.length;
|
|
129
|
+
|
|
130
|
+
_audit('threat_detected', {
|
|
131
|
+
toolName,
|
|
132
|
+
threats: scanResult.threats.map(t => ({ category: t.category, severity: t.severity })),
|
|
133
|
+
blocked: blockOnThreat
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (onThreat) {
|
|
137
|
+
try { onThreat({ toolName, args, threats: scanResult.threats }); } catch (_e) { /* */ }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (blockOnThreat) {
|
|
141
|
+
stats.toolCallsBlocked++;
|
|
142
|
+
if (onBlock) {
|
|
143
|
+
try { onBlock({ toolName, args, threats: scanResult.threats }); } catch (_e) { /* */ }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
content: [{
|
|
148
|
+
type: 'text',
|
|
149
|
+
text: `[Agent Shield] Tool call blocked: ${scanResult.threats.map(t => t.category).join(', ')} detected in arguments`
|
|
150
|
+
}],
|
|
151
|
+
isError: true
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Runtime auth check (if enabled)
|
|
158
|
+
if (runtime && extra && extra.sessionId) {
|
|
159
|
+
const result = runtime.secureToolCall(extra.sessionId, toolName, args);
|
|
160
|
+
if (!result.allowed) {
|
|
161
|
+
stats.toolCallsBlocked++;
|
|
162
|
+
return {
|
|
163
|
+
content: [{
|
|
164
|
+
type: 'text',
|
|
165
|
+
text: `[Agent Shield] Authorization denied: ${result.reason || result.violations.map(v => v.message).join('; ')}`
|
|
166
|
+
}],
|
|
167
|
+
isError: true
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_audit('tool_allowed', { toolName });
|
|
173
|
+
|
|
174
|
+
// Call original handler
|
|
175
|
+
const result = await handler(request, extra);
|
|
176
|
+
|
|
177
|
+
// Scan tool result
|
|
178
|
+
if (scanOutputs && result && result.content) {
|
|
179
|
+
stats.toolResultsScanned++;
|
|
180
|
+
for (const item of result.content) {
|
|
181
|
+
if (item.type === 'text' && item.text) {
|
|
182
|
+
const outputScan = scanText(item.text, { sensitivity });
|
|
183
|
+
if (outputScan.threats && outputScan.threats.length > 0) {
|
|
184
|
+
stats.threatsDetected += outputScan.threats.length;
|
|
185
|
+
_audit('output_threat', {
|
|
186
|
+
toolName,
|
|
187
|
+
threats: outputScan.threats.map(t => ({ category: t.category, severity: t.severity }))
|
|
188
|
+
});
|
|
189
|
+
if (onThreat) {
|
|
190
|
+
try { onThreat({ toolName, threats: outputScan.threats, direction: 'output' }); } catch (_e) { /* */ }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return result;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
return originalSetRequestHandler(schema, wrappedHandler);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Pass through non-tool handlers unchanged
|
|
204
|
+
return originalSetRequestHandler(schema, handler);
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Attach shield API to server instance
|
|
209
|
+
server.agentShield = {
|
|
210
|
+
/** Get scanning stats */
|
|
211
|
+
getStats() {
|
|
212
|
+
return runtime ? runtime.getReport() : { ...stats, auditEntries: auditLog.length };
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
/** Get the audit log */
|
|
216
|
+
getAuditLog(limit = 100) {
|
|
217
|
+
return runtime ? runtime.getAuditLog(limit) : auditLog.slice(-limit);
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
/** Get the runtime (if auth is enabled) */
|
|
221
|
+
getRuntime() {
|
|
222
|
+
return runtime;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
/** Create an authenticated session (requires enforceAuth) */
|
|
226
|
+
createSession(params) {
|
|
227
|
+
if (!runtime) throw new Error(`${LOG_PREFIX} createSession requires enforceAuth or signingKey`);
|
|
228
|
+
return runtime.createSession(params);
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/** Terminate a session */
|
|
232
|
+
terminateSession(sessionId) {
|
|
233
|
+
if (!runtime) return false;
|
|
234
|
+
return runtime.terminateSession(sessionId);
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
/** Shutdown the security runtime */
|
|
238
|
+
shutdown() {
|
|
239
|
+
if (runtime) runtime.shutdown();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
function _audit(type, data) {
|
|
244
|
+
if (auditLog.length >= maxAuditEntries) {
|
|
245
|
+
auditLog.splice(0, auditLog.length - Math.floor(maxAuditEntries * 0.75));
|
|
246
|
+
}
|
|
247
|
+
auditLog.push({ type, timestamp: Date.now(), ...data });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log(`${LOG_PREFIX} MCP server secured (scanInputs: ${scanInputs}, scanOutputs: ${scanOutputs}, blockOnThreat: ${blockOnThreat}, auth: ${!!runtime})`);
|
|
251
|
+
|
|
252
|
+
return server;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Creates a standalone security middleware for MCP servers that don't use
|
|
257
|
+
* the official SDK. Works with any JSON-RPC 2.0 MCP implementation.
|
|
258
|
+
*
|
|
259
|
+
* @param {object} [options] - Same options as shieldMCPServer
|
|
260
|
+
* @returns {object} Middleware with processRequest/processResponse methods
|
|
261
|
+
*/
|
|
262
|
+
function createMCPSecurityLayer(options = {}) {
|
|
263
|
+
const scanInputs = options.scanInputs !== false;
|
|
264
|
+
const scanOutputs = options.scanOutputs !== false;
|
|
265
|
+
const blockOnThreat = options.blockOnThreat !== false;
|
|
266
|
+
const sensitivity = options.sensitivity || 'medium';
|
|
267
|
+
|
|
268
|
+
let runtime = null;
|
|
269
|
+
if (options.enforceAuth || options.signingKey) {
|
|
270
|
+
runtime = new MCPSecurityRuntime({
|
|
271
|
+
signingKey: options.signingKey,
|
|
272
|
+
enforceAuth: options.enforceAuth,
|
|
273
|
+
enableBehaviorMonitoring: options.enableBehaviorMonitoring !== false,
|
|
274
|
+
onThreat: options.onThreat,
|
|
275
|
+
onBlock: options.onBlock
|
|
276
|
+
});
|
|
277
|
+
if (options.tools) {
|
|
278
|
+
for (const [toolName, requirements] of Object.entries(options.tools)) {
|
|
279
|
+
runtime.registerTool(toolName, requirements);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
/**
|
|
286
|
+
* Process an incoming MCP request before handling.
|
|
287
|
+
* @param {object} request - JSON-RPC 2.0 request
|
|
288
|
+
* @param {object} [context] - Optional context (sessionId, userId, etc.)
|
|
289
|
+
* @returns {{ allowed: boolean, threats: Array, request: object }}
|
|
290
|
+
*/
|
|
291
|
+
processRequest(request, context = {}) {
|
|
292
|
+
if (!request || request.method !== 'tools/call') {
|
|
293
|
+
return { allowed: true, threats: [], request };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const toolName = request.params && request.params.name;
|
|
297
|
+
const args = request.params && request.params.arguments;
|
|
298
|
+
|
|
299
|
+
// Runtime auth check
|
|
300
|
+
if (runtime && context.sessionId) {
|
|
301
|
+
const result = runtime.secureToolCall(context.sessionId, toolName, args);
|
|
302
|
+
if (!result.allowed) {
|
|
303
|
+
return { allowed: false, threats: result.threats, violations: result.violations, request };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Scan arguments
|
|
308
|
+
if (scanInputs && args) {
|
|
309
|
+
const argsText = typeof args === 'string' ? args : JSON.stringify(args);
|
|
310
|
+
const scanResult = scanText(argsText, { sensitivity });
|
|
311
|
+
if (scanResult.threats && scanResult.threats.length > 0 && blockOnThreat) {
|
|
312
|
+
return { allowed: false, threats: scanResult.threats, request };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { allowed: true, threats: [], request };
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Process an MCP response before returning.
|
|
321
|
+
* @param {object} response - JSON-RPC 2.0 response
|
|
322
|
+
* @returns {{ safe: boolean, threats: Array, response: object }}
|
|
323
|
+
*/
|
|
324
|
+
processResponse(response, _context = {}) {
|
|
325
|
+
if (!scanOutputs || !response || !response.result || !response.result.content) {
|
|
326
|
+
return { safe: true, threats: [], response };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const allThreats = [];
|
|
330
|
+
for (const item of response.result.content) {
|
|
331
|
+
if (item.type === 'text' && item.text) {
|
|
332
|
+
const scanResult = scanText(item.text, { sensitivity });
|
|
333
|
+
if (scanResult.threats) allThreats.push(...scanResult.threats);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return { safe: allThreats.length === 0, threats: allThreats, response };
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
/** Get the runtime (if auth is enabled) */
|
|
341
|
+
getRuntime() { return runtime; },
|
|
342
|
+
|
|
343
|
+
/** Shutdown */
|
|
344
|
+
shutdown() { if (runtime) runtime.shutdown(); }
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// =========================================================================
|
|
349
|
+
// Exports
|
|
350
|
+
// =========================================================================
|
|
351
|
+
|
|
352
|
+
module.exports = {
|
|
353
|
+
shieldMCPServer,
|
|
354
|
+
createMCPSecurityLayer
|
|
355
|
+
};
|