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/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 doctor --fix');
169
- console.log(' npx agent-security-scanner-mcp demo --lang py\n');
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.2.0",
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",
@@ -430,6 +430,7 @@ class Finding:
430
430
  end_column: int = 0
431
431
  metavariables: Dict[str, str] = field(default_factory=dict)
432
432
  metadata: Dict[str, Any] = field(default_factory=dict)
433
+ confidence: str = "MEDIUM"
433
434
 
434
435
 
435
436
  class RuleEngine:
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 = "warning") -> Dict:
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
  }