getdoorman 1.0.9 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/doorman.js +45 -41
- package/bin/getdoorman.js +45 -41
- package/package.json +1 -1
- package/src/rules/bugs/general.js +3 -2
- package/src/rules/compliance/healthcare.js +1 -1
- package/src/rules/compliance/index.js +24 -24
- package/src/rules/data/index.js +26 -26
- package/src/rules/dependencies/index.js +5 -5
- package/src/rules/deployment/index.js +7 -7
- package/src/rules/infrastructure/index.js +28 -28
- package/src/rules/performance/index.js +1 -1
- package/src/rules/reliability/index.js +1 -1
- package/src/rules/security/ai-api.js +1 -0
- package/src/rules/security/auth.js +2 -2
- package/src/rules/security/cors.js +2 -2
- package/src/rules/security/crypto.js +2 -2
- package/src/rules/security/csharp.js +3 -3
- package/src/rules/security/csrf.js +1 -1
- package/src/rules/security/file-upload.js +1 -1
- package/src/rules/security/go.js +3 -3
- package/src/rules/security/misconfiguration.js +4 -4
- package/src/rules/security/oauth-jwt.js +5 -2
- package/src/rules/security/prototype-pollution.js +2 -2
- package/src/rules/security/rate-limiting.js +1 -1
- package/src/rules/security/rust.js +3 -3
- package/src/rules/security/ssrf.js +8 -4
- package/src/rules/security/supply-chain-advanced.js +2 -2
- package/src/rules/security/xss.js +2 -2
- package/src/simple-checks.js +257 -0
- package/src/simple-reporter.js +60 -0
package/bin/doorman.js
CHANGED
|
@@ -41,8 +41,35 @@ program
|
|
|
41
41
|
.option('--save-baseline [path]', 'Save current findings as baseline for future diffs')
|
|
42
42
|
.option('--profile', 'Show performance profile of slowest rules')
|
|
43
43
|
.option('--share', 'Generate a shareable score card')
|
|
44
|
+
.option('--deep', 'Run full 2,500+ rule scan (advanced)')
|
|
44
45
|
.action(async (path, options) => {
|
|
45
46
|
try {
|
|
47
|
+
// Default: simple 10-check mode. --deep for full scan.
|
|
48
|
+
if (!options.deep && !options.json && !options.sarif && !options.html && !options.ci && !options.detail && !options.strict) {
|
|
49
|
+
const { collectFiles } = await import('../src/scanner.js');
|
|
50
|
+
const { runSimpleChecks, printSimpleReport } = await import('../src/simple-reporter.js');
|
|
51
|
+
const files = await collectFiles(path, { silent: true });
|
|
52
|
+
const results = runSimpleChecks(files);
|
|
53
|
+
printSimpleReport(results);
|
|
54
|
+
|
|
55
|
+
// Viral hooks on first scan
|
|
56
|
+
const { isFirstScan, installClaudeMd, installAgentsMd, installCursorRules, installClaudeHook } = await import('../src/hooks.js');
|
|
57
|
+
const { resolve } = await import('path');
|
|
58
|
+
const resolvedPath = resolve(path);
|
|
59
|
+
if (isFirstScan(resolvedPath)) {
|
|
60
|
+
const chalk = (await import('chalk')).default;
|
|
61
|
+
installClaudeMd(resolvedPath);
|
|
62
|
+
installAgentsMd(resolvedPath);
|
|
63
|
+
installCursorRules(resolvedPath);
|
|
64
|
+
installClaudeHook(resolvedPath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const failCount = results.filter(r => !r.passed).length;
|
|
68
|
+
process.exit(failCount > 0 ? 1 : 0);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Deep mode: full 2,500+ rule scan
|
|
46
73
|
const result = await check(path, options);
|
|
47
74
|
|
|
48
75
|
// Generate shareable score card
|
|
@@ -110,55 +137,32 @@ program
|
|
|
110
137
|
program
|
|
111
138
|
.command('fix')
|
|
112
139
|
.description('Tell your AI to fix issues — works in Claude, Codex, Cursor')
|
|
113
|
-
.argument('[severity]', 'Filter: critical, high, medium, or all', 'critical')
|
|
114
140
|
.argument('[path]', 'Path to scan', '.')
|
|
115
|
-
.action(async (
|
|
141
|
+
.action(async (path) => {
|
|
116
142
|
const chalk = (await import('chalk')).default;
|
|
143
|
+
const { collectFiles } = await import('../src/scanner.js');
|
|
144
|
+
const { runSimpleChecks } = await import('../src/simple-reporter.js');
|
|
117
145
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const { loadScanCache } = await import('../src/scan-cache.js');
|
|
122
|
-
const { resolve } = await import('path');
|
|
123
|
-
const cache = loadScanCache(resolve(path));
|
|
124
|
-
if (cache && cache.findings) findings = cache.findings;
|
|
125
|
-
} catch {}
|
|
126
|
-
|
|
127
|
-
if (findings.length === 0) {
|
|
128
|
-
console.log('Scanning...');
|
|
129
|
-
const result = await check(path, { silent: true, full: true });
|
|
130
|
-
findings = result.findings;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Filter by severity
|
|
134
|
-
const sevFilter = severity.toLowerCase();
|
|
135
|
-
let filtered;
|
|
136
|
-
if (sevFilter === 'all') {
|
|
137
|
-
filtered = findings;
|
|
138
|
-
} else if (sevFilter === 'critical') {
|
|
139
|
-
filtered = findings.filter(f => f.severity === 'critical');
|
|
140
|
-
} else if (sevFilter === 'high') {
|
|
141
|
-
filtered = findings.filter(f => f.severity === 'critical' || f.severity === 'high');
|
|
142
|
-
} else if (sevFilter === 'medium') {
|
|
143
|
-
filtered = findings.filter(f => f.severity === 'critical' || f.severity === 'high' || f.severity === 'medium');
|
|
144
|
-
} else {
|
|
145
|
-
filtered = findings.filter(f => f.severity === sevFilter);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const top = filtered.slice(0, 20);
|
|
146
|
+
const files = await collectFiles(path, { silent: true });
|
|
147
|
+
const results = runSimpleChecks(files);
|
|
148
|
+
const failed = results.filter(r => !r.passed);
|
|
149
149
|
|
|
150
|
-
if (
|
|
151
|
-
console.log(
|
|
150
|
+
if (failed.length === 0) {
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(chalk.green.bold(' All clear. Nothing to fix.'));
|
|
153
|
+
console.log('');
|
|
152
154
|
return;
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
// Build the prompt
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
const issues = [];
|
|
159
|
+
for (const r of failed) {
|
|
160
|
+
for (const f of r.findings.slice(0, 5)) {
|
|
161
|
+
const loc = f.file ? ` in ${f.file}${f.line ? ':' + f.line : ''}` : '';
|
|
162
|
+
issues.push(`- ${r.name}: ${f.message}${loc}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const prompt = `Fix these issues in my code:\n\n${issues.join('\n')}`;
|
|
162
166
|
|
|
163
167
|
// Detect environment: AI tool or manual terminal
|
|
164
168
|
const isAI = process.env.CLAUDE_CODE || process.env.CURSOR_SESSION || process.env.CODEX_SANDBOX || process.env.TERM_PROGRAM === 'claude';
|
package/bin/getdoorman.js
CHANGED
|
@@ -41,8 +41,35 @@ program
|
|
|
41
41
|
.option('--save-baseline [path]', 'Save current findings as baseline for future diffs')
|
|
42
42
|
.option('--profile', 'Show performance profile of slowest rules')
|
|
43
43
|
.option('--share', 'Generate a shareable score card')
|
|
44
|
+
.option('--deep', 'Run full 2,500+ rule scan (advanced)')
|
|
44
45
|
.action(async (path, options) => {
|
|
45
46
|
try {
|
|
47
|
+
// Default: simple 10-check mode. --deep for full scan.
|
|
48
|
+
if (!options.deep && !options.json && !options.sarif && !options.html && !options.ci && !options.detail && !options.strict) {
|
|
49
|
+
const { collectFiles } = await import('../src/scanner.js');
|
|
50
|
+
const { runSimpleChecks, printSimpleReport } = await import('../src/simple-reporter.js');
|
|
51
|
+
const files = await collectFiles(path, { silent: true });
|
|
52
|
+
const results = runSimpleChecks(files);
|
|
53
|
+
printSimpleReport(results);
|
|
54
|
+
|
|
55
|
+
// Viral hooks on first scan
|
|
56
|
+
const { isFirstScan, installClaudeMd, installAgentsMd, installCursorRules, installClaudeHook } = await import('../src/hooks.js');
|
|
57
|
+
const { resolve } = await import('path');
|
|
58
|
+
const resolvedPath = resolve(path);
|
|
59
|
+
if (isFirstScan(resolvedPath)) {
|
|
60
|
+
const chalk = (await import('chalk')).default;
|
|
61
|
+
installClaudeMd(resolvedPath);
|
|
62
|
+
installAgentsMd(resolvedPath);
|
|
63
|
+
installCursorRules(resolvedPath);
|
|
64
|
+
installClaudeHook(resolvedPath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const failCount = results.filter(r => !r.passed).length;
|
|
68
|
+
process.exit(failCount > 0 ? 1 : 0);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Deep mode: full 2,500+ rule scan
|
|
46
73
|
const result = await check(path, options);
|
|
47
74
|
|
|
48
75
|
// Generate shareable score card
|
|
@@ -110,55 +137,32 @@ program
|
|
|
110
137
|
program
|
|
111
138
|
.command('fix')
|
|
112
139
|
.description('Tell your AI to fix issues — works in Claude, Codex, Cursor')
|
|
113
|
-
.argument('[severity]', 'Filter: critical, high, medium, or all', 'critical')
|
|
114
140
|
.argument('[path]', 'Path to scan', '.')
|
|
115
|
-
.action(async (
|
|
141
|
+
.action(async (path) => {
|
|
116
142
|
const chalk = (await import('chalk')).default;
|
|
143
|
+
const { collectFiles } = await import('../src/scanner.js');
|
|
144
|
+
const { runSimpleChecks } = await import('../src/simple-reporter.js');
|
|
117
145
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const { loadScanCache } = await import('../src/scan-cache.js');
|
|
122
|
-
const { resolve } = await import('path');
|
|
123
|
-
const cache = loadScanCache(resolve(path));
|
|
124
|
-
if (cache && cache.findings) findings = cache.findings;
|
|
125
|
-
} catch {}
|
|
126
|
-
|
|
127
|
-
if (findings.length === 0) {
|
|
128
|
-
console.log('Scanning...');
|
|
129
|
-
const result = await check(path, { silent: true, full: true });
|
|
130
|
-
findings = result.findings;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Filter by severity
|
|
134
|
-
const sevFilter = severity.toLowerCase();
|
|
135
|
-
let filtered;
|
|
136
|
-
if (sevFilter === 'all') {
|
|
137
|
-
filtered = findings;
|
|
138
|
-
} else if (sevFilter === 'critical') {
|
|
139
|
-
filtered = findings.filter(f => f.severity === 'critical');
|
|
140
|
-
} else if (sevFilter === 'high') {
|
|
141
|
-
filtered = findings.filter(f => f.severity === 'critical' || f.severity === 'high');
|
|
142
|
-
} else if (sevFilter === 'medium') {
|
|
143
|
-
filtered = findings.filter(f => f.severity === 'critical' || f.severity === 'high' || f.severity === 'medium');
|
|
144
|
-
} else {
|
|
145
|
-
filtered = findings.filter(f => f.severity === sevFilter);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const top = filtered.slice(0, 20);
|
|
146
|
+
const files = await collectFiles(path, { silent: true });
|
|
147
|
+
const results = runSimpleChecks(files);
|
|
148
|
+
const failed = results.filter(r => !r.passed);
|
|
149
149
|
|
|
150
|
-
if (
|
|
151
|
-
console.log(
|
|
150
|
+
if (failed.length === 0) {
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(chalk.green.bold(' All clear. Nothing to fix.'));
|
|
153
|
+
console.log('');
|
|
152
154
|
return;
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
// Build the prompt
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
const issues = [];
|
|
159
|
+
for (const r of failed) {
|
|
160
|
+
for (const f of r.findings.slice(0, 5)) {
|
|
161
|
+
const loc = f.file ? ` in ${f.file}${f.line ? ':' + f.line : ''}` : '';
|
|
162
|
+
issues.push(`- ${r.name}: ${f.message}${loc}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const prompt = `Fix these issues in my code:\n\n${issues.join('\n')}`;
|
|
162
166
|
|
|
163
167
|
// Detect environment: AI tool or manual terminal
|
|
164
168
|
const isAI = process.env.CLAUDE_CODE || process.env.CURSOR_SESSION || process.env.CODEX_SANDBOX || process.env.TERM_PROGRAM === 'claude';
|
package/package.json
CHANGED
|
@@ -290,9 +290,10 @@ const rules = [
|
|
|
290
290
|
for (let i = 0; i < lines.length; i++) {
|
|
291
291
|
const line = lines[i];
|
|
292
292
|
// JS: fs.openSync, createReadStream, createConnection without close
|
|
293
|
-
if (/(?:openSync|createReadStream|createWriteStream|
|
|
293
|
+
if (/(?:openSync|createReadStream|createWriteStream|createServer)\s*\(/.test(line)) {
|
|
294
|
+
// Skip createConnection — often a DB client that handles pooling internally
|
|
294
295
|
const rest = lines.slice(i, Math.min(lines.length, i + 30)).join('\n');
|
|
295
|
-
if (!rest.includes('.close') && !rest.includes('.end') && !rest.includes('.destroy') && !rest.includes('finally')) {
|
|
296
|
+
if (!rest.includes('.close') && !rest.includes('.end') && !rest.includes('.destroy') && !rest.includes('finally') && !rest.includes('pool') && !rest.includes('await')) {
|
|
296
297
|
findings.push({
|
|
297
298
|
ruleId: 'BUG-GEN-009', category: 'bugs', severity: 'high',
|
|
298
299
|
title: 'Resource opened but never closed — potential leak',
|
|
@@ -107,7 +107,7 @@ const rules = [
|
|
|
107
107
|
|
|
108
108
|
// COMP-HIPAA-011: PHI in client-side storage
|
|
109
109
|
{
|
|
110
|
-
id: 'COMP-HIPAA-011', category: 'compliance', severity: '
|
|
110
|
+
id: 'COMP-HIPAA-011', category: 'compliance', severity: 'high', confidence: 'definite',
|
|
111
111
|
title: 'PHI in Client-Side Storage',
|
|
112
112
|
check({ files }) {
|
|
113
113
|
const findings = [];
|
|
@@ -422,7 +422,7 @@ const rules = [
|
|
|
422
422
|
},
|
|
423
423
|
|
|
424
424
|
// COMP-FIN-001: Payment card data stored
|
|
425
|
-
{ id: 'COMP-FIN-001', category: 'compliance', severity: '
|
|
425
|
+
{ id: 'COMP-FIN-001', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Payment Card Data Stored (PCI DSS Violation)',
|
|
426
426
|
check({ files }) {
|
|
427
427
|
const findings = [];
|
|
428
428
|
for (const [fp, c] of files) {
|
|
@@ -440,7 +440,7 @@ const rules = [
|
|
|
440
440
|
},
|
|
441
441
|
|
|
442
442
|
// COMP-FIN-002: CVV stored after authorization
|
|
443
|
-
{ id: 'COMP-FIN-002', category: 'compliance', severity: '
|
|
443
|
+
{ id: 'COMP-FIN-002', category: 'compliance', severity: 'high', confidence: 'definite', title: 'CVV/CVC Stored After Authorization',
|
|
444
444
|
check({ files }) {
|
|
445
445
|
const findings = [];
|
|
446
446
|
for (const [fp, c] of files) {
|
|
@@ -652,7 +652,7 @@ const rules = [
|
|
|
652
652
|
},
|
|
653
653
|
|
|
654
654
|
// COMP-FIN-003: Payment page not on HTTPS
|
|
655
|
-
{ id: 'COMP-FIN-003', category: 'compliance', severity: '
|
|
655
|
+
{ id: 'COMP-FIN-003', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Payment Form Served Over HTTP',
|
|
656
656
|
check({ files }) {
|
|
657
657
|
const findings = [];
|
|
658
658
|
for (const [fp, c] of files) {
|
|
@@ -869,7 +869,7 @@ const rules = [
|
|
|
869
869
|
},
|
|
870
870
|
|
|
871
871
|
// COMP-PCI-001: Credit card number in logs
|
|
872
|
-
{ id: 'COMP-PCI-001', category: 'compliance', severity: '
|
|
872
|
+
{ id: 'COMP-PCI-001', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Credit Card Number Potentially Logged',
|
|
873
873
|
check({ files }) {
|
|
874
874
|
const findings = [];
|
|
875
875
|
for (const [fp, c] of files) {
|
|
@@ -886,7 +886,7 @@ const rules = [
|
|
|
886
886
|
},
|
|
887
887
|
|
|
888
888
|
// COMP-PCI-002: Direct Stripe/Braintree keys in client-side code
|
|
889
|
-
{ id: 'COMP-PCI-002', category: 'compliance', severity: '
|
|
889
|
+
{ id: 'COMP-PCI-002', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Payment Provider Secret Key in Client-Side Code',
|
|
890
890
|
check({ files }) {
|
|
891
891
|
const findings = [];
|
|
892
892
|
for (const [fp, c] of files) {
|
|
@@ -902,7 +902,7 @@ const rules = [
|
|
|
902
902
|
},
|
|
903
903
|
|
|
904
904
|
// COMP-PCI-003: No TLS on payment endpoints
|
|
905
|
-
{ id: 'COMP-PCI-003', category: 'compliance', severity: '
|
|
905
|
+
{ id: 'COMP-PCI-003', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Payment Endpoint Without TLS Enforcement',
|
|
906
906
|
check({ files }) {
|
|
907
907
|
const findings = [];
|
|
908
908
|
for (const [fp, c] of files) {
|
|
@@ -946,7 +946,7 @@ const rules = [
|
|
|
946
946
|
},
|
|
947
947
|
|
|
948
948
|
// COMP-HIPAA-001: Health data without encryption
|
|
949
|
-
{ id: 'COMP-HIPAA-001', category: 'compliance', severity: '
|
|
949
|
+
{ id: 'COMP-HIPAA-001', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Health Information Without Encryption (HIPAA)',
|
|
950
950
|
check({ files }) {
|
|
951
951
|
const findings = [];
|
|
952
952
|
for (const [fp, c] of files) {
|
|
@@ -962,7 +962,7 @@ const rules = [
|
|
|
962
962
|
},
|
|
963
963
|
|
|
964
964
|
// COMP-HIPAA-002: PHI logged without redaction
|
|
965
|
-
{ id: 'COMP-HIPAA-002', category: 'compliance', severity: '
|
|
965
|
+
{ id: 'COMP-HIPAA-002', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Protected Health Information in Logs',
|
|
966
966
|
check({ files }) {
|
|
967
967
|
const findings = [];
|
|
968
968
|
for (const [fp, c] of files) {
|
|
@@ -1267,7 +1267,7 @@ const rules = [
|
|
|
1267
1267
|
},
|
|
1268
1268
|
|
|
1269
1269
|
// COMP-CHILDREN-001: Behavioral advertising targeting children
|
|
1270
|
-
{ id: 'COMP-CHILDREN-001', category: 'compliance', severity: '
|
|
1270
|
+
{ id: 'COMP-CHILDREN-001', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Behavioral Advertising on Platform Serving Minors',
|
|
1271
1271
|
check({ files }) {
|
|
1272
1272
|
const findings = [];
|
|
1273
1273
|
const allCode = [...files.values()].join('\n');
|
|
@@ -1379,7 +1379,7 @@ const rules = [
|
|
|
1379
1379
|
},
|
|
1380
1380
|
|
|
1381
1381
|
// COMP-PCI-005: Card number pattern in source code
|
|
1382
|
-
{ id: 'COMP-PCI-005', category: 'compliance', severity: '
|
|
1382
|
+
{ id: 'COMP-PCI-005', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Payment Card Number Regex in Source',
|
|
1383
1383
|
check({ files }) {
|
|
1384
1384
|
const findings = [];
|
|
1385
1385
|
for (const [fp, c] of files) {
|
|
@@ -1499,7 +1499,7 @@ const rules = [
|
|
|
1499
1499
|
},
|
|
1500
1500
|
},
|
|
1501
1501
|
// COMP-HIPAA-005: PHI transmitted over HTTP
|
|
1502
|
-
{ id: 'COMP-HIPAA-005', category: 'compliance', severity: '
|
|
1502
|
+
{ id: 'COMP-HIPAA-005', category: 'compliance', severity: 'high', confidence: 'definite', title: 'Health Data Transmitted Over HTTP',
|
|
1503
1503
|
check({ files }) {
|
|
1504
1504
|
const findings = [];
|
|
1505
1505
|
for (const [fp, c] of files) {
|
|
@@ -1642,7 +1642,7 @@ const rules = [
|
|
|
1642
1642
|
},
|
|
1643
1643
|
},
|
|
1644
1644
|
// COMP-HIPAA-006: PHI in URL parameters
|
|
1645
|
-
{ id: 'COMP-HIPAA-006', category: 'compliance', severity: '
|
|
1645
|
+
{ id: 'COMP-HIPAA-006', category: 'compliance', severity: 'high', confidence: 'definite', title: 'PHI in URL Parameters',
|
|
1646
1646
|
check({ files }) {
|
|
1647
1647
|
const findings = [];
|
|
1648
1648
|
for (const [fp, c] of files) {
|
|
@@ -1658,7 +1658,7 @@ const rules = [
|
|
|
1658
1658
|
},
|
|
1659
1659
|
},
|
|
1660
1660
|
// COMP-PCI-007: Storing CVV after authorization
|
|
1661
|
-
{ id: 'COMP-PCI-007', category: 'compliance', severity: '
|
|
1661
|
+
{ id: 'COMP-PCI-007', category: 'compliance', severity: 'high', confidence: 'definite', title: 'CVV/CVV2 Value Stored in Database',
|
|
1662
1662
|
check({ files }) {
|
|
1663
1663
|
const findings = [];
|
|
1664
1664
|
for (const [fp, c] of files) {
|
|
@@ -1740,7 +1740,7 @@ const rules = [
|
|
|
1740
1740
|
},
|
|
1741
1741
|
},
|
|
1742
1742
|
// COMP-CHILDREN-002: Collecting children's location data — only for apps explicitly targeting children
|
|
1743
|
-
{ id: 'COMP-CHILDREN-002', category: 'compliance', severity: '
|
|
1743
|
+
{ id: 'COMP-CHILDREN-002', category: 'compliance', severity: 'high', confidence: 'definite', title: "Location Data Collected That May Apply to Children's Platforms",
|
|
1744
1744
|
check({ files }) {
|
|
1745
1745
|
const findings = [];
|
|
1746
1746
|
// Only fire if package.json or config explicitly mentions COPPA/children targeting
|
|
@@ -1780,7 +1780,7 @@ function isSourceFile(f) { return ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']
|
|
|
1780
1780
|
|
|
1781
1781
|
// COMP-016: HIPAA - PHI in log statements
|
|
1782
1782
|
rules.push({
|
|
1783
|
-
id: 'COMP-016', category: 'compliance', severity: '
|
|
1783
|
+
id: 'COMP-016', category: 'compliance', severity: 'high', confidence: 'definite', title: 'HIPAA: Protected Health Information (PHI) logged',
|
|
1784
1784
|
check({ files }) {
|
|
1785
1785
|
const findings = [];
|
|
1786
1786
|
const phiFields = /(?:diagnosis|condition|medication|treatment|prescription|patient.*name|dob|date_of_birth|ssn|medical_record|insurance|health_plan)/i;
|
|
@@ -1801,7 +1801,7 @@ rules.push({
|
|
|
1801
1801
|
|
|
1802
1802
|
// COMP-017: HIPAA - Unencrypted health data storage
|
|
1803
1803
|
rules.push({
|
|
1804
|
-
id: 'COMP-017', category: 'compliance', severity: '
|
|
1804
|
+
id: 'COMP-017', category: 'compliance', severity: 'high', confidence: 'definite', title: 'HIPAA: Health data field stored without encryption annotation',
|
|
1805
1805
|
check({ files }) {
|
|
1806
1806
|
const findings = [];
|
|
1807
1807
|
const phiField = /(?:diagnosis|condition|medication|prescription|health_data|medical_history|lab_result)\s*[:=]/i;
|
|
@@ -1822,7 +1822,7 @@ rules.push({
|
|
|
1822
1822
|
|
|
1823
1823
|
// COMP-018: PCI-DSS - Full card number stored
|
|
1824
1824
|
rules.push({
|
|
1825
|
-
id: 'COMP-018', category: 'compliance', severity: '
|
|
1825
|
+
id: 'COMP-018', category: 'compliance', severity: 'high', confidence: 'definite', title: 'PCI-DSS: Full credit card number stored or logged',
|
|
1826
1826
|
check({ files }) {
|
|
1827
1827
|
const findings = [];
|
|
1828
1828
|
const cardField = /(?:card_number|cardNumber|pan|credit_card|cc_number)\s*[:=]/i;
|
|
@@ -1843,7 +1843,7 @@ rules.push({
|
|
|
1843
1843
|
|
|
1844
1844
|
// COMP-019: PCI-DSS - CVV stored
|
|
1845
1845
|
rules.push({
|
|
1846
|
-
id: 'COMP-019', category: 'compliance', severity: '
|
|
1846
|
+
id: 'COMP-019', category: 'compliance', severity: 'high', confidence: 'definite', title: 'PCI-DSS: CVV/CVV2 security code stored',
|
|
1847
1847
|
check({ files }) {
|
|
1848
1848
|
const findings = [];
|
|
1849
1849
|
const cvvField = /(?:cvv|cvv2|cvc|security_code|card_verification)\s*[:=]/i;
|
|
@@ -1863,7 +1863,7 @@ rules.push({
|
|
|
1863
1863
|
|
|
1864
1864
|
// COMP-020: PCI-DSS - Unmasked PAN in logs
|
|
1865
1865
|
rules.push({
|
|
1866
|
-
id: 'COMP-020', category: 'compliance', severity: '
|
|
1866
|
+
id: 'COMP-020', category: 'compliance', severity: 'high', confidence: 'definite', title: 'PCI-DSS: Card number or PAN in log output',
|
|
1867
1867
|
check({ files }) {
|
|
1868
1868
|
const findings = [];
|
|
1869
1869
|
const pattern = /(?:console\.|logger\.|log\.)\w*\s*\([^)]*(?:cardNumber|card_number|pan|creditCard|credit_card)/i;
|
|
@@ -2076,7 +2076,7 @@ rules.push({
|
|
|
2076
2076
|
|
|
2077
2077
|
// COMP-032: PCI-DSS - Unencrypted transmission
|
|
2078
2078
|
rules.push({
|
|
2079
|
-
id: 'COMP-032', category: 'compliance', severity: '
|
|
2079
|
+
id: 'COMP-032', category: 'compliance', severity: 'high', confidence: 'definite', title: 'PCI-DSS: Payment data transmitted over unencrypted connection',
|
|
2080
2080
|
check({ files }) {
|
|
2081
2081
|
const findings = [];
|
|
2082
2082
|
for (const [fp, c] of files) {
|
|
@@ -2145,7 +2145,7 @@ rules.push({
|
|
|
2145
2145
|
|
|
2146
2146
|
// COMP-036: SOC2 - Plaintext passwords in configuration
|
|
2147
2147
|
rules.push({
|
|
2148
|
-
id: 'COMP-036', category: 'compliance', severity: '
|
|
2148
|
+
id: 'COMP-036', category: 'compliance', severity: 'high', confidence: 'definite', title: 'SOC2: Plaintext credentials in configuration files',
|
|
2149
2149
|
check({ files }) {
|
|
2150
2150
|
const findings = [];
|
|
2151
2151
|
for (const [fp, c] of files) {
|
|
@@ -2199,7 +2199,7 @@ rules.push({
|
|
|
2199
2199
|
|
|
2200
2200
|
// COMP-039: PCI-DSS - Logging card data
|
|
2201
2201
|
rules.push({
|
|
2202
|
-
id: 'COMP-039', category: 'compliance', severity: '
|
|
2202
|
+
id: 'COMP-039', category: 'compliance', severity: 'high', confidence: 'definite', title: 'PCI-DSS: Full card data logged — compliance violation',
|
|
2203
2203
|
check({ files }) {
|
|
2204
2204
|
const findings = [];
|
|
2205
2205
|
const p = /(?:console\.|logger\.)\w*\s*\([^)]*(?:card|pan|cvv|expiry|expirationDate)/i;
|
|
@@ -2285,7 +2285,7 @@ rules.push({
|
|
|
2285
2285
|
|
|
2286
2286
|
// COMP-044: HIPAA - PHI sent via email without encryption
|
|
2287
2287
|
rules.push({
|
|
2288
|
-
id: 'COMP-044', category: 'compliance', severity: '
|
|
2288
|
+
id: 'COMP-044', category: 'compliance', severity: 'high', confidence: 'definite', title: 'HIPAA: Health data potentially sent via email without encryption',
|
|
2289
2289
|
check({ files }) {
|
|
2290
2290
|
const findings = [];
|
|
2291
2291
|
for (const [fp, c] of files) {
|
|
@@ -2598,7 +2598,7 @@ rules.push({
|
|
|
2598
2598
|
|
|
2599
2599
|
// COMP-064: HIPAA - PHI in debug logs
|
|
2600
2600
|
rules.push({
|
|
2601
|
-
id: 'COMP-064', category: 'compliance', severity: '
|
|
2601
|
+
id: 'COMP-064', category: 'compliance', severity: 'high', confidence: 'definite', title: 'HIPAA: PHI fields may appear in debug logs',
|
|
2602
2602
|
check({ files }) {
|
|
2603
2603
|
const findings = [];
|
|
2604
2604
|
for (const [fp, c] of files) {
|