getdoorman 1.0.5 → 1.0.7

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.
Files changed (37) hide show
  1. package/bin/doorman.js +18 -11
  2. package/bin/getdoorman.js +18 -11
  3. package/package.json +4 -4
  4. package/src/auth.js +2 -2
  5. package/src/compliance.js +3 -2
  6. package/src/hooks.js +11 -31
  7. package/src/reporter.js +7 -6
  8. package/src/rules/bugs/ai-codegen.js +1 -1
  9. package/src/rules/bugs/crypto-bugs.js +1 -1
  10. package/src/rules/bugs/go-bugs.js +1 -1
  11. package/src/rules/bugs/nextjs-bugs.js +3 -1
  12. package/src/rules/compliance/healthcare.js +4 -4
  13. package/src/rules/compliance/regional-eu.js +1 -1
  14. package/src/rules/compliance/regional-international.js +4 -4
  15. package/src/rules/infrastructure/index.js +5 -5
  16. package/src/rules/quality/index.js +8 -8
  17. package/src/rules/scope-rules.js +2 -2
  18. package/src/rules/security/ai-api.js +8 -8
  19. package/src/rules/security/auth.js +5 -5
  20. package/src/rules/security/csharp.js +15 -15
  21. package/src/rules/security/dart.js +6 -6
  22. package/src/rules/security/deserialization.js +1 -1
  23. package/src/rules/security/go.js +8 -8
  24. package/src/rules/security/injection.js +26 -23
  25. package/src/rules/security/mcp-server.js +4 -4
  26. package/src/rules/security/oauth-jwt.js +1 -1
  27. package/src/rules/security/path-traversal.js +6 -6
  28. package/src/rules/security/php.js +28 -28
  29. package/src/rules/security/ruby.js +20 -20
  30. package/src/rules/security/rust.js +6 -6
  31. package/src/rules/security/shell.js +9 -9
  32. package/src/rules/security/ssrf.js +19 -3
  33. package/src/rules/security/swift.js +3 -3
  34. package/src/rules/security/taint.js +1 -1
  35. package/src/scanner.js +1 -2
  36. package/src/telemetry.js +2 -1
  37. package/src/version.js +8 -0
package/bin/doorman.js CHANGED
@@ -3,13 +3,19 @@
3
3
  import { Command } from 'commander';
4
4
  import { check } from '../src/index.js';
5
5
  import { generateReport } from '../src/compliance.js';
6
+ import { readFileSync } from 'fs';
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname, join } from 'path';
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
6
12
 
7
13
  const program = new Command();
8
14
 
9
15
  program
10
- .name('doorman')
16
+ .name('getdoorman')
11
17
  .description('Find security issues. Tell your AI to fix them.')
12
- .version('1.0.0');
18
+ .version(pkg.version);
13
19
 
14
20
  program
15
21
  .command('check')
@@ -53,7 +59,7 @@ program
53
59
 
54
60
  // After first scan: add CLAUDE.md, offer hooks, collect email
55
61
  if (!options.ci && !options.quiet && !options.json && !options.sarif && !options.html && !options.silent) {
56
- const { isFirstScan, installClaudeMd, installAgentsMd, installCursorRules, installClaudeHook, installGitHook } = await import('../src/hooks.js');
62
+ const { isFirstScan, installClaudeMd, installAgentsMd, installCursorRules, installClaudeHook } = await import('../src/hooks.js');
57
63
  const { loadAuth, saveAuth } = await import('../src/auth.js');
58
64
  const { resolve } = await import('path');
59
65
  const resolvedPath = resolve(path);
@@ -61,19 +67,20 @@ program
61
67
  if (isFirstScan(resolvedPath)) {
62
68
  const chalk = (await import('chalk')).default;
63
69
 
64
- // Add to all AI tool config files (viral growth)
70
+ // Add Doorman instructions to AI config files (only appends, never overwrites)
65
71
  const claudeMd = installClaudeMd(resolvedPath);
66
72
  const agentsMd = installAgentsMd(resolvedPath);
67
73
  const cursorRules = installCursorRules(resolvedPath);
68
- if (claudeMd) console.log(chalk.green(' Added to CLAUDE.md (Claude will suggest Doorman)'));
69
- if (agentsMd) console.log(chalk.green(' Added to AGENTS.md (Codex will suggest Doorman)'));
70
- if (cursorRules) console.log(chalk.green(' Added to .cursorrules (Cursor will suggest Doorman)'));
74
+ if (claudeMd) console.log(chalk.dim(' Added Doorman to CLAUDE.md'));
75
+ if (agentsMd) console.log(chalk.dim(' Added Doorman to AGENTS.md'));
76
+ if (cursorRules) console.log(chalk.dim(' Added Doorman to .cursorrules'));
71
77
 
72
- // Install hooks
78
+ // Auto-run hook for Claude Code (non-blocking, just a scan)
73
79
  const hookInstalled = installClaudeHook(resolvedPath);
74
- if (hookInstalled) console.log(chalk.green(' Auto-run enabled scans after every file edit'));
75
- const gitInstalled = installGitHook(resolvedPath);
76
- if (gitInstalled) console.log(chalk.green(' ✓ Pre-commit hook installed'));
80
+ if (hookInstalled) console.log(chalk.dim(' Auto-run enabled for Claude Code'));
81
+
82
+ // NOTE: No pre-commit hook on first scan — too aggressive.
83
+ // Users can opt in with: npx getdoorman init
77
84
 
78
85
  // Collect email
79
86
  const auth = loadAuth();
package/bin/getdoorman.js CHANGED
@@ -3,13 +3,19 @@
3
3
  import { Command } from 'commander';
4
4
  import { check } from '../src/index.js';
5
5
  import { generateReport } from '../src/compliance.js';
6
+ import { readFileSync } from 'fs';
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname, join } from 'path';
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
6
12
 
7
13
  const program = new Command();
8
14
 
9
15
  program
10
- .name('doorman')
16
+ .name('getdoorman')
11
17
  .description('Find security issues. Tell your AI to fix them.')
12
- .version('1.0.0');
18
+ .version(pkg.version);
13
19
 
14
20
  program
15
21
  .command('check')
@@ -53,7 +59,7 @@ program
53
59
 
54
60
  // After first scan: add CLAUDE.md, offer hooks, collect email
55
61
  if (!options.ci && !options.quiet && !options.json && !options.sarif && !options.html && !options.silent) {
56
- const { isFirstScan, installClaudeMd, installAgentsMd, installCursorRules, installClaudeHook, installGitHook } = await import('../src/hooks.js');
62
+ const { isFirstScan, installClaudeMd, installAgentsMd, installCursorRules, installClaudeHook } = await import('../src/hooks.js');
57
63
  const { loadAuth, saveAuth } = await import('../src/auth.js');
58
64
  const { resolve } = await import('path');
59
65
  const resolvedPath = resolve(path);
@@ -61,19 +67,20 @@ program
61
67
  if (isFirstScan(resolvedPath)) {
62
68
  const chalk = (await import('chalk')).default;
63
69
 
64
- // Add to all AI tool config files (viral growth)
70
+ // Add Doorman instructions to AI config files (only appends, never overwrites)
65
71
  const claudeMd = installClaudeMd(resolvedPath);
66
72
  const agentsMd = installAgentsMd(resolvedPath);
67
73
  const cursorRules = installCursorRules(resolvedPath);
68
- if (claudeMd) console.log(chalk.green(' Added to CLAUDE.md (Claude will suggest Doorman)'));
69
- if (agentsMd) console.log(chalk.green(' Added to AGENTS.md (Codex will suggest Doorman)'));
70
- if (cursorRules) console.log(chalk.green(' Added to .cursorrules (Cursor will suggest Doorman)'));
74
+ if (claudeMd) console.log(chalk.dim(' Added Doorman to CLAUDE.md'));
75
+ if (agentsMd) console.log(chalk.dim(' Added Doorman to AGENTS.md'));
76
+ if (cursorRules) console.log(chalk.dim(' Added Doorman to .cursorrules'));
71
77
 
72
- // Install hooks
78
+ // Auto-run hook for Claude Code (non-blocking, just a scan)
73
79
  const hookInstalled = installClaudeHook(resolvedPath);
74
- if (hookInstalled) console.log(chalk.green(' Auto-run enabled scans after every file edit'));
75
- const gitInstalled = installGitHook(resolvedPath);
76
- if (gitInstalled) console.log(chalk.green(' ✓ Pre-commit hook installed'));
80
+ if (hookInstalled) console.log(chalk.dim(' Auto-run enabled for Claude Code'));
81
+
82
+ // NOTE: No pre-commit hook on first scan — too aggressive.
83
+ // Users can opt in with: npx getdoorman init
77
84
 
78
85
  // Collect email
79
86
  const auth = loadAuth();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getdoorman",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Zero-config security scanner for AI-assisted development. 2000+ rules, 11 languages, 4 detection engines.",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -36,11 +36,11 @@
36
36
  "license": "MIT",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/tiago520/VoiceAI.git"
39
+ "url": "https://doorman.sh"
40
40
  },
41
- "homepage": "https://github.com/tiago520/VoiceAI#readme",
41
+ "homepage": "https://doorman.sh",
42
42
  "bugs": {
43
- "url": "https://github.com/tiago520/VoiceAI/issues"
43
+ "url": "https://doorman.sh"
44
44
  },
45
45
  "files": [
46
46
  "src/",
package/src/auth.js CHANGED
@@ -12,12 +12,12 @@ const PLANS = {
12
12
  categories: ['security', 'bugs'],
13
13
  },
14
14
  pro: {
15
- name: 'Pro', price: '$20/mo', maxScans: Infinity,
15
+ name: 'Pro', price: 'coming soon', maxScans: Infinity,
16
16
  categories: ['security', 'bugs', 'performance', 'reliability', 'cost', 'data', 'quality'],
17
17
  dashboard: true, autoRun: true, cicd: true,
18
18
  },
19
19
  enterprise: {
20
- name: 'Enterprise', price: '$100/mo', maxScans: Infinity,
20
+ name: 'Enterprise', price: 'coming soon', maxScans: Infinity,
21
21
  categories: 'all',
22
22
  dashboard: true, autoRun: true, cicd: true,
23
23
  compliance: true, teamDashboard: true, prComments: true, slack: true, customRules: true,
package/src/compliance.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { VERSION } from './version.js';
1
2
  import { writeFileSync } from 'fs';
2
3
  import { resolve } from 'path';
3
4
 
@@ -421,7 +422,7 @@ export function generateReport(scanResult, options = {}) {
421
422
  <tr><td>Project</td><td>${escapeHtml(projectName)}</td></tr>
422
423
  <tr><td>Detected Stack</td><td>${escapeHtml(stackLabel)}</td></tr>
423
424
  <tr><td>Total Findings</td><td>${findings.length}</td></tr>
424
- <tr><td>Doorman Version</td><td>v1.0.0</td></tr>
425
+ <tr><td>Doorman Version</td><td>v${VERSION}</td></tr>
425
426
  </table>
426
427
 
427
428
  <div class="section">
@@ -486,7 +487,7 @@ export function generateReport(scanResult, options = {}) {
486
487
 
487
488
  <div class="attestation">
488
489
  <strong>Attestation</strong><br>
489
- This report was generated by Doorman v1.0.0 on ${escapeHtml(reportTime)}. The findings
490
+ This report was generated by Doorman v${VERSION} on ${escapeHtml(reportTime)}. The findings
490
491
  reflect an automated analysis of the project source code at the time of the scan. This report
491
492
  is intended to support compliance efforts and should be reviewed by a qualified auditor before
492
493
  being used as evidence for certification or regulatory purposes.
package/src/hooks.js CHANGED
@@ -90,23 +90,15 @@ npx getdoorman check --quiet --ci
90
90
  */
91
91
  export function installClaudeMd(targetPath) {
92
92
  const claudeMdPath = join(targetPath, 'CLAUDE.md');
93
- const doormanBlock = `
94
- ## Doorman — Code Safety
95
-
96
- Before shipping or when asked to check code safety, run:
97
- \`\`\`bash
98
- npx getdoorman check
99
- \`\`\`
100
-
101
- To fix issues, run \`npx getdoorman fix critical\` and follow the instructions.
102
- `;
93
+ const doormanLine = '\n# Run `npx getdoorman check` before shipping to find security issues.\n';
103
94
 
104
95
  if (existsSync(claudeMdPath)) {
105
96
  const content = readFileSync(claudeMdPath, 'utf-8');
106
97
  if (content.includes('doorman') || content.includes('getdoorman')) return false;
107
- writeFileSync(claudeMdPath, content + '\n' + doormanBlock);
98
+ // Append one line — don't overwrite existing content
99
+ writeFileSync(claudeMdPath, content + doormanLine);
108
100
  } else {
109
- writeFileSync(claudeMdPath, doormanBlock.trimStart());
101
+ writeFileSync(claudeMdPath, doormanLine.trimStart());
110
102
  }
111
103
  return true;
112
104
  }
@@ -116,23 +108,14 @@ To fix issues, run \`npx getdoorman fix critical\` and follow the instructions.
116
108
  */
117
109
  export function installAgentsMd(targetPath) {
118
110
  const agentsMdPath = join(targetPath, 'AGENTS.md');
119
- const doormanBlock = `
120
- ## Doorman — Code Safety
121
-
122
- Before shipping or when asked to check code safety, run:
123
- \`\`\`bash
124
- npx getdoorman check
125
- \`\`\`
126
-
127
- To fix issues, run \`npx getdoorman fix critical\` and follow the instructions.
128
- `;
111
+ const doormanLine = '\n# Run `npx getdoorman check` before shipping to find security issues.\n';
129
112
 
130
113
  if (existsSync(agentsMdPath)) {
131
114
  const content = readFileSync(agentsMdPath, 'utf-8');
132
115
  if (content.includes('doorman') || content.includes('getdoorman')) return false;
133
- writeFileSync(agentsMdPath, content + '\n' + doormanBlock);
116
+ writeFileSync(agentsMdPath, content + doormanLine);
134
117
  } else {
135
- writeFileSync(agentsMdPath, doormanBlock.trimStart());
118
+ writeFileSync(agentsMdPath, doormanLine.trimStart());
136
119
  }
137
120
  return true;
138
121
  }
@@ -142,18 +125,15 @@ To fix issues, run \`npx getdoorman fix critical\` and follow the instructions.
142
125
  */
143
126
  export function installCursorRules(targetPath) {
144
127
  const cursorPath = join(targetPath, '.cursorrules');
145
- const doormanBlock = `
146
- # Doorman — Code Safety
147
- Before shipping or when asked to check code safety, run: npx getdoorman check
148
- To fix issues, run: npx getdoorman fix critical
149
- `;
128
+ const doormanLine = '\n# Run `npx getdoorman check` before shipping to find security issues.\n';
150
129
 
151
130
  if (existsSync(cursorPath)) {
152
131
  const content = readFileSync(cursorPath, 'utf-8');
153
132
  if (content.includes('doorman') || content.includes('getdoorman')) return false;
154
- writeFileSync(cursorPath, content + '\n' + doormanBlock);
133
+ writeFileSync(cursorPath, content + doormanLine);
155
134
  } else {
156
- writeFileSync(cursorPath, doormanBlock.trimStart());
135
+ // Don't create .cursorrules if it doesn't exist — too invasive
136
+ return false;
157
137
  }
158
138
  return true;
159
139
  }
package/src/reporter.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { writeFileSync } from 'fs';
3
+ import { VERSION } from './version.js';
3
4
 
4
5
  const SEVERITY_COLORS = {
5
6
  critical: chalk.bgRed.white.bold,
@@ -406,7 +407,7 @@ export function printReport(findings, stack, score, options = {}) {
406
407
 
407
408
  console.log('');
408
409
  console.log(chalk.bold.cyan('\u2501'.repeat(60)));
409
- console.log(chalk.bold.cyan(' Doorman v1.0.0 \u2014 Scan Results'));
410
+ console.log(chalk.bold.cyan(` Doorman v 2014 Scan Results`));
410
411
  console.log(chalk.bold.cyan('\u2501'.repeat(60)));
411
412
  console.log('');
412
413
 
@@ -556,7 +557,7 @@ function printVibeCoderReport(visible, score, hiddenCategoryCount = 0, proUnlock
556
557
  // --- Upsell ---
557
558
  if (proUnlockCount > 0) {
558
559
  console.log(chalk.cyan(` 🔒 ${proUnlockCount} more issues in performance, cost, reliability, data...`));
559
- console.log(chalk.gray(' Upgrade to Pro ($20/mo) to see all categories + unlimited fixes.'));
560
+ console.log(chalk.gray(' Upgrade to Pro to see all categories. Coming soon at doorman.sh.'));
560
561
  console.log('');
561
562
  }
562
563
  if (enterpriseUnlockCount > 0 && proUnlockCount === 0) {
@@ -575,7 +576,7 @@ export function generateJSON(findings, stack, score, metadata = {}) {
575
576
  const categoryCounts = countByCategory(findings);
576
577
 
577
578
  return {
578
- version: '1.0.0',
579
+ version: VERSION,
579
580
  timestamp: new Date().toISOString(),
580
581
  score,
581
582
  summary: {
@@ -695,8 +696,8 @@ export function generateSARIF(findings, stack, score, metadata = {}) {
695
696
  tool: {
696
697
  driver: {
697
698
  name: 'Doorman',
698
- version: '1.0.0',
699
- informationUri: 'https://github.com/tiago520/VoiceAI',
699
+ version: VERSION,
700
+ informationUri: 'https://doorman.sh',
700
701
  rules,
701
702
  },
702
703
  },
@@ -886,7 +887,7 @@ export function generateHTML(findings, stack, score, metadata = {}) {
886
887
  </table>
887
888
  </div>
888
889
 
889
- <p class="meta">Generated by Doorman v1.0.0</p>
890
+ <p class="meta">Generated by Doorman v</p>
890
891
  </div>
891
892
 
892
893
  <script>
@@ -194,7 +194,7 @@ const rules = [
194
194
  {
195
195
  id: 'BUG-AI-006',
196
196
  category: 'bugs',
197
- severity: 'critical',
197
+ severity: 'high',
198
198
  confidence: 'likely',
199
199
  title: 'User input used directly in database query',
200
200
  check({ files }) {
@@ -161,7 +161,7 @@ const rules = [
161
161
  {
162
162
  id: 'BUG-CRYPTO-005',
163
163
  category: 'bugs',
164
- severity: 'critical',
164
+ severity: 'high',
165
165
  confidence: 'likely',
166
166
  title: 'Hardcoded encryption key or IV',
167
167
  check({ files }) {
@@ -94,7 +94,7 @@ const rules = [
94
94
  {
95
95
  id: 'BUG-GO-004',
96
96
  category: 'bugs',
97
- severity: 'critical',
97
+ severity: 'high',
98
98
  confidence: 'likely',
99
99
  title: 'Race condition — shared variable in goroutine without sync',
100
100
  check({ files }) {
@@ -20,7 +20,9 @@ const rules = [
20
20
  if (hasUseClient) continue;
21
21
  const lines = content.split('\n');
22
22
  for (let i = 0; i < lines.length; i++) {
23
- if (/\b(window|document|localStorage|sessionStorage|navigator|location\.href|addEventListener)\b/.test(lines[i]) && !/typeof\s+(window|document)/.test(lines[i]) && !/\/\//.test(lines[i].split(/window|document/)[0])) {
23
+ // Skip API route files — request.signal, addEventListener are valid server-side
24
+ if (/\/api\//.test(fp) || /route\.(ts|js)$/.test(fp)) continue;
25
+ if (/\b(window|document|localStorage|sessionStorage|navigator)\b/.test(lines[i]) && !/typeof\s+(window|document)/.test(lines[i]) && !/\/\//.test(lines[i].split(/window|document/)[0]) && !/request\.signal|AbortSignal|abortController/i.test(lines[i])) {
24
26
  findings.push({
25
27
  ruleId: 'BUG-NEXT-001', category: 'bugs', severity: 'high',
26
28
  title: 'Browser API used in server component — will crash at build time',
@@ -23,7 +23,7 @@ function hasHealthContext(files) {
23
23
  const rules = [
24
24
  // COMP-HIPAA-007: PHI in error messages/stack traces
25
25
  {
26
- id: 'COMP-HIPAA-007', category: 'compliance', severity: 'critical', confidence: 'likely',
26
+ id: 'COMP-HIPAA-007', category: 'compliance', severity: 'high', confidence: 'likely',
27
27
  title: 'PHI Exposed in Error Messages',
28
28
  check({ files }) {
29
29
  const findings = [];
@@ -68,7 +68,7 @@ const rules = [
68
68
 
69
69
  // COMP-HIPAA-009: PHI shared with third-party SDKs
70
70
  {
71
- id: 'COMP-HIPAA-009', category: 'compliance', severity: 'critical', confidence: 'likely',
71
+ id: 'COMP-HIPAA-009', category: 'compliance', severity: 'high', confidence: 'likely',
72
72
  title: 'PHI Shared with Third-Party Monitoring SDKs',
73
73
  check({ files }) {
74
74
  const findings = [];
@@ -131,7 +131,7 @@ const rules = [
131
131
 
132
132
  // COMP-HIPAA-012: Missing access controls on health endpoints
133
133
  {
134
- id: 'COMP-HIPAA-012', category: 'compliance', severity: 'critical', confidence: 'likely',
134
+ id: 'COMP-HIPAA-012', category: 'compliance', severity: 'high', confidence: 'likely',
135
135
  title: 'Missing Access Controls on Health Endpoints',
136
136
  check({ files }) {
137
137
  const findings = [];
@@ -384,7 +384,7 @@ const rules = [
384
384
 
385
385
  // COMP-HIPAA-024: Telehealth without end-to-end encryption
386
386
  {
387
- id: 'COMP-HIPAA-024', category: 'compliance', severity: 'critical', confidence: 'likely',
387
+ id: 'COMP-HIPAA-024', category: 'compliance', severity: 'high', confidence: 'likely',
388
388
  title: 'Telehealth Without End-to-End Encryption',
389
389
  check({ files }) {
390
390
  const findings = [];
@@ -364,7 +364,7 @@ const rules = [
364
364
  },
365
365
  },
366
366
 
367
- { id: 'COMP-EUAI-007', category: 'compliance', severity: 'critical', confidence: 'likely',
367
+ { id: 'COMP-EUAI-007', category: 'compliance', severity: 'high', confidence: 'likely',
368
368
  title: 'Emotion Recognition Without Safeguards',
369
369
  check({ files }) {
370
370
  const findings = [];
@@ -344,7 +344,7 @@ const rules = [
344
344
  {
345
345
  id: 'COMP-LGPD-006',
346
346
  category: 'compliance',
347
- severity: 'critical',
347
+ severity: 'high',
348
348
  confidence: 'likely',
349
349
  title: 'LGPD: Children\'s Data Without Parental Consent',
350
350
  check({ files }) {
@@ -513,7 +513,7 @@ const rules = [
513
513
  {
514
514
  id: 'COMP-PIPEDA-006',
515
515
  category: 'compliance',
516
- severity: 'critical',
516
+ severity: 'high',
517
517
  confidence: 'likely',
518
518
  title: 'PIPEDA: Sensitive Data Without Explicit Consent',
519
519
  check({ files }) {
@@ -720,7 +720,7 @@ const rules = [
720
720
  {
721
721
  id: 'COMP-DPDPA-003',
722
722
  category: 'compliance',
723
- severity: 'critical',
723
+ severity: 'high',
724
724
  confidence: 'likely',
725
725
  title: 'DPDPA: Children\'s Data Without Verifiable Parental Consent',
726
726
  check({ files }) {
@@ -878,7 +878,7 @@ const rules = [
878
878
  {
879
879
  id: 'COMP-APPI-005',
880
880
  category: 'compliance',
881
- severity: 'critical',
881
+ severity: 'high',
882
882
  confidence: 'likely',
883
883
  title: 'APPI: Sensitive Personal Info Without Explicit Consent',
884
884
  check({ files }) {
@@ -702,7 +702,7 @@ const rules = [
702
702
  },
703
703
 
704
704
  // INFRA-TF-005: Public S3 bucket in Terraform
705
- { id: 'INFRA-TF-005', category: 'infrastructure', severity: 'critical', confidence: 'likely', title: 'Terraform S3 Bucket Publicly Accessible',
705
+ { id: 'INFRA-TF-005', category: 'infrastructure', severity: 'high', confidence: 'likely', title: 'Terraform S3 Bucket Publicly Accessible',
706
706
  check({ files }) {
707
707
  const findings = [];
708
708
  for (const [fp, c] of files) {
@@ -1459,7 +1459,7 @@ const rules = [
1459
1459
  },
1460
1460
  },
1461
1461
  // INFRA-CLOUD-029: No S3 bucket public access block
1462
- { id: 'INFRA-CLOUD-029', category: 'infrastructure', severity: 'critical', confidence: 'likely', title: 'S3 Bucket Without Public Access Block',
1462
+ { id: 'INFRA-CLOUD-029', category: 'infrastructure', severity: 'high', confidence: 'likely', title: 'S3 Bucket Without Public Access Block',
1463
1463
  check({ files }) {
1464
1464
  const findings = [];
1465
1465
  for (const [fp, c] of files) {
@@ -2193,7 +2193,7 @@ rules.push({
2193
2193
 
2194
2194
  // INFRA-TF-039: IAM role with wildcard permissions
2195
2195
  rules.push({
2196
- id: 'INFRA-TF-039', category: 'infrastructure', severity: 'critical', confidence: 'likely', title: 'IAM role with wildcard action (*) permissions',
2196
+ id: 'INFRA-TF-039', category: 'infrastructure', severity: 'high', confidence: 'likely', title: 'IAM role with wildcard action (*) permissions',
2197
2197
  check({ files }) {
2198
2198
  const findings = [];
2199
2199
  for (const [fp, c] of files) {
@@ -2348,7 +2348,7 @@ rules.push({
2348
2348
 
2349
2349
  // INFRA-TF-044: S3 bucket with public access allowed
2350
2350
  rules.push({
2351
- id: 'INFRA-TF-044', category: 'infrastructure', severity: 'critical', confidence: 'likely', title: 'Terraform S3 bucket without block public access settings',
2351
+ id: 'INFRA-TF-044', category: 'infrastructure', severity: 'high', confidence: 'likely', title: 'Terraform S3 bucket without block public access settings',
2352
2352
  check({ files }) {
2353
2353
  const findings = [];
2354
2354
  for (const [fp, c] of files) {
@@ -2666,7 +2666,7 @@ rules.push({
2666
2666
 
2667
2667
  // INFRA-064: Cloud storage bucket public access
2668
2668
  rules.push({
2669
- id: 'INFRA-064', category: 'infrastructure', severity: 'critical', confidence: 'likely', title: 'Cloud storage with public access enabled',
2669
+ id: 'INFRA-064', category: 'infrastructure', severity: 'high', confidence: 'likely', title: 'Cloud storage with public access enabled',
2670
2670
  check({ files }) {
2671
2671
  const findings = [];
2672
2672
  for (const [fp, c] of files) {
@@ -381,7 +381,7 @@ const rules = [
381
381
  },
382
382
 
383
383
  // QUAL-SMELL-001: eval() usage
384
- { id: 'QUAL-SMELL-001', category: 'quality', severity: 'critical', confidence: 'likely', title: 'eval() Usage',
384
+ { id: 'QUAL-SMELL-001', category: 'quality', severity: 'high', confidence: 'likely', title: 'eval() Usage',
385
385
  check({ files }) {
386
386
  const findings = [];
387
387
  for (const [fp, c] of files) {
@@ -399,7 +399,7 @@ const rules = [
399
399
  },
400
400
 
401
401
  // QUAL-SMELL-002: new Function() with dynamic string
402
- { id: 'QUAL-SMELL-002', category: 'quality', severity: 'critical', confidence: 'likely', title: 'new Function() — Behaves Like eval()',
402
+ { id: 'QUAL-SMELL-002', category: 'quality', severity: 'high', confidence: 'likely', title: 'new Function() — Behaves Like eval()',
403
403
  check({ files }) {
404
404
  const findings = [];
405
405
  for (const [fp, c] of files) {
@@ -1184,7 +1184,7 @@ const rules = [
1184
1184
  },
1185
1185
 
1186
1186
  // QUAL-SEC-002: Insecure random for tokens
1187
- { id: 'QUAL-SEC-002', category: 'quality', severity: 'critical', confidence: 'likely', title: 'Math.random() Used for Security Tokens',
1187
+ { id: 'QUAL-SEC-002', category: 'quality', severity: 'high', confidence: 'likely', title: 'Math.random() Used for Security Tokens',
1188
1188
  check({ files }) {
1189
1189
  const findings = [];
1190
1190
  for (const [fp, c] of files) {
@@ -1361,7 +1361,7 @@ const rules = [
1361
1361
  },
1362
1362
 
1363
1363
  // QUAL-SEC-005: Hardcoded JWT secret
1364
- { id: 'QUAL-SEC-005', category: 'quality', severity: 'critical', confidence: 'likely', title: 'Hardcoded JWT Secret',
1364
+ { id: 'QUAL-SEC-005', category: 'quality', severity: 'high', confidence: 'likely', title: 'Hardcoded JWT Secret',
1365
1365
  check({ files }) {
1366
1366
  const findings = [];
1367
1367
  for (const [fp, c] of files) {
@@ -1520,7 +1520,7 @@ const rules = [
1520
1520
  },
1521
1521
  },
1522
1522
  // QUAL-SEC-007: Command injection via exec
1523
- { id: 'QUAL-SEC-007', category: 'quality', severity: 'critical', confidence: 'likely', title: 'Command Injection via exec/spawn',
1523
+ { id: 'QUAL-SEC-007', category: 'quality', severity: 'high', confidence: 'likely', title: 'Command Injection via exec/spawn',
1524
1524
  check({ files }) {
1525
1525
  const findings = [];
1526
1526
  for (const [fp, c] of files) {
@@ -1824,7 +1824,7 @@ const rules = [
1824
1824
  },
1825
1825
  },
1826
1826
  // QUAL-SEC-010: Using eval() with user-controlled input
1827
- { id: 'QUAL-SEC-010', category: 'quality', severity: 'critical', confidence: 'likely', title: 'eval() Used with Potentially User-Controlled Data',
1827
+ { id: 'QUAL-SEC-010', category: 'quality', severity: 'high', confidence: 'likely', title: 'eval() Used with Potentially User-Controlled Data',
1828
1828
  check({ files }) {
1829
1829
  const findings = [];
1830
1830
  for (const [fp, c] of files) {
@@ -1937,7 +1937,7 @@ const rules = [
1937
1937
  },
1938
1938
  },
1939
1939
  // QUAL-SEC-011: Insecure deserialization
1940
- { id: 'QUAL-SEC-011', category: 'quality', severity: 'critical', confidence: 'likely', title: 'Insecure Deserialization with node-serialize',
1940
+ { id: 'QUAL-SEC-011', category: 'quality', severity: 'high', confidence: 'likely', title: 'Insecure Deserialization with node-serialize',
1941
1941
  check({ files, stack }) {
1942
1942
  const findings = [];
1943
1943
  const allDeps = { ...stack.dependencies, ...stack.devDependencies };
@@ -1953,7 +1953,7 @@ const rules = [
1953
1953
  },
1954
1954
  },
1955
1955
  // QUAL-ARCH-014: No input sanitization before DB queries
1956
- { id: 'QUAL-ARCH-014', category: 'quality', severity: 'critical', confidence: 'likely', title: 'String Template Used in Database Query',
1956
+ { id: 'QUAL-ARCH-014', category: 'quality', severity: 'high', confidence: 'likely', title: 'String Template Used in Database Query',
1957
1957
  check({ files }) {
1958
1958
  const findings = [];
1959
1959
  for (const [fp, c] of files) {
@@ -577,7 +577,7 @@ const rules = [
577
577
  {
578
578
  id: 'SCOPE-SEC-001',
579
579
  category: 'security',
580
- severity: 'critical',
580
+ severity: 'high',
581
581
  confidence: 'likely',
582
582
  title: 'eval/exec Outside Sandboxed Context',
583
583
  description: 'eval() or exec() used outside of a sandboxing function (vm.runInNewContext, sandbox, etc.).',
@@ -596,7 +596,7 @@ const rules = [
596
596
  for (let i = 0; i < lines.length; i++) {
597
597
  const lineNum = i + 1;
598
598
  if (scope.isInsideComment(lineNum) || scope.isInsideString(lineNum)) continue;
599
- if (/\b(eval|Function)\s*\(/.test(lines[i]) || /\bexec\s*\(/.test(lines[i])) {
599
+ if (/\b(eval|Function)\s*\(/.test(lines[i]) || /\b(child_process\.|execSync|execFileSync)\s*\(/.test(lines[i]) || /(?<!\w\.)\bexec\s*\(/.test(lines[i]) && !/\.exec\s*\(/.test(lines[i])) {
600
600
  if (!hasSandbox) {
601
601
  findings.push({
602
602
  ruleId: this.id, category: this.category, severity: this.severity,
@@ -116,7 +116,7 @@ const rules = [
116
116
  {
117
117
  id: 'SEC-AI-004',
118
118
  category: 'security',
119
- severity: 'critical',
119
+ severity: 'high',
120
120
  confidence: 'likely',
121
121
  title: 'Hardcoded AI provider API key',
122
122
  description: 'AI provider API key (Cohere, Mistral, Replicate, HuggingFace, etc.) is hardcoded in source code.',
@@ -137,7 +137,7 @@ const rules = [
137
137
  {
138
138
  id: 'SEC-AI-005',
139
139
  category: 'security',
140
- severity: 'critical',
140
+ severity: 'high',
141
141
  confidence: 'likely',
142
142
  title: 'AI API key exposed in client-side code',
143
143
  description: 'AI API key is used in frontend/client code, exposing it to end users via browser DevTools or source maps.',
@@ -191,7 +191,7 @@ const rules = [
191
191
  {
192
192
  id: 'SEC-AI-007',
193
193
  category: 'security',
194
- severity: 'critical',
194
+ severity: 'high',
195
195
  confidence: 'likely',
196
196
  title: 'Prompt injection vulnerability',
197
197
  description: 'User input is directly concatenated or interpolated into AI prompts without sanitization, enabling prompt injection.',
@@ -213,7 +213,7 @@ const rules = [
213
213
  {
214
214
  id: 'SEC-AI-008',
215
215
  category: 'security',
216
- severity: 'critical',
216
+ severity: 'high',
217
217
  confidence: 'likely',
218
218
  title: 'User input injected into system prompt',
219
219
  description: 'User-controlled data is included in the system prompt, allowing privilege escalation via prompt injection.',
@@ -465,7 +465,7 @@ const rules = [
465
465
  {
466
466
  id: 'SEC-AI-018',
467
467
  category: 'security',
468
- severity: 'critical',
468
+ severity: 'high',
469
469
  confidence: 'likely',
470
470
  title: 'Unsafe AI function/tool calling execution',
471
471
  description: 'AI function call results are executed without validation, allowing the AI to trigger arbitrary functions.',
@@ -762,7 +762,7 @@ const rules = [
762
762
  {
763
763
  id: 'SEC-AI-031',
764
764
  category: 'security',
765
- severity: 'critical',
765
+ severity: 'high',
766
766
  confidence: 'likely',
767
767
  title: 'AI output used in code execution',
768
768
  description: 'AI API response is passed to eval(), exec(), or code execution functions, enabling arbitrary code execution.',
@@ -783,7 +783,7 @@ const rules = [
783
783
  {
784
784
  id: 'SEC-AI-032',
785
785
  category: 'security',
786
- severity: 'critical',
786
+ severity: 'high',
787
787
  confidence: 'likely',
788
788
  title: 'AI output used in SQL query construction',
789
789
  description: 'AI API response is interpolated into SQL queries, enabling SQL injection via AI-generated content.',
@@ -805,7 +805,7 @@ const rules = [
805
805
  {
806
806
  id: 'SEC-AI-033',
807
807
  category: 'security',
808
- severity: 'critical',
808
+ severity: 'high',
809
809
  confidence: 'likely',
810
810
  title: 'AI API key embedded in mobile application',
811
811
  description: 'AI API key found in mobile app code (React Native, Flutter, Swift, Kotlin) where it can be extracted.',