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.
- package/bin/doorman.js +18 -11
- package/bin/getdoorman.js +18 -11
- package/package.json +4 -4
- package/src/auth.js +2 -2
- package/src/compliance.js +3 -2
- package/src/hooks.js +11 -31
- package/src/reporter.js +7 -6
- package/src/rules/bugs/ai-codegen.js +1 -1
- package/src/rules/bugs/crypto-bugs.js +1 -1
- package/src/rules/bugs/go-bugs.js +1 -1
- package/src/rules/bugs/nextjs-bugs.js +3 -1
- package/src/rules/compliance/healthcare.js +4 -4
- package/src/rules/compliance/regional-eu.js +1 -1
- package/src/rules/compliance/regional-international.js +4 -4
- package/src/rules/infrastructure/index.js +5 -5
- package/src/rules/quality/index.js +8 -8
- package/src/rules/scope-rules.js +2 -2
- package/src/rules/security/ai-api.js +8 -8
- package/src/rules/security/auth.js +5 -5
- package/src/rules/security/csharp.js +15 -15
- package/src/rules/security/dart.js +6 -6
- package/src/rules/security/deserialization.js +1 -1
- package/src/rules/security/go.js +8 -8
- package/src/rules/security/injection.js +26 -23
- package/src/rules/security/mcp-server.js +4 -4
- package/src/rules/security/oauth-jwt.js +1 -1
- package/src/rules/security/path-traversal.js +6 -6
- package/src/rules/security/php.js +28 -28
- package/src/rules/security/ruby.js +20 -20
- package/src/rules/security/rust.js +6 -6
- package/src/rules/security/shell.js +9 -9
- package/src/rules/security/ssrf.js +19 -3
- package/src/rules/security/swift.js +3 -3
- package/src/rules/security/taint.js +1 -1
- package/src/scanner.js +1 -2
- package/src/telemetry.js +2 -1
- 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('
|
|
16
|
+
.name('getdoorman')
|
|
11
17
|
.description('Find security issues. Tell your AI to fix them.')
|
|
12
|
-
.version(
|
|
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
|
|
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
|
|
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.
|
|
69
|
-
if (agentsMd) console.log(chalk.
|
|
70
|
-
if (cursorRules) console.log(chalk.
|
|
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
|
-
//
|
|
78
|
+
// Auto-run hook for Claude Code (non-blocking, just a scan)
|
|
73
79
|
const hookInstalled = installClaudeHook(resolvedPath);
|
|
74
|
-
if (hookInstalled) console.log(chalk.
|
|
75
|
-
|
|
76
|
-
|
|
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('
|
|
16
|
+
.name('getdoorman')
|
|
11
17
|
.description('Find security issues. Tell your AI to fix them.')
|
|
12
|
-
.version(
|
|
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
|
|
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
|
|
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.
|
|
69
|
-
if (agentsMd) console.log(chalk.
|
|
70
|
-
if (cursorRules) console.log(chalk.
|
|
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
|
-
//
|
|
78
|
+
// Auto-run hook for Claude Code (non-blocking, just a scan)
|
|
73
79
|
const hookInstalled = installClaudeHook(resolvedPath);
|
|
74
|
-
if (hookInstalled) console.log(chalk.
|
|
75
|
-
|
|
76
|
-
|
|
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.
|
|
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://
|
|
39
|
+
"url": "https://doorman.sh"
|
|
40
40
|
},
|
|
41
|
-
"homepage": "https://
|
|
41
|
+
"homepage": "https://doorman.sh",
|
|
42
42
|
"bugs": {
|
|
43
|
-
"url": "https://
|
|
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: '
|
|
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: '
|
|
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>
|
|
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
|
|
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
|
|
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
|
-
|
|
98
|
+
// Append one line — don't overwrite existing content
|
|
99
|
+
writeFileSync(claudeMdPath, content + doormanLine);
|
|
108
100
|
} else {
|
|
109
|
-
writeFileSync(claudeMdPath,
|
|
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
|
|
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 +
|
|
116
|
+
writeFileSync(agentsMdPath, content + doormanLine);
|
|
134
117
|
} else {
|
|
135
|
-
writeFileSync(agentsMdPath,
|
|
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
|
|
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 +
|
|
133
|
+
writeFileSync(cursorPath, content + doormanLine);
|
|
155
134
|
} else {
|
|
156
|
-
|
|
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(
|
|
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
|
|
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:
|
|
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:
|
|
699
|
-
informationUri: 'https://
|
|
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
|
|
890
|
+
<p class="meta">Generated by Doorman v</p>
|
|
890
891
|
</div>
|
|
891
892
|
|
|
892
893
|
<script>
|
|
@@ -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
|
-
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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) {
|
package/src/rules/scope-rules.js
CHANGED
|
@@ -577,7 +577,7 @@ const rules = [
|
|
|
577
577
|
{
|
|
578
578
|
id: 'SCOPE-SEC-001',
|
|
579
579
|
category: 'security',
|
|
580
|
-
severity: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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.',
|