agent-security-scanner-mcp 3.4.0 → 3.5.1
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 +3 -1
- package/analyzer.py +5 -22
- package/index.js +2 -191
- package/package.json +5 -15
- package/pattern_matcher.py +0 -1
- package/regex_fallback.py +1 -199
- package/src/cli/init.js +0 -93
- package/src/fix-patterns.js +17 -66
- package/src/tools/fix-security.js +4 -31
- package/src/tools/scan-prompt.js +1 -71
- package/src/tools/scan-security.js +5 -33
- package/src/utils.js +7 -76
- package/cross_file_analyzer.py +0 -216
- package/scripts/postinstall.js +0 -25
- package/skills/openclaw/SKILL.md +0 -102
- package/skills/security-scan-batch.md +0 -107
- package/skills/security-scanner.md +0 -76
- package/src/config.js +0 -181
- package/src/context.js +0 -228
- package/src/dedup.js +0 -129
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@ Security scanner for AI coding agents and autonomous assistants. Scans code for
|
|
|
5
5
|
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
6
6
|
[](https://www.npmjs.com/package/agent-security-scanner-mcp)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](benchmarks/RESULTS.md)
|
|
9
|
+
[](https://github.com/sinewaveai/agent-security-scanner-mcp/actions/workflows/test.yml)
|
|
8
10
|
|
|
9
11
|
> **New in v3.3.0:** Full [OpenClaw](https://openclaw.ai) integration with 30+ rules targeting autonomous AI threats — data exfiltration, credential theft, messaging abuse, and unsafe automation. [See OpenClaw setup](#openclaw-integration).
|
|
10
12
|
|
|
@@ -917,4 +919,4 @@ npm install -g agent-security-scanner-mcp-full
|
|
|
917
919
|
|
|
918
920
|
## License
|
|
919
921
|
|
|
920
|
-
MIT
|
|
922
|
+
MIT
|
package/analyzer.py
CHANGED
|
@@ -11,7 +11,6 @@ import sys
|
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
|
-
import argparse
|
|
15
14
|
from typing import List, Dict, Any
|
|
16
15
|
|
|
17
16
|
# Add the directory containing this script to the path
|
|
@@ -92,7 +91,6 @@ def analyze_file_regex(file_path):
|
|
|
92
91
|
'column': match.start() + col_offset,
|
|
93
92
|
'length': match.end() - match.start(),
|
|
94
93
|
'severity': rule['severity'],
|
|
95
|
-
'confidence': rule.get('metadata', {}).get('confidence', 'MEDIUM'),
|
|
96
94
|
'metadata': rule.get('metadata', {}),
|
|
97
95
|
'engine': 'regex'
|
|
98
96
|
})
|
|
@@ -193,7 +191,6 @@ def analyze_file_ast(file_path):
|
|
|
193
191
|
'column': f.column,
|
|
194
192
|
'length': length,
|
|
195
193
|
'severity': f.severity,
|
|
196
|
-
'confidence': f.metadata.get('confidence', getattr(f, 'confidence', 'MEDIUM')),
|
|
197
194
|
'metadata': f.metadata,
|
|
198
195
|
'engine': 'taint' if is_taint else 'ast',
|
|
199
196
|
})
|
|
@@ -232,30 +229,16 @@ def analyze_file(file_path):
|
|
|
232
229
|
|
|
233
230
|
|
|
234
231
|
def main():
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
help='Analysis engine: auto (default), ast (tree-sitter only), regex (regex only)')
|
|
239
|
-
args = parser.parse_args()
|
|
232
|
+
if len(sys.argv) < 2:
|
|
233
|
+
print(json.dumps({'error': 'No file path provided'}))
|
|
234
|
+
sys.exit(1)
|
|
240
235
|
|
|
241
|
-
file_path =
|
|
236
|
+
file_path = sys.argv[1]
|
|
242
237
|
if not os.path.exists(file_path):
|
|
243
238
|
print(json.dumps({'error': f'File not found: {file_path}'}))
|
|
244
239
|
sys.exit(1)
|
|
245
240
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if engine == 'regex':
|
|
249
|
-
results = analyze_file_regex(file_path)
|
|
250
|
-
elif engine == 'ast':
|
|
251
|
-
if not HAS_AST_ENGINE:
|
|
252
|
-
print(json.dumps({'error': 'AST engine requested but tree-sitter is not available. Install dependencies: python3 -m pip install -r requirements.txt'}))
|
|
253
|
-
sys.exit(1)
|
|
254
|
-
results = analyze_file_ast(file_path)
|
|
255
|
-
else:
|
|
256
|
-
# auto: use AST if available, otherwise regex
|
|
257
|
-
results = analyze_file(file_path)
|
|
258
|
-
|
|
241
|
+
results = analyze_file(file_path)
|
|
259
242
|
print(json.dumps(results))
|
|
260
243
|
|
|
261
244
|
|
package/index.js
CHANGED
|
@@ -17,12 +17,9 @@ 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
20
|
import { runInit } from './src/cli/init.js';
|
|
23
21
|
import { runDoctor } from './src/cli/doctor.js';
|
|
24
22
|
import { runDemo } from './src/cli/demo.js';
|
|
25
|
-
import { runInitHooks } from './src/cli/init-hooks.js';
|
|
26
23
|
|
|
27
24
|
// Handle both ESM and CJS bundling (Smithery bundles to CJS)
|
|
28
25
|
let __dirname;
|
|
@@ -137,22 +134,6 @@ server.tool(
|
|
|
137
134
|
scanAgentPrompt
|
|
138
135
|
);
|
|
139
136
|
|
|
140
|
-
// Register scan_git_diff tool
|
|
141
|
-
server.tool(
|
|
142
|
-
"scan_git_diff",
|
|
143
|
-
"Scan git diff for new security vulnerabilities. Only reports issues on changed lines. Use for PR reviews.",
|
|
144
|
-
scanDiffSchema,
|
|
145
|
-
scanDiff
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Register scan_project tool
|
|
149
|
-
server.tool(
|
|
150
|
-
"scan_project",
|
|
151
|
-
"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.",
|
|
152
|
-
scanProjectSchema,
|
|
153
|
-
scanProject
|
|
154
|
-
);
|
|
155
|
-
|
|
156
137
|
// ===========================================
|
|
157
138
|
// CLI COMMANDS - Extracted to src/cli/
|
|
158
139
|
// ===========================================
|
|
@@ -175,187 +156,17 @@ if (cliArgs[0] === 'init') {
|
|
|
175
156
|
console.error(` Error: ${err.message}\n`);
|
|
176
157
|
process.exit(1);
|
|
177
158
|
});
|
|
178
|
-
} else if (cliArgs[0] === 'init-hooks') {
|
|
179
|
-
runInitHooks(cliArgs.slice(1)).then(() => process.exit(0)).catch((err) => {
|
|
180
|
-
console.error(` Error: ${err.message}\n`);
|
|
181
|
-
process.exit(1);
|
|
182
|
-
});
|
|
183
|
-
} else if (cliArgs[0] === 'scan-prompt') {
|
|
184
|
-
// CLI mode: scan-prompt <text> [--verbosity minimal|compact|full]
|
|
185
|
-
const text = cliArgs[1];
|
|
186
|
-
if (!text) {
|
|
187
|
-
console.error('Usage: agent-security-scanner-mcp scan-prompt <text> [--verbosity minimal|compact|full]');
|
|
188
|
-
process.exit(1);
|
|
189
|
-
}
|
|
190
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
191
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
192
|
-
|
|
193
|
-
loadPackageLists();
|
|
194
|
-
scanAgentPrompt({ prompt_text: text, verbosity }).then(result => {
|
|
195
|
-
const output = JSON.parse(result.content[0].text);
|
|
196
|
-
console.log(JSON.stringify(output, null, 2));
|
|
197
|
-
process.exit(output.action === 'BLOCK' ? 1 : 0);
|
|
198
|
-
}).catch(err => {
|
|
199
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
200
|
-
process.exit(1);
|
|
201
|
-
});
|
|
202
|
-
} else if (cliArgs[0] === 'scan-security') {
|
|
203
|
-
// CLI mode: scan-security <file> [--verbosity minimal|compact|full] [--format json|sarif]
|
|
204
|
-
const filePath = cliArgs[1];
|
|
205
|
-
if (!filePath) {
|
|
206
|
-
console.error('Usage: agent-security-scanner-mcp scan-security <file> [--verbosity minimal|compact|full] [--format json|sarif]');
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
209
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
210
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
211
|
-
const formatIdx = cliArgs.indexOf('--format');
|
|
212
|
-
const outputFormat = formatIdx !== -1 ? cliArgs[formatIdx + 1] : 'json';
|
|
213
|
-
|
|
214
|
-
loadPackageLists();
|
|
215
|
-
scanSecurity({ file_path: filePath, verbosity, output_format: outputFormat }).then(result => {
|
|
216
|
-
const output = JSON.parse(result.content[0].text);
|
|
217
|
-
console.log(JSON.stringify(output, null, 2));
|
|
218
|
-
process.exit(output.issues_count > 0 || output.total > 0 ? 1 : 0);
|
|
219
|
-
}).catch(err => {
|
|
220
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
221
|
-
process.exit(1);
|
|
222
|
-
});
|
|
223
|
-
} else if (cliArgs[0] === 'check-package') {
|
|
224
|
-
// CLI mode: check-package <name> <ecosystem>
|
|
225
|
-
const packageName = cliArgs[1];
|
|
226
|
-
const ecosystem = cliArgs[2];
|
|
227
|
-
if (!packageName || !ecosystem) {
|
|
228
|
-
console.error('Usage: agent-security-scanner-mcp check-package <name> <ecosystem>');
|
|
229
|
-
console.error('Ecosystems: npm, pypi, rubygems, crates, dart, perl, raku');
|
|
230
|
-
process.exit(1);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
loadPackageLists();
|
|
234
|
-
checkPackage({ package_name: packageName, ecosystem }).then(result => {
|
|
235
|
-
const output = JSON.parse(result.content[0].text);
|
|
236
|
-
console.log(JSON.stringify(output, null, 2));
|
|
237
|
-
process.exit(output.legitimate ? 0 : 1);
|
|
238
|
-
}).catch(err => {
|
|
239
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
240
|
-
process.exit(1);
|
|
241
|
-
});
|
|
242
|
-
} else if (cliArgs[0] === 'scan-packages') {
|
|
243
|
-
// CLI mode: scan-packages <file> <ecosystem> [--verbosity minimal|compact|full]
|
|
244
|
-
const filePath = cliArgs[1];
|
|
245
|
-
const ecosystem = cliArgs[2];
|
|
246
|
-
if (!filePath || !ecosystem) {
|
|
247
|
-
console.error('Usage: agent-security-scanner-mcp scan-packages <file> <ecosystem> [--verbosity minimal|compact|full]');
|
|
248
|
-
console.error('Ecosystems: npm, pypi, rubygems, crates, dart, perl, raku');
|
|
249
|
-
process.exit(1);
|
|
250
|
-
}
|
|
251
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
252
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
253
|
-
|
|
254
|
-
loadPackageLists();
|
|
255
|
-
scanPackages({ file_path: filePath, ecosystem, verbosity }).then(result => {
|
|
256
|
-
const output = JSON.parse(result.content[0].text);
|
|
257
|
-
console.log(JSON.stringify(output, null, 2));
|
|
258
|
-
process.exit(output.hallucinated_count > 0 ? 1 : 0);
|
|
259
|
-
}).catch(err => {
|
|
260
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
261
|
-
process.exit(1);
|
|
262
|
-
});
|
|
263
|
-
} else if (cliArgs[0] === 'scan-project') {
|
|
264
|
-
// CLI mode: scan-project <dir> [--recursive] [--diff-only] [--cross-file] [--include '*.py'] [--exclude '*.test.js'] [--verbosity minimal|compact|full]
|
|
265
|
-
const dirPath = cliArgs[1];
|
|
266
|
-
if (!dirPath || dirPath.startsWith('--')) {
|
|
267
|
-
console.error('Usage: agent-security-scanner-mcp scan-project <directory> [--recursive] [--diff-only] [--cross-file] [--include <pattern>] [--exclude <pattern>] [--verbosity minimal|compact|full]');
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
271
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
272
|
-
const recursive = !cliArgs.includes('--no-recursive');
|
|
273
|
-
const diffOnly = cliArgs.includes('--diff-only');
|
|
274
|
-
const crossFile = cliArgs.includes('--cross-file');
|
|
275
|
-
const includeIdx = cliArgs.indexOf('--include');
|
|
276
|
-
const includePatterns = includeIdx !== -1 ? [cliArgs[includeIdx + 1]] : undefined;
|
|
277
|
-
const excludeIdx = cliArgs.indexOf('--exclude');
|
|
278
|
-
const excludePatterns = excludeIdx !== -1 ? [cliArgs[excludeIdx + 1]] : undefined;
|
|
279
|
-
|
|
280
|
-
scanProject({ directory_path: dirPath, recursive, diff_only: diffOnly, cross_file: crossFile, include_patterns: includePatterns, exclude_patterns: excludePatterns, verbosity }).then(result => {
|
|
281
|
-
const output = JSON.parse(result.content[0].text);
|
|
282
|
-
console.log(JSON.stringify(output, null, 2));
|
|
283
|
-
const total = output.issues_count || output.total || 0;
|
|
284
|
-
process.exit(total > 0 ? 1 : 0);
|
|
285
|
-
}).catch(err => {
|
|
286
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
287
|
-
process.exit(1);
|
|
288
|
-
});
|
|
289
|
-
} else if (cliArgs[0] === 'scan-diff') {
|
|
290
|
-
// CLI mode: scan-diff [base] [target] [--verbosity minimal|compact|full]
|
|
291
|
-
// Parse positional args, skipping flag values
|
|
292
|
-
const verbosityIdx = cliArgs.indexOf('--verbosity');
|
|
293
|
-
const flagValueIndices = new Set(verbosityIdx !== -1 ? [verbosityIdx, verbosityIdx + 1] : []);
|
|
294
|
-
const positionalArgs = cliArgs.slice(1).filter((arg, idx) => !arg.startsWith('--') && !flagValueIndices.has(idx + 1));
|
|
295
|
-
const baseRef = positionalArgs[0];
|
|
296
|
-
const targetRef = positionalArgs[1];
|
|
297
|
-
const verbosity = verbosityIdx !== -1 ? cliArgs[verbosityIdx + 1] : 'compact';
|
|
298
|
-
|
|
299
|
-
scanDiff({ base_ref: baseRef, target_ref: targetRef, verbosity }).then(result => {
|
|
300
|
-
const output = JSON.parse(result.content[0].text);
|
|
301
|
-
console.log(JSON.stringify(output, null, 2));
|
|
302
|
-
process.exit(output.issues_count > 0 || output.total > 0 ? 1 : 0);
|
|
303
|
-
}).catch(err => {
|
|
304
|
-
console.error(JSON.stringify({ error: err.message }));
|
|
305
|
-
process.exit(1);
|
|
306
|
-
});
|
|
307
|
-
} else if (cliArgs[0] === 'benchmark') {
|
|
308
|
-
// CLI mode: benchmark [--save] [--json-only] [--compare-latest] [--corpus <path>]
|
|
309
|
-
const benchmarkPath = join(__dirname, 'benchmarks', 'benchmark_runner.py');
|
|
310
|
-
const benchArgs = [benchmarkPath];
|
|
311
|
-
|
|
312
|
-
// Pass through supported flags
|
|
313
|
-
for (let i = 1; i < cliArgs.length; i++) {
|
|
314
|
-
if (['--save', '--json-only', '--compare-latest'].includes(cliArgs[i])) {
|
|
315
|
-
benchArgs.push(cliArgs[i]);
|
|
316
|
-
} else if (cliArgs[i] === '--corpus' && cliArgs[i + 1]) {
|
|
317
|
-
benchArgs.push('--corpus', cliArgs[i + 1]);
|
|
318
|
-
i++;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
try {
|
|
323
|
-
execFileSync('python3', benchArgs, { stdio: 'inherit', timeout: 300000 });
|
|
324
|
-
} catch (err) {
|
|
325
|
-
if (err.status) process.exit(err.status);
|
|
326
|
-
console.error(`Benchmark error: ${err.message}`);
|
|
327
|
-
process.exit(1);
|
|
328
|
-
}
|
|
329
|
-
process.exit(0);
|
|
330
159
|
} else if (cliArgs[0] === '--help' || cliArgs[0] === '-h' || cliArgs[0] === 'help') {
|
|
331
160
|
console.log('\n agent-security-scanner-mcp\n');
|
|
332
161
|
console.log(' Commands:');
|
|
333
162
|
console.log(' init [client] Set up MCP config for a client');
|
|
334
|
-
console.log(' init-hooks Install Claude Code hooks for auto-scanning');
|
|
335
163
|
console.log(' doctor [--fix] Check environment & client configs');
|
|
336
164
|
console.log(' demo [--lang js] Generate vulnerable file + scan it');
|
|
337
|
-
console.log(' benchmark [flags] Run accuracy benchmarks\n');
|
|
338
|
-
console.log(' CLI Tools (for scripts & OpenClaw):');
|
|
339
|
-
console.log(' scan-prompt <text> Scan prompt for injection attacks');
|
|
340
|
-
console.log(' scan-security <file> Scan file for vulnerabilities');
|
|
341
|
-
console.log(' check-package <n> <e> Check if package exists in ecosystem');
|
|
342
|
-
console.log(' scan-packages <f> <e> Scan file imports for hallucinated packages');
|
|
343
|
-
console.log(' scan-project <dir> Scan directory for vulnerabilities with grading');
|
|
344
|
-
console.log(' scan-diff [base] [target] Scan git diff for new vulnerabilities\n');
|
|
345
165
|
console.log(' (no args) Start MCP server on stdio\n');
|
|
346
|
-
console.log(' Options:');
|
|
347
|
-
console.log(' --verbosity <level> minimal|compact|full (default: compact)');
|
|
348
|
-
console.log(' --format <type> json|sarif (scan-security only)');
|
|
349
|
-
console.log(' --include <pattern> Include only matching files (scan-project)');
|
|
350
|
-
console.log(' --exclude <pattern> Exclude matching files (scan-project)\n');
|
|
351
166
|
console.log(' Examples:');
|
|
352
167
|
console.log(' npx agent-security-scanner-mcp init');
|
|
353
|
-
console.log(' npx agent-security-scanner-mcp
|
|
354
|
-
console.log(' npx agent-security-scanner-mcp
|
|
355
|
-
console.log(' npx agent-security-scanner-mcp check-package flask pypi');
|
|
356
|
-
console.log(' npx agent-security-scanner-mcp scan-project ./src --verbosity minimal');
|
|
357
|
-
console.log(' npx agent-security-scanner-mcp scan-diff HEAD~1');
|
|
358
|
-
console.log(' npx agent-security-scanner-mcp benchmark --save --compare-latest\n');
|
|
168
|
+
console.log(' npx agent-security-scanner-mcp doctor --fix');
|
|
169
|
+
console.log(' npx agent-security-scanner-mcp demo --lang py\n');
|
|
359
170
|
process.exit(0);
|
|
360
171
|
} else {
|
|
361
172
|
// Normal MCP server mode
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
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.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": {
|
|
@@ -10,11 +10,9 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"start": "node index.js",
|
|
13
|
-
"postinstall": "node scripts/postinstall.js",
|
|
14
13
|
"test": "vitest run",
|
|
15
14
|
"test:watch": "vitest",
|
|
16
|
-
"test:coverage": "vitest run --coverage"
|
|
17
|
-
"benchmark": "python3 benchmarks/benchmark_runner.py --save --compare-latest"
|
|
15
|
+
"test:coverage": "vitest run --coverage"
|
|
18
16
|
},
|
|
19
17
|
"keywords": [
|
|
20
18
|
"mcp",
|
|
@@ -54,9 +52,7 @@
|
|
|
54
52
|
"zed",
|
|
55
53
|
"prompt-firewall",
|
|
56
54
|
"auto-fix",
|
|
57
|
-
"hallucination"
|
|
58
|
-
"openclaw",
|
|
59
|
-
"clawdbot"
|
|
55
|
+
"hallucination"
|
|
60
56
|
],
|
|
61
57
|
"author": "Sinewave AI <divya@sinewave.ai>",
|
|
62
58
|
"license": "MIT",
|
|
@@ -84,9 +80,6 @@
|
|
|
84
80
|
"src/cli/*.js",
|
|
85
81
|
"src/fix-patterns.js",
|
|
86
82
|
"src/utils.js",
|
|
87
|
-
"src/dedup.js",
|
|
88
|
-
"src/context.js",
|
|
89
|
-
"src/config.js",
|
|
90
83
|
"analyzer.py",
|
|
91
84
|
"ast_parser.py",
|
|
92
85
|
"generic_ast.py",
|
|
@@ -96,10 +89,7 @@
|
|
|
96
89
|
"taint_analyzer.py",
|
|
97
90
|
"requirements.txt",
|
|
98
91
|
"rules/**",
|
|
99
|
-
"packages/**"
|
|
100
|
-
"skills/**",
|
|
101
|
-
"scripts/postinstall.js",
|
|
102
|
-
"cross_file_analyzer.py"
|
|
92
|
+
"packages/**"
|
|
103
93
|
],
|
|
104
94
|
"devDependencies": {
|
|
105
95
|
"all-the-package-names": "^2.0.2349",
|
package/pattern_matcher.py
CHANGED
package/regex_fallback.py
CHANGED
|
@@ -9,207 +9,10 @@ from typing import List, Dict, Optional
|
|
|
9
9
|
import re
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
# Severity classification by vulnerability class
|
|
13
|
-
SEVERITY_MAP = {
|
|
14
|
-
# ERROR - exploitable vulnerabilities (injection, RCE, deserialization)
|
|
15
|
-
'sql-injection': 'error',
|
|
16
|
-
'sql-injection-query': 'error',
|
|
17
|
-
'sql-injection-sprintf': 'error',
|
|
18
|
-
'sql-injection-where': 'error',
|
|
19
|
-
'sql-injection-order': 'error',
|
|
20
|
-
'sql-injection-raw': 'error',
|
|
21
|
-
'sql-injection-db-cursor': 'error',
|
|
22
|
-
'sql-injection-using-sqlalchemy': 'error',
|
|
23
|
-
'sql-injection-sqlcommand': 'error',
|
|
24
|
-
'sql-injection-sqlquery': 'error',
|
|
25
|
-
'sql-injection-concat': 'error',
|
|
26
|
-
'command-injection': 'error',
|
|
27
|
-
'command-injection-exec': 'error',
|
|
28
|
-
'command-injection-system': 'error',
|
|
29
|
-
'command-injection-open': 'error',
|
|
30
|
-
'command-injection-process-start': 'error',
|
|
31
|
-
'child-process-exec': 'error',
|
|
32
|
-
'spawn-shell': 'error',
|
|
33
|
-
'dangerous-subprocess-use': 'error',
|
|
34
|
-
'dangerous-system-call': 'error',
|
|
35
|
-
'eval-detected': 'error',
|
|
36
|
-
'eval-usage': 'error',
|
|
37
|
-
'exec-detected': 'error',
|
|
38
|
-
'pickle-load': 'error',
|
|
39
|
-
'unsafe-unserialize': 'error',
|
|
40
|
-
'unsafe-yaml-load': 'error',
|
|
41
|
-
'unsafe-marshal': 'error',
|
|
42
|
-
'yaml-load': 'error',
|
|
43
|
-
'file-inclusion': 'error',
|
|
44
|
-
'path-traversal': 'error',
|
|
45
|
-
'xss-echo': 'error',
|
|
46
|
-
'xss-raw': 'error',
|
|
47
|
-
'ssrf': 'error',
|
|
48
|
-
'open-redirect': 'error',
|
|
49
|
-
'backticks-exec': 'error',
|
|
50
|
-
'preg-code-exec': 'error',
|
|
51
|
-
'assert-usage': 'error',
|
|
52
|
-
'insecure-deserialization-binaryformatter': 'error',
|
|
53
|
-
'insecure-deserialization-xmlserializer': 'error',
|
|
54
|
-
'libc-system-call': 'error',
|
|
55
|
-
'format-string-printf': 'error',
|
|
56
|
-
'format-string-syslog': 'error',
|
|
57
|
-
'xss-innerhtml': 'error',
|
|
58
|
-
'xss-response-write': 'error',
|
|
59
|
-
'path-traversal-directory-delete': 'error',
|
|
60
|
-
'path-traversal-file-delete': 'error',
|
|
61
|
-
'path-traversal-file-read': 'error',
|
|
62
|
-
|
|
63
|
-
# WARNING - risky patterns requiring attention
|
|
64
|
-
'innerHTML': 'warning',
|
|
65
|
-
'outerHTML': 'warning',
|
|
66
|
-
'document-write': 'warning',
|
|
67
|
-
'insertAdjacentHTML': 'warning',
|
|
68
|
-
'dangerouslySetInnerHTML': 'warning',
|
|
69
|
-
'function-constructor': 'warning',
|
|
70
|
-
'setTimeout-string': 'warning',
|
|
71
|
-
'strcpy-usage': 'warning',
|
|
72
|
-
'strcat-usage': 'warning',
|
|
73
|
-
'sprintf-usage': 'warning',
|
|
74
|
-
'vsprintf-usage': 'warning',
|
|
75
|
-
'gets-usage': 'warning',
|
|
76
|
-
'system-usage': 'warning',
|
|
77
|
-
'popen-usage': 'warning',
|
|
78
|
-
'hardcoded-password': 'warning',
|
|
79
|
-
'hardcoded-secret': 'warning',
|
|
80
|
-
'hardcoded-api-key': 'warning',
|
|
81
|
-
'hardcoded-connection-string': 'warning',
|
|
82
|
-
'session-secret-hardcoded': 'warning',
|
|
83
|
-
'ssl-verify-disabled': 'warning',
|
|
84
|
-
'curl-ssl-disabled': 'warning',
|
|
85
|
-
'csrf-disabled': 'warning',
|
|
86
|
-
'mass-assignment-permit-all': 'warning',
|
|
87
|
-
'constantize': 'warning',
|
|
88
|
-
'render-inline': 'warning',
|
|
89
|
-
'privileged-container': 'warning',
|
|
90
|
-
'run-as-root': 'warning',
|
|
91
|
-
'allow-privilege-escalation': 'warning',
|
|
92
|
-
'host-network': 'warning',
|
|
93
|
-
'host-pid': 'warning',
|
|
94
|
-
'host-path': 'warning',
|
|
95
|
-
'secrets-in-env': 'warning',
|
|
96
|
-
'cluster-admin-binding': 'warning',
|
|
97
|
-
'capabilities-add': 'warning',
|
|
98
|
-
'no-readonly-root': 'warning',
|
|
99
|
-
'wildcard-rbac': 'warning',
|
|
100
|
-
's3-public-read': 'warning',
|
|
101
|
-
'security-group-open-ingress': 'warning',
|
|
102
|
-
'rds-public-access': 'warning',
|
|
103
|
-
'rds-encryption-disabled': 'warning',
|
|
104
|
-
'rds-deletion-protection': 'warning',
|
|
105
|
-
'cloudtrail-disabled': 'warning',
|
|
106
|
-
'kms-key-rotation': 'warning',
|
|
107
|
-
'ebs-encryption-disabled': 'warning',
|
|
108
|
-
'ec2-imdsv1': 'warning',
|
|
109
|
-
'phpinfo-exposure': 'warning',
|
|
110
|
-
'error-display': 'warning',
|
|
111
|
-
'permissive-cors': 'warning',
|
|
112
|
-
'mcrypt-deprecated': 'warning',
|
|
113
|
-
'aws-access-key-id': 'warning',
|
|
114
|
-
'aws-secret-access-key': 'warning',
|
|
115
|
-
'github-pat': 'warning',
|
|
116
|
-
'stripe-api-key': 'warning',
|
|
117
|
-
'private-key-rsa': 'warning',
|
|
118
|
-
'database-url': 'warning',
|
|
119
|
-
'jwt-token': 'warning',
|
|
120
|
-
'openai-api-key': 'warning',
|
|
121
|
-
'python.lang.security.audit.hardcoded-password': 'warning',
|
|
122
|
-
'python.lang.security.audit.hardcoded-api-key': 'warning',
|
|
123
|
-
'generic.secrets.security.hardcoded-password': 'warning',
|
|
124
|
-
'generic.secrets.security.hardcoded-api-key': 'warning',
|
|
125
|
-
|
|
126
|
-
# INFO - informational / hygiene / low-risk patterns
|
|
127
|
-
'weak-hash-md5': 'info',
|
|
128
|
-
'weak-hash-sha1': 'info',
|
|
129
|
-
'weak-hash': 'info',
|
|
130
|
-
'weak-cipher': 'info',
|
|
131
|
-
'weak-cipher-des': 'info',
|
|
132
|
-
'ecb-mode': 'info',
|
|
133
|
-
'weak-random': 'info',
|
|
134
|
-
'insecure-random': 'info',
|
|
135
|
-
'insecure-hash-md5': 'info',
|
|
136
|
-
'insecure-hash-sha1': 'info',
|
|
137
|
-
'insecure-memset': 'info',
|
|
138
|
-
'strtok-usage': 'info',
|
|
139
|
-
'insecure-tempfile': 'info',
|
|
140
|
-
'unchecked-return': 'info',
|
|
141
|
-
'unsafe-block': 'info',
|
|
142
|
-
'unwrap-usage': 'info',
|
|
143
|
-
'raw-pointer-deref': 'info',
|
|
144
|
-
'panic-usage': 'info',
|
|
145
|
-
'compile-detected': 'info',
|
|
146
|
-
'scanf-usage': 'info',
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
# Confidence classification by rule ID
|
|
150
|
-
CONFIDENCE_MAP = {
|
|
151
|
-
# HIGH - very specific patterns, low false-positive rate
|
|
152
|
-
'sql-injection-db-cursor': 'HIGH',
|
|
153
|
-
'sql-injection-using-sqlalchemy': 'HIGH',
|
|
154
|
-
'sql-injection-sqlcommand': 'HIGH',
|
|
155
|
-
'sql-injection-sqlquery': 'HIGH',
|
|
156
|
-
'sql-injection-concat': 'HIGH',
|
|
157
|
-
'format-string-printf': 'HIGH',
|
|
158
|
-
'format-string-syslog': 'HIGH',
|
|
159
|
-
'pickle-load': 'HIGH',
|
|
160
|
-
'eval-detected': 'HIGH',
|
|
161
|
-
'eval-usage': 'HIGH',
|
|
162
|
-
'exec-detected': 'HIGH',
|
|
163
|
-
'child-process-exec': 'HIGH',
|
|
164
|
-
'dangerous-subprocess-use': 'HIGH',
|
|
165
|
-
'dangerous-system-call': 'HIGH',
|
|
166
|
-
'hardcoded-password': 'HIGH',
|
|
167
|
-
'hardcoded-connection-string': 'HIGH',
|
|
168
|
-
'aws-access-key-id': 'HIGH',
|
|
169
|
-
'github-pat': 'HIGH',
|
|
170
|
-
'stripe-api-key': 'HIGH',
|
|
171
|
-
'private-key-rsa': 'HIGH',
|
|
172
|
-
'openai-api-key': 'HIGH',
|
|
173
|
-
'unsafe-unserialize': 'HIGH',
|
|
174
|
-
'backticks-exec': 'HIGH',
|
|
175
|
-
'preg-code-exec': 'HIGH',
|
|
176
|
-
'file-inclusion': 'HIGH',
|
|
177
|
-
'gets-usage': 'HIGH',
|
|
178
|
-
'insecure-deserialization-binaryformatter': 'HIGH',
|
|
179
|
-
'insecure-deserialization-xmlserializer': 'HIGH',
|
|
180
|
-
|
|
181
|
-
# LOW - broad patterns with high false-positive rate
|
|
182
|
-
'compile-detected': 'LOW',
|
|
183
|
-
'unsafe-block': 'LOW',
|
|
184
|
-
'unwrap-usage': 'LOW',
|
|
185
|
-
'insecure-memset': 'LOW',
|
|
186
|
-
'strtok-usage': 'LOW',
|
|
187
|
-
'unchecked-return': 'LOW',
|
|
188
|
-
'scanf-usage': 'LOW',
|
|
189
|
-
'panic-usage': 'LOW',
|
|
190
|
-
'raw-pointer-deref': 'LOW',
|
|
191
|
-
'insecure-random': 'LOW',
|
|
192
|
-
'weak-random': 'LOW',
|
|
193
|
-
'insecure-hash-md5': 'LOW',
|
|
194
|
-
'insecure-hash-sha1': 'LOW',
|
|
195
|
-
'weak-hash-md5': 'LOW',
|
|
196
|
-
'weak-hash-sha1': 'LOW',
|
|
197
|
-
'weak-hash': 'LOW',
|
|
198
|
-
'database-url': 'LOW',
|
|
199
|
-
|
|
200
|
-
# Everything else defaults to MEDIUM
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
12
|
def _make_finding(rule_id: str, line_idx: int, line: str, col_start: int = 0, col_end: Optional[int] = None,
|
|
205
|
-
message: Optional[str] = None, severity:
|
|
206
|
-
confidence: Optional[str] = None) -> Dict:
|
|
13
|
+
message: Optional[str] = None, severity: str = "warning") -> Dict:
|
|
207
14
|
if col_end is None:
|
|
208
15
|
col_end = max(col_start + 1, len(line.rstrip("\n")))
|
|
209
|
-
if severity is None:
|
|
210
|
-
severity = SEVERITY_MAP.get(rule_id, "warning")
|
|
211
|
-
if confidence is None:
|
|
212
|
-
confidence = CONFIDENCE_MAP.get(rule_id, "MEDIUM")
|
|
213
16
|
return {
|
|
214
17
|
"ruleId": rule_id,
|
|
215
18
|
"message": message or f"[Regex] {rule_id}",
|
|
@@ -219,7 +22,6 @@ def _make_finding(rule_id: str, line_idx: int, line: str, col_start: int = 0, co
|
|
|
219
22
|
"endColumn": col_end,
|
|
220
23
|
"length": max(0, col_end - col_start),
|
|
221
24
|
"severity": severity,
|
|
222
|
-
"confidence": confidence,
|
|
223
25
|
"metadata": {"source": "regex-fallback"},
|
|
224
26
|
"metavariables": {},
|
|
225
27
|
}
|