agent-security-scanner-mcp 3.2.0 → 3.4.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 +283 -3
- package/analyzer.py +22 -5
- package/cross_file_analyzer.py +216 -0
- package/index.js +191 -2
- package/package.json +15 -5
- package/pattern_matcher.py +1 -0
- package/regex_fallback.py +199 -1
- package/rules/openclaw.security.yaml +283 -0
- package/scripts/postinstall.js +25 -0
- package/skills/openclaw/SKILL.md +102 -0
- package/skills/security-scan-batch.md +107 -0
- package/skills/security-scanner.md +76 -0
- package/src/cli/init-hooks.js +164 -0
- package/src/cli/init.js +93 -0
- package/src/config.js +181 -0
- package/src/context.js +228 -0
- package/src/dedup.js +129 -0
- package/src/fix-patterns.js +66 -17
- package/src/tools/fix-security.js +31 -4
- package/src/tools/scan-diff.js +151 -0
- package/src/tools/scan-project.js +308 -0
- package/src/tools/scan-prompt.js +71 -1
- package/src/tools/scan-security.js +33 -5
- package/src/utils.js +76 -7
package/index.js
CHANGED
|
@@ -17,9 +17,12 @@ 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';
|
|
20
22
|
import { runInit } from './src/cli/init.js';
|
|
21
23
|
import { runDoctor } from './src/cli/doctor.js';
|
|
22
24
|
import { runDemo } from './src/cli/demo.js';
|
|
25
|
+
import { runInitHooks } from './src/cli/init-hooks.js';
|
|
23
26
|
|
|
24
27
|
// Handle both ESM and CJS bundling (Smithery bundles to CJS)
|
|
25
28
|
let __dirname;
|
|
@@ -134,6 +137,22 @@ server.tool(
|
|
|
134
137
|
scanAgentPrompt
|
|
135
138
|
);
|
|
136
139
|
|
|
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
|
+
|
|
137
156
|
// ===========================================
|
|
138
157
|
// CLI COMMANDS - Extracted to src/cli/
|
|
139
158
|
// ===========================================
|
|
@@ -156,17 +175,187 @@ if (cliArgs[0] === 'init') {
|
|
|
156
175
|
console.error(` Error: ${err.message}\n`);
|
|
157
176
|
process.exit(1);
|
|
158
177
|
});
|
|
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);
|
|
159
330
|
} else if (cliArgs[0] === '--help' || cliArgs[0] === '-h' || cliArgs[0] === 'help') {
|
|
160
331
|
console.log('\n agent-security-scanner-mcp\n');
|
|
161
332
|
console.log(' Commands:');
|
|
162
333
|
console.log(' init [client] Set up MCP config for a client');
|
|
334
|
+
console.log(' init-hooks Install Claude Code hooks for auto-scanning');
|
|
163
335
|
console.log(' doctor [--fix] Check environment & client configs');
|
|
164
336
|
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');
|
|
165
345
|
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');
|
|
166
351
|
console.log(' Examples:');
|
|
167
352
|
console.log(' npx agent-security-scanner-mcp init');
|
|
168
|
-
console.log(' npx agent-security-scanner-mcp
|
|
169
|
-
console.log(' npx agent-security-scanner-mcp
|
|
353
|
+
console.log(' npx agent-security-scanner-mcp scan-prompt "ignore previous instructions"');
|
|
354
|
+
console.log(' npx agent-security-scanner-mcp scan-security ./app.py --verbosity minimal');
|
|
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');
|
|
170
359
|
process.exit(0);
|
|
171
360
|
} else {
|
|
172
361
|
// 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.4.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,9 +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
|
-
"test:coverage": "vitest run --coverage"
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"benchmark": "python3 benchmarks/benchmark_runner.py --save --compare-latest"
|
|
16
18
|
},
|
|
17
19
|
"keywords": [
|
|
18
20
|
"mcp",
|
|
@@ -52,7 +54,9 @@
|
|
|
52
54
|
"zed",
|
|
53
55
|
"prompt-firewall",
|
|
54
56
|
"auto-fix",
|
|
55
|
-
"hallucination"
|
|
57
|
+
"hallucination",
|
|
58
|
+
"openclaw",
|
|
59
|
+
"clawdbot"
|
|
56
60
|
],
|
|
57
61
|
"author": "Sinewave AI <divya@sinewave.ai>",
|
|
58
62
|
"license": "MIT",
|
|
@@ -80,6 +84,9 @@
|
|
|
80
84
|
"src/cli/*.js",
|
|
81
85
|
"src/fix-patterns.js",
|
|
82
86
|
"src/utils.js",
|
|
87
|
+
"src/dedup.js",
|
|
88
|
+
"src/context.js",
|
|
89
|
+
"src/config.js",
|
|
83
90
|
"analyzer.py",
|
|
84
91
|
"ast_parser.py",
|
|
85
92
|
"generic_ast.py",
|
|
@@ -89,7 +96,10 @@
|
|
|
89
96
|
"taint_analyzer.py",
|
|
90
97
|
"requirements.txt",
|
|
91
98
|
"rules/**",
|
|
92
|
-
"packages/**"
|
|
99
|
+
"packages/**",
|
|
100
|
+
"skills/**",
|
|
101
|
+
"scripts/postinstall.js",
|
|
102
|
+
"cross_file_analyzer.py"
|
|
93
103
|
],
|
|
94
104
|
"devDependencies": {
|
|
95
105
|
"all-the-package-names": "^2.0.2349",
|
package/pattern_matcher.py
CHANGED
package/regex_fallback.py
CHANGED
|
@@ -9,10 +9,207 @@ 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
|
+
|
|
12
204
|
def _make_finding(rule_id: str, line_idx: int, line: str, col_start: int = 0, col_end: Optional[int] = None,
|
|
13
|
-
message: Optional[str] = None, severity: str =
|
|
205
|
+
message: Optional[str] = None, severity: Optional[str] = None,
|
|
206
|
+
confidence: Optional[str] = None) -> Dict:
|
|
14
207
|
if col_end is None:
|
|
15
208
|
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")
|
|
16
213
|
return {
|
|
17
214
|
"ruleId": rule_id,
|
|
18
215
|
"message": message or f"[Regex] {rule_id}",
|
|
@@ -22,6 +219,7 @@ def _make_finding(rule_id: str, line_idx: int, line: str, col_start: int = 0, co
|
|
|
22
219
|
"endColumn": col_end,
|
|
23
220
|
"length": max(0, col_end - col_start),
|
|
24
221
|
"severity": severity,
|
|
222
|
+
"confidence": confidence,
|
|
25
223
|
"metadata": {"source": "regex-fallback"},
|
|
26
224
|
"metavariables": {},
|
|
27
225
|
}
|