agent-security-scanner-mcp 3.6.0 → 3.8.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/README.md +42 -8
- package/analyzer.py +22 -5
- package/cross_file_analyzer.py +216 -0
- package/daemon.py +179 -0
- package/generic_ast.py +7 -2
- package/index.js +279 -3
- package/package.json +19 -5
- package/packages/npm-bloom.json +1 -0
- package/pattern_matcher.py +1 -0
- package/regex_fallback.py +199 -1
- package/requirements.txt +1 -0
- package/rules/prompt-injection.security.yaml +273 -41
- package/scripts/postinstall.js +60 -0
- package/skills/openclaw/SKILL.md +102 -0
- package/skills/security-review.md +139 -0
- package/skills/security-scan-batch.md +107 -0
- package/skills/security-scanner.md +76 -0
- package/src/cli/doctor.js +29 -1
- package/src/cli/init.js +93 -0
- package/src/cli/report.js +444 -0
- package/src/config.js +247 -0
- package/src/context.js +289 -0
- package/src/daemon-client.js +233 -0
- package/src/dedup.js +129 -0
- package/src/fix-patterns.js +76 -19
- package/src/history.js +159 -0
- package/src/tools/check-package.js +36 -12
- package/src/tools/fix-security.js +32 -5
- package/src/tools/import-resolver.js +249 -0
- package/src/tools/project-context.js +365 -0
- package/src/tools/scan-action.js +489 -0
- package/src/tools/scan-mcp.js +588 -0
- package/src/tools/scan-project.js +16 -4
- package/src/tools/scan-prompt.js +292 -527
- package/src/tools/scan-security.js +37 -6
- package/src/typosquat.js +210 -0
- package/src/utils.js +215 -8
- package/taint_analyzer.py +516 -11
- package/templates/gitlab-ci-security.yml +225 -0
- package/templates/pre-commit-hook.sh +233 -0
package/index.js
CHANGED
|
@@ -11,15 +11,21 @@ import { homedir, platform } from "os";
|
|
|
11
11
|
import { createInterface } from "readline";
|
|
12
12
|
import { createHash } from "crypto";
|
|
13
13
|
import { envVarReplacement, FIX_TEMPLATES } from './src/fix-patterns.js';
|
|
14
|
-
import { detectLanguage, runAnalyzer, generateFix, toSarif } from './src/utils.js';
|
|
14
|
+
import { detectLanguage, runAnalyzer, generateFix, toSarif, shutdownDaemon } from './src/utils.js';
|
|
15
15
|
import { scanSecuritySchema, scanSecurity } from './src/tools/scan-security.js';
|
|
16
16
|
import { fixSecuritySchema, fixSecurity } from './src/tools/fix-security.js';
|
|
17
17
|
import { loadPackageLists, checkPackageSchema, checkPackage, getPackageStats } from './src/tools/check-package.js';
|
|
18
18
|
import { scanPackagesSchema, scanPackages } from './src/tools/scan-packages.js';
|
|
19
19
|
import { scanAgentPromptSchema, scanAgentPrompt } from './src/tools/scan-prompt.js';
|
|
20
|
+
import { scanDiffSchema, scanDiff } from './src/tools/scan-diff.js';
|
|
21
|
+
import { scanProjectSchema, scanProject } from './src/tools/scan-project.js';
|
|
22
|
+
import { scanAgentActionSchema, scanAgentAction } from './src/tools/scan-action.js';
|
|
23
|
+
import { scanMcpServerSchema, scanMcpServer } from './src/tools/scan-mcp.js';
|
|
20
24
|
import { runInit } from './src/cli/init.js';
|
|
21
25
|
import { runDoctor } from './src/cli/doctor.js';
|
|
22
26
|
import { runDemo } from './src/cli/demo.js';
|
|
27
|
+
import { runInitHooks } from './src/cli/init-hooks.js';
|
|
28
|
+
import { runReport } from './src/cli/report.js';
|
|
23
29
|
|
|
24
30
|
// Handle both ESM and CJS bundling (Smithery bundles to CJS)
|
|
25
31
|
let __dirname;
|
|
@@ -134,6 +140,46 @@ server.tool(
|
|
|
134
140
|
scanAgentPrompt
|
|
135
141
|
);
|
|
136
142
|
|
|
143
|
+
// Register scan_git_diff tool
|
|
144
|
+
server.tool(
|
|
145
|
+
"scan_git_diff",
|
|
146
|
+
"Scan git diff for new security vulnerabilities. Only reports issues on changed lines. Use for PR reviews.",
|
|
147
|
+
scanDiffSchema,
|
|
148
|
+
scanDiff
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Register scan_project tool
|
|
152
|
+
server.tool(
|
|
153
|
+
"scan_project",
|
|
154
|
+
"Scan an entire directory for security vulnerabilities with .gitignore support and security grading. Use verbosity='minimal' for grade + counts, 'compact' (default) for top issues, 'full' for all details.",
|
|
155
|
+
scanProjectSchema,
|
|
156
|
+
scanProject
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// ===========================================
|
|
160
|
+
// AGENT ACTION MONITORING
|
|
161
|
+
// ===========================================
|
|
162
|
+
|
|
163
|
+
// Register scan_agent_action tool
|
|
164
|
+
server.tool(
|
|
165
|
+
"scan_agent_action",
|
|
166
|
+
"Pre-execution security check for agent actions (bash, file_write, file_read, http_request, file_delete). Returns ALLOW/WARN/BLOCK. Lighter than scan_agent_prompt — evaluates concrete actions.",
|
|
167
|
+
scanAgentActionSchema,
|
|
168
|
+
scanAgentAction
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// ===========================================
|
|
172
|
+
// MCP SERVER SECURITY SCANNING
|
|
173
|
+
// ===========================================
|
|
174
|
+
|
|
175
|
+
// Register scan_mcp_server tool
|
|
176
|
+
server.tool(
|
|
177
|
+
"scan_mcp_server",
|
|
178
|
+
"Scan an MCP server's source code for security vulnerabilities: overly broad permissions, missing input validation, data exfiltration, insecure patterns. Returns grade (A-F) and recommendations.",
|
|
179
|
+
scanMcpServerSchema,
|
|
180
|
+
scanMcpServer
|
|
181
|
+
);
|
|
182
|
+
|
|
137
183
|
// ===========================================
|
|
138
184
|
// CLI COMMANDS - Extracted to src/cli/
|
|
139
185
|
// ===========================================
|
|
@@ -156,17 +202,234 @@ if (cliArgs[0] === 'init') {
|
|
|
156
202
|
console.error(` Error: ${err.message}\n`);
|
|
157
203
|
process.exit(1);
|
|
158
204
|
});
|
|
205
|
+
} else if (cliArgs[0] === 'init-hooks') {
|
|
206
|
+
runInitHooks(cliArgs.slice(1)).then(() => process.exit(0)).catch((err) => {
|
|
207
|
+
console.error(` Error: ${err.message}\n`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
});
|
|
210
|
+
} else if (cliArgs[0] === 'report') {
|
|
211
|
+
runReport(cliArgs.slice(1)).then(() => process.exit(0)).catch((err) => {
|
|
212
|
+
console.error(` Error: ${err.message}\n`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
});
|
|
215
|
+
} else if (cliArgs[0] === 'scan-prompt') {
|
|
216
|
+
// CLI mode: scan-prompt <text> [--verbosity minimal|compact|full]
|
|
217
|
+
const text = cliArgs[1];
|
|
218
|
+
if (!text) {
|
|
219
|
+
console.error('Usage: agent-security-scanner-mcp scan-prompt <text> [--verbosity minimal|compact|full]');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
223
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
224
|
+
|
|
225
|
+
loadPackageLists();
|
|
226
|
+
scanAgentPrompt({ prompt_text: text, verbosity }).then(result => {
|
|
227
|
+
const output = JSON.parse(result.content[0].text);
|
|
228
|
+
console.log(JSON.stringify(output, null, 2));
|
|
229
|
+
process.exit(output.action === 'BLOCK' ? 1 : 0);
|
|
230
|
+
}).catch(err => {
|
|
231
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
});
|
|
234
|
+
} else if (cliArgs[0] === 'scan-security') {
|
|
235
|
+
// CLI mode: scan-security <file> [--verbosity minimal|compact|full] [--format json|sarif]
|
|
236
|
+
const filePath = cliArgs[1];
|
|
237
|
+
if (!filePath) {
|
|
238
|
+
console.error('Usage: agent-security-scanner-mcp scan-security <file> [--verbosity minimal|compact|full] [--format json|sarif]');
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
242
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
243
|
+
const formatIdx = cliArgs.indexOf('--format');
|
|
244
|
+
const outputFormat = formatIdx !== -1 ? cliArgs[formatIdx + 1] : 'json';
|
|
245
|
+
|
|
246
|
+
loadPackageLists();
|
|
247
|
+
scanSecurity({ file_path: filePath, verbosity, output_format: outputFormat }).then(result => {
|
|
248
|
+
const output = JSON.parse(result.content[0].text);
|
|
249
|
+
console.log(JSON.stringify(output, null, 2));
|
|
250
|
+
process.exit(output.issues_count > 0 || output.total > 0 ? 1 : 0);
|
|
251
|
+
}).catch(err => {
|
|
252
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
253
|
+
process.exit(1);
|
|
254
|
+
});
|
|
255
|
+
} else if (cliArgs[0] === 'check-package') {
|
|
256
|
+
// CLI mode: check-package <name> <ecosystem>
|
|
257
|
+
const packageName = cliArgs[1];
|
|
258
|
+
const ecosystem = cliArgs[2];
|
|
259
|
+
if (!packageName || !ecosystem) {
|
|
260
|
+
console.error('Usage: agent-security-scanner-mcp check-package <name> <ecosystem>');
|
|
261
|
+
console.error('Ecosystems: npm, pypi, rubygems, crates, dart, perl, raku');
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
loadPackageLists();
|
|
266
|
+
checkPackage({ package_name: packageName, ecosystem }).then(result => {
|
|
267
|
+
const output = JSON.parse(result.content[0].text);
|
|
268
|
+
console.log(JSON.stringify(output, null, 2));
|
|
269
|
+
process.exit(output.legitimate ? 0 : 1);
|
|
270
|
+
}).catch(err => {
|
|
271
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
272
|
+
process.exit(1);
|
|
273
|
+
});
|
|
274
|
+
} else if (cliArgs[0] === 'scan-packages') {
|
|
275
|
+
// CLI mode: scan-packages <file> <ecosystem> [--verbosity minimal|compact|full]
|
|
276
|
+
const filePath = cliArgs[1];
|
|
277
|
+
const ecosystem = cliArgs[2];
|
|
278
|
+
if (!filePath || !ecosystem) {
|
|
279
|
+
console.error('Usage: agent-security-scanner-mcp scan-packages <file> <ecosystem> [--verbosity minimal|compact|full]');
|
|
280
|
+
console.error('Ecosystems: npm, pypi, rubygems, crates, dart, perl, raku');
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
284
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
285
|
+
|
|
286
|
+
loadPackageLists();
|
|
287
|
+
scanPackages({ file_path: filePath, ecosystem, verbosity }).then(result => {
|
|
288
|
+
const output = JSON.parse(result.content[0].text);
|
|
289
|
+
console.log(JSON.stringify(output, null, 2));
|
|
290
|
+
process.exit(output.hallucinated_count > 0 ? 1 : 0);
|
|
291
|
+
}).catch(err => {
|
|
292
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
});
|
|
295
|
+
} else if (cliArgs[0] === 'scan-project') {
|
|
296
|
+
// CLI mode: scan-project <dir> [--recursive] [--diff-only] [--cross-file] [--include '*.py'] [--exclude '*.test.js'] [--verbosity minimal|compact|full]
|
|
297
|
+
const dirPath = cliArgs[1];
|
|
298
|
+
if (!dirPath || dirPath.startsWith('--')) {
|
|
299
|
+
console.error('Usage: agent-security-scanner-mcp scan-project <directory> [--recursive] [--diff-only] [--cross-file] [--include <pattern>] [--exclude <pattern>] [--verbosity minimal|compact|full]');
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
303
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
304
|
+
const recursive = !cliArgs.includes('--no-recursive');
|
|
305
|
+
const diffOnly = cliArgs.includes('--diff-only');
|
|
306
|
+
const crossFile = cliArgs.includes('--cross-file');
|
|
307
|
+
const includeIdx = cliArgs.indexOf('--include');
|
|
308
|
+
const includePatterns = includeIdx !== -1 ? [cliArgs[includeIdx + 1]] : undefined;
|
|
309
|
+
const excludeIdx = cliArgs.indexOf('--exclude');
|
|
310
|
+
const excludePatterns = excludeIdx !== -1 ? [cliArgs[excludeIdx + 1]] : undefined;
|
|
311
|
+
|
|
312
|
+
scanProject({ directory_path: dirPath, recursive, diff_only: diffOnly, cross_file: crossFile, include_patterns: includePatterns, exclude_patterns: excludePatterns, verbosity }).then(result => {
|
|
313
|
+
const output = JSON.parse(result.content[0].text);
|
|
314
|
+
console.log(JSON.stringify(output, null, 2));
|
|
315
|
+
const total = output.issues_count || output.total || 0;
|
|
316
|
+
process.exit(total > 0 ? 1 : 0);
|
|
317
|
+
}).catch(err => {
|
|
318
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
319
|
+
process.exit(1);
|
|
320
|
+
});
|
|
321
|
+
} else if (cliArgs[0] === 'scan-diff') {
|
|
322
|
+
// CLI mode: scan-diff [base] [target] [--verbosity minimal|compact|full]
|
|
323
|
+
// Parse positional args, skipping flag values
|
|
324
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
325
|
+
const flagValueIndices = new Set(verbosityIdx !== -1 ? [verbosityIdx, verbosityIdx + 1] : []);
|
|
326
|
+
const positionalArgs = cliArgs.slice(1).filter((arg, idx) => !arg.startsWith('--') && !flagValueIndices.has(idx + 1));
|
|
327
|
+
const baseRef = positionalArgs[0];
|
|
328
|
+
const targetRef = positionalArgs[1];
|
|
329
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
330
|
+
|
|
331
|
+
scanDiff({ base_ref: baseRef, target_ref: targetRef, verbosity }).then(result => {
|
|
332
|
+
const output = JSON.parse(result.content[0].text);
|
|
333
|
+
console.log(JSON.stringify(output, null, 2));
|
|
334
|
+
process.exit(output.issues_count > 0 || output.total > 0 ? 1 : 0);
|
|
335
|
+
}).catch(err => {
|
|
336
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
337
|
+
process.exit(1);
|
|
338
|
+
});
|
|
339
|
+
} else if (cliArgs[0] === 'benchmark') {
|
|
340
|
+
// CLI mode: benchmark [--save] [--json-only] [--compare-latest] [--corpus <path>]
|
|
341
|
+
const benchmarkPath = join(__dirname, 'benchmarks', 'benchmark_runner.py');
|
|
342
|
+
const benchArgs = [benchmarkPath];
|
|
343
|
+
|
|
344
|
+
// Pass through supported flags
|
|
345
|
+
for (let i = 1; i < cliArgs.length; i++) {
|
|
346
|
+
if (['--save', '--json-only', '--compare-latest'].includes(cliArgs[i])) {
|
|
347
|
+
benchArgs.push(cliArgs[i]);
|
|
348
|
+
} else if (cliArgs[i] === '--corpus' && cliArgs[i + 1]) {
|
|
349
|
+
benchArgs.push('--corpus', cliArgs[i + 1]);
|
|
350
|
+
i++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
execFileSync('python3', benchArgs, { stdio: 'inherit', timeout: 300000 });
|
|
356
|
+
} catch (err) {
|
|
357
|
+
if (err.status) process.exit(err.status);
|
|
358
|
+
console.error(`Benchmark error: ${err.message}`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
process.exit(0);
|
|
362
|
+
} else if (cliArgs[0] === 'scan-mcp') {
|
|
363
|
+
// CLI mode: scan-mcp <path> [--verbosity minimal|compact|full]
|
|
364
|
+
const serverPath = cliArgs[1];
|
|
365
|
+
if (!serverPath) {
|
|
366
|
+
console.error('Usage: agent-security-scanner-mcp scan-mcp <server-path> [--verbosity minimal|compact|full]');
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
370
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
371
|
+
|
|
372
|
+
scanMcpServer({ server_path: serverPath, verbosity }).then(result => {
|
|
373
|
+
const output = JSON.parse(result.content[0].text);
|
|
374
|
+
console.log(JSON.stringify(output, null, 2));
|
|
375
|
+
process.exit(output.findings_count > 0 ? 1 : 0);
|
|
376
|
+
}).catch(err => {
|
|
377
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
378
|
+
process.exit(1);
|
|
379
|
+
});
|
|
380
|
+
} else if (cliArgs[0] === 'scan-action') {
|
|
381
|
+
// CLI mode: scan-action <type> <value> [--verbosity minimal|compact|full]
|
|
382
|
+
const actionType = cliArgs[1];
|
|
383
|
+
const actionValue = cliArgs[2];
|
|
384
|
+
if (!actionType || !actionValue) {
|
|
385
|
+
console.error('Usage: agent-security-scanner-mcp scan-action <type> <value> [--verbosity minimal|compact|full]');
|
|
386
|
+
console.error('Types: bash, file_write, file_read, http_request, file_delete');
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
390
|
+
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
391
|
+
|
|
392
|
+
scanAgentAction({ action_type: actionType, action_value: actionValue, verbosity }).then(result => {
|
|
393
|
+
const output = JSON.parse(result.content[0].text);
|
|
394
|
+
console.log(JSON.stringify(output, null, 2));
|
|
395
|
+
process.exit(output.action === 'BLOCK' ? 1 : 0);
|
|
396
|
+
}).catch(err => {
|
|
397
|
+
console.error(JSON.stringify({ error: err.message }));
|
|
398
|
+
process.exit(1);
|
|
399
|
+
});
|
|
159
400
|
} else if (cliArgs[0] === '--help' || cliArgs[0] === '-h' || cliArgs[0] === 'help') {
|
|
160
401
|
console.log('\n agent-security-scanner-mcp\n');
|
|
161
402
|
console.log(' Commands:');
|
|
162
403
|
console.log(' init [client] Set up MCP config for a client');
|
|
404
|
+
console.log(' init-hooks Install Claude Code hooks for auto-scanning');
|
|
163
405
|
console.log(' doctor [--fix] Check environment & client configs');
|
|
164
406
|
console.log(' demo [--lang js] Generate vulnerable file + scan it');
|
|
407
|
+
console.log(' report <dir> Generate HTML security report with history');
|
|
408
|
+
console.log(' benchmark [flags] Run accuracy benchmarks\n');
|
|
409
|
+
console.log(' CLI Tools (for scripts & OpenClaw):');
|
|
410
|
+
console.log(' scan-prompt <text> Scan prompt for injection attacks');
|
|
411
|
+
console.log(' scan-security <file> Scan file for vulnerabilities');
|
|
412
|
+
console.log(' check-package <n> <e> Check if package exists in ecosystem');
|
|
413
|
+
console.log(' scan-packages <f> <e> Scan file imports for hallucinated packages');
|
|
414
|
+
console.log(' scan-project <dir> Scan directory for vulnerabilities with grading');
|
|
415
|
+
console.log(' scan-diff [base] [target] Scan git diff for new vulnerabilities');
|
|
416
|
+
console.log(' scan-mcp <path> Scan MCP server source for security issues');
|
|
417
|
+
console.log(' scan-action <t> <v> Check agent action before execution\n');
|
|
165
418
|
console.log(' (no args) Start MCP server on stdio\n');
|
|
419
|
+
console.log(' Options:');
|
|
420
|
+
console.log(' --verbosity <level> minimal|compact|full (default: compact)');
|
|
421
|
+
console.log(' --format <type> json|sarif (scan-security only)');
|
|
422
|
+
console.log(' --include <pattern> Include only matching files (scan-project)');
|
|
423
|
+
console.log(' --exclude <pattern> Exclude matching files (scan-project)\n');
|
|
166
424
|
console.log(' Examples:');
|
|
167
425
|
console.log(' npx agent-security-scanner-mcp init');
|
|
168
|
-
console.log(' npx agent-security-scanner-mcp
|
|
169
|
-
console.log(' npx agent-security-scanner-mcp
|
|
426
|
+
console.log(' npx agent-security-scanner-mcp scan-prompt "ignore previous instructions"');
|
|
427
|
+
console.log(' npx agent-security-scanner-mcp scan-security ./app.py --verbosity minimal');
|
|
428
|
+
console.log(' npx agent-security-scanner-mcp check-package flask pypi');
|
|
429
|
+
console.log(' npx agent-security-scanner-mcp scan-project ./src --verbosity minimal');
|
|
430
|
+
console.log(' npx agent-security-scanner-mcp scan-diff HEAD~1');
|
|
431
|
+
console.log(' npx agent-security-scanner-mcp report ./src --json');
|
|
432
|
+
console.log(' npx agent-security-scanner-mcp benchmark --save --compare-latest\n');
|
|
170
433
|
process.exit(0);
|
|
171
434
|
} else {
|
|
172
435
|
// Normal MCP server mode
|
|
@@ -176,8 +439,21 @@ if (cliArgs[0] === 'init') {
|
|
|
176
439
|
const transport = new StdioServerTransport();
|
|
177
440
|
await server.connect(transport);
|
|
178
441
|
console.error("Security Scanner MCP Server running on stdio");
|
|
442
|
+
|
|
443
|
+
// Shutdown daemon when MCP server closes
|
|
444
|
+
server.server.onclose = async () => {
|
|
445
|
+
await shutdownDaemon();
|
|
446
|
+
};
|
|
179
447
|
}
|
|
180
448
|
|
|
449
|
+
// Graceful shutdown on signals
|
|
450
|
+
const shutdownHandler = async () => {
|
|
451
|
+
await shutdownDaemon();
|
|
452
|
+
process.exit(0);
|
|
453
|
+
};
|
|
454
|
+
process.on('SIGTERM', shutdownHandler);
|
|
455
|
+
process.on('SIGINT', shutdownHandler);
|
|
456
|
+
|
|
181
457
|
main().catch((error) => {
|
|
182
458
|
console.error("Fatal error:", error);
|
|
183
459
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"mcpName": "io.github.sinewaveai/agent-security-scanner-mcp",
|
|
5
|
-
"description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 1000+ vulnerability rules with AST & taint analysis, auto-fix. For Claude Code, Cursor, Windsurf, Cline.",
|
|
5
|
+
"description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 1000+ vulnerability rules with AST & taint analysis, auto-fix. For Claude Code, Cursor, Windsurf, Cline, OpenClaw.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": {
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"start": "node index.js",
|
|
13
|
+
"postinstall": "node scripts/postinstall.js",
|
|
13
14
|
"test": "vitest run",
|
|
14
15
|
"test:watch": "vitest",
|
|
15
16
|
"test:coverage": "vitest run --coverage",
|
|
16
|
-
"
|
|
17
|
+
"benchmark": "python3 benchmarks/benchmark_runner.py --save --compare-latest"
|
|
17
18
|
},
|
|
18
19
|
"keywords": [
|
|
19
20
|
"mcp",
|
|
@@ -53,7 +54,9 @@
|
|
|
53
54
|
"zed",
|
|
54
55
|
"prompt-firewall",
|
|
55
56
|
"auto-fix",
|
|
56
|
-
"hallucination"
|
|
57
|
+
"hallucination",
|
|
58
|
+
"openclaw",
|
|
59
|
+
"clawdbot"
|
|
57
60
|
],
|
|
58
61
|
"author": "Sinewave AI <divya@sinewave.ai>",
|
|
59
62
|
"license": "MIT",
|
|
@@ -81,6 +84,13 @@
|
|
|
81
84
|
"src/cli/*.js",
|
|
82
85
|
"src/fix-patterns.js",
|
|
83
86
|
"src/utils.js",
|
|
87
|
+
"src/daemon-client.js",
|
|
88
|
+
"src/dedup.js",
|
|
89
|
+
"src/context.js",
|
|
90
|
+
"src/config.js",
|
|
91
|
+
"src/history.js",
|
|
92
|
+
"src/typosquat.js",
|
|
93
|
+
"templates/**",
|
|
84
94
|
"analyzer.py",
|
|
85
95
|
"ast_parser.py",
|
|
86
96
|
"generic_ast.py",
|
|
@@ -90,7 +100,11 @@
|
|
|
90
100
|
"taint_analyzer.py",
|
|
91
101
|
"requirements.txt",
|
|
92
102
|
"rules/**",
|
|
93
|
-
"packages/**"
|
|
103
|
+
"packages/**",
|
|
104
|
+
"skills/**",
|
|
105
|
+
"scripts/postinstall.js",
|
|
106
|
+
"cross_file_analyzer.py",
|
|
107
|
+
"daemon.py"
|
|
94
108
|
],
|
|
95
109
|
"devDependencies": {
|
|
96
110
|
"all-the-package-names": "^2.0.2349",
|