getdoorman 1.0.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/LICENSE +21 -0
- package/README.md +181 -0
- package/bin/doorman.js +444 -0
- package/package.json +74 -0
- package/src/ai-fixer.js +559 -0
- package/src/ast-scanner.js +434 -0
- package/src/auth.js +149 -0
- package/src/baseline.js +48 -0
- package/src/compliance.js +539 -0
- package/src/config.js +466 -0
- package/src/custom-rules.js +32 -0
- package/src/dashboard.js +202 -0
- package/src/detector.js +142 -0
- package/src/fix-engine.js +48 -0
- package/src/fix-registry-extra.js +95 -0
- package/src/fix-registry-go-rust.js +77 -0
- package/src/fix-registry-java-csharp.js +77 -0
- package/src/fix-registry-js.js +99 -0
- package/src/fix-registry-mcp-ai.js +57 -0
- package/src/fix-registry-python.js +87 -0
- package/src/fixer-ruby-php.js +608 -0
- package/src/fixer.js +2113 -0
- package/src/hooks.js +115 -0
- package/src/ignore.js +176 -0
- package/src/index.js +384 -0
- package/src/metrics.js +126 -0
- package/src/monorepo.js +65 -0
- package/src/presets.js +54 -0
- package/src/reporter.js +975 -0
- package/src/rule-worker.js +36 -0
- package/src/rules/ast-rules.js +756 -0
- package/src/rules/bugs/accessibility.js +235 -0
- package/src/rules/bugs/ai-codegen-fixable.js +172 -0
- package/src/rules/bugs/ai-codegen.js +365 -0
- package/src/rules/bugs/code-smell-bugs.js +247 -0
- package/src/rules/bugs/crypto-bugs.js +195 -0
- package/src/rules/bugs/docker-bugs.js +158 -0
- package/src/rules/bugs/general.js +361 -0
- package/src/rules/bugs/go-bugs.js +279 -0
- package/src/rules/bugs/index.js +73 -0
- package/src/rules/bugs/js-api.js +257 -0
- package/src/rules/bugs/js-array-object.js +210 -0
- package/src/rules/bugs/js-async-fixable.js +223 -0
- package/src/rules/bugs/js-async.js +211 -0
- package/src/rules/bugs/js-closure-scope.js +182 -0
- package/src/rules/bugs/js-database.js +203 -0
- package/src/rules/bugs/js-error-handling.js +148 -0
- package/src/rules/bugs/js-logic.js +261 -0
- package/src/rules/bugs/js-memory.js +214 -0
- package/src/rules/bugs/js-node.js +361 -0
- package/src/rules/bugs/js-react.js +373 -0
- package/src/rules/bugs/js-regex.js +200 -0
- package/src/rules/bugs/js-state.js +272 -0
- package/src/rules/bugs/js-type-coercion.js +318 -0
- package/src/rules/bugs/nextjs-bugs.js +242 -0
- package/src/rules/bugs/nextjs-fixable.js +120 -0
- package/src/rules/bugs/node-fixable.js +178 -0
- package/src/rules/bugs/python-advanced.js +245 -0
- package/src/rules/bugs/python-fixable.js +98 -0
- package/src/rules/bugs/python.js +284 -0
- package/src/rules/bugs/react-fixable.js +207 -0
- package/src/rules/bugs/ruby-bugs.js +182 -0
- package/src/rules/bugs/shell-bugs.js +181 -0
- package/src/rules/bugs/silent-failures.js +261 -0
- package/src/rules/bugs/ts-bugs.js +235 -0
- package/src/rules/bugs/unused-vars.js +65 -0
- package/src/rules/compliance/accessibility-ext.js +468 -0
- package/src/rules/compliance/education.js +322 -0
- package/src/rules/compliance/financial.js +421 -0
- package/src/rules/compliance/frameworks.js +507 -0
- package/src/rules/compliance/healthcare.js +520 -0
- package/src/rules/compliance/index.js +2714 -0
- package/src/rules/compliance/regional-eu.js +480 -0
- package/src/rules/compliance/regional-international.js +903 -0
- package/src/rules/cost/index.js +1993 -0
- package/src/rules/data/index.js +2503 -0
- package/src/rules/dependencies/index.js +1684 -0
- package/src/rules/deployment/index.js +2050 -0
- package/src/rules/index.js +71 -0
- package/src/rules/infrastructure/index.js +3048 -0
- package/src/rules/performance/index.js +3455 -0
- package/src/rules/quality/index.js +3175 -0
- package/src/rules/reliability/index.js +3040 -0
- package/src/rules/scope-rules.js +815 -0
- package/src/rules/security/ai-api.js +1177 -0
- package/src/rules/security/auth.js +1328 -0
- package/src/rules/security/cors.js +127 -0
- package/src/rules/security/crypto.js +527 -0
- package/src/rules/security/csharp.js +862 -0
- package/src/rules/security/csrf.js +193 -0
- package/src/rules/security/dart.js +835 -0
- package/src/rules/security/deserialization.js +291 -0
- package/src/rules/security/file-upload.js +187 -0
- package/src/rules/security/go.js +850 -0
- package/src/rules/security/headers.js +235 -0
- package/src/rules/security/index.js +65 -0
- package/src/rules/security/injection.js +1639 -0
- package/src/rules/security/mcp-server.js +71 -0
- package/src/rules/security/misconfiguration.js +660 -0
- package/src/rules/security/oauth-jwt.js +329 -0
- package/src/rules/security/path-traversal.js +295 -0
- package/src/rules/security/php.js +1054 -0
- package/src/rules/security/prototype-pollution.js +283 -0
- package/src/rules/security/rate-limiting.js +208 -0
- package/src/rules/security/ruby.js +1061 -0
- package/src/rules/security/rust.js +693 -0
- package/src/rules/security/secrets.js +747 -0
- package/src/rules/security/shell.js +647 -0
- package/src/rules/security/ssrf.js +298 -0
- package/src/rules/security/supply-chain-advanced.js +393 -0
- package/src/rules/security/supply-chain.js +734 -0
- package/src/rules/security/swift.js +835 -0
- package/src/rules/security/taint.js +27 -0
- package/src/rules/security/xss.js +520 -0
- package/src/scan-cache.js +71 -0
- package/src/scanner.js +710 -0
- package/src/scope-analyzer.js +685 -0
- package/src/share.js +88 -0
- package/src/taint.js +300 -0
- package/src/telemetry.js +183 -0
- package/src/tracer.js +190 -0
- package/src/upload.js +35 -0
- package/src/worker.js +31 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const JS_EXT = ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'];
|
|
2
|
+
const isJS = (f) => JS_EXT.some(ext => f.endsWith(ext));
|
|
3
|
+
const isTest = (f) => f.includes('test') || f.includes('spec') || f.includes('mock') || f.includes('fixture');
|
|
4
|
+
|
|
5
|
+
const rules = [
|
|
6
|
+
// SEC-CSRF-001
|
|
7
|
+
{
|
|
8
|
+
id: 'SEC-CSRF-001', category: 'security', severity: 'high', confidence: 'likely',
|
|
9
|
+
title: 'Missing CSRF protection',
|
|
10
|
+
check({ files, stack }) {
|
|
11
|
+
const findings = [];
|
|
12
|
+
if (stack.framework === 'nextjs') return findings; // Next.js server actions have built-in CSRF
|
|
13
|
+
if (stack.runtime !== 'node') return findings;
|
|
14
|
+
|
|
15
|
+
const hasCsrf = Object.keys(stack.dependencies || {}).some(d =>
|
|
16
|
+
['csurf', 'csrf', 'csrf-csrf', 'lusca'].includes(d)
|
|
17
|
+
);
|
|
18
|
+
if (hasCsrf) return findings;
|
|
19
|
+
|
|
20
|
+
const hasForms = [...files.values()].some(c =>
|
|
21
|
+
c.includes('<form') && (c.includes('method="POST"') || c.includes("method='POST'") || c.includes('method="post"'))
|
|
22
|
+
);
|
|
23
|
+
if (hasForms) {
|
|
24
|
+
findings.push({
|
|
25
|
+
ruleId: 'SEC-CSRF-001', category: 'security', severity: 'high',
|
|
26
|
+
title: 'No CSRF protection on forms with POST method',
|
|
27
|
+
description: 'Forms without CSRF tokens can be exploited by attackers to submit requests on behalf of users.',
|
|
28
|
+
fix: null,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return findings;
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// SEC-CSRF-002
|
|
36
|
+
{
|
|
37
|
+
id: 'SEC-CSRF-002', category: 'security', severity: 'critical', confidence: 'definite',
|
|
38
|
+
title: 'CSRF token not validated on server',
|
|
39
|
+
check({ files }) {
|
|
40
|
+
const findings = [];
|
|
41
|
+
for (const [fp, content] of files) {
|
|
42
|
+
if (!isJS(fp) || isTest(fp)) continue;
|
|
43
|
+
if (content.includes('csrf') && content.includes('token')) {
|
|
44
|
+
// Token is referenced but no middleware validates it
|
|
45
|
+
if (!content.includes('csrfProtection') && !content.includes('verifyCsrf') && !content.includes('csrf()')) {
|
|
46
|
+
// Check if it's just being set but never verified
|
|
47
|
+
if (content.includes('csrfToken') && !content.includes('validate') && !content.includes('verify')) {
|
|
48
|
+
findings.push({
|
|
49
|
+
ruleId: 'SEC-CSRF-002', category: 'security', severity: 'critical',
|
|
50
|
+
title: 'CSRF token generated but may not be validated server-side',
|
|
51
|
+
file: fp, fix: null,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return findings;
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// SEC-CSRF-003
|
|
62
|
+
{
|
|
63
|
+
id: 'SEC-CSRF-003', category: 'security', severity: 'high', confidence: 'likely',
|
|
64
|
+
title: 'Cookie without SameSite attribute',
|
|
65
|
+
check({ files }) {
|
|
66
|
+
const findings = [];
|
|
67
|
+
for (const [fp, content] of files) {
|
|
68
|
+
if (!isJS(fp) || isTest(fp)) continue;
|
|
69
|
+
const lines = content.split('\n');
|
|
70
|
+
for (let i = 0; i < lines.length; i++) {
|
|
71
|
+
if (lines[i].match(/(?:cookie|session).*(?:httpOnly|secure|maxAge)/i) && !lines[i].includes('sameSite') && !content.includes('sameSite')) {
|
|
72
|
+
findings.push({
|
|
73
|
+
ruleId: 'SEC-CSRF-003', category: 'security', severity: 'high',
|
|
74
|
+
title: 'Cookie configured without SameSite attribute',
|
|
75
|
+
description: 'Set SameSite=Lax or SameSite=Strict to prevent CSRF attacks.',
|
|
76
|
+
file: fp, line: i + 1, fix: null,
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return findings;
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// SEC-CSRF-004
|
|
87
|
+
{
|
|
88
|
+
id: 'SEC-CSRF-004', category: 'security', severity: 'high', confidence: 'likely',
|
|
89
|
+
title: 'Cookie without Secure flag',
|
|
90
|
+
check({ files }) {
|
|
91
|
+
const findings = [];
|
|
92
|
+
for (const [fp, content] of files) {
|
|
93
|
+
if (!isJS(fp) || isTest(fp)) continue;
|
|
94
|
+
if (content.match(/cookie.*httpOnly/i) || content.match(/session.*cookie/i)) {
|
|
95
|
+
if (content.includes('secure: false') || (!content.includes('secure:') && !content.includes('secure :'))) {
|
|
96
|
+
findings.push({
|
|
97
|
+
ruleId: 'SEC-CSRF-004', category: 'security', severity: 'high',
|
|
98
|
+
title: 'Session cookie missing Secure flag — will be sent over HTTP',
|
|
99
|
+
file: fp, fix: null,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return findings;
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// SEC-CSRF-005
|
|
109
|
+
{
|
|
110
|
+
id: 'SEC-CSRF-005', category: 'security', severity: 'high', confidence: 'likely',
|
|
111
|
+
title: 'Cookie without HttpOnly flag',
|
|
112
|
+
check({ files }) {
|
|
113
|
+
const findings = [];
|
|
114
|
+
for (const [fp, content] of files) {
|
|
115
|
+
if (!isJS(fp) || isTest(fp)) continue;
|
|
116
|
+
if (content.match(/(?:res\.cookie|setCookie|set-cookie)/i)) {
|
|
117
|
+
if (content.includes('httpOnly: false')) {
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
for (let i = 0; i < lines.length; i++) {
|
|
120
|
+
if (lines[i].includes('httpOnly: false')) {
|
|
121
|
+
findings.push({
|
|
122
|
+
ruleId: 'SEC-CSRF-005', category: 'security', severity: 'high',
|
|
123
|
+
title: 'Cookie with httpOnly explicitly set to false — accessible to JavaScript',
|
|
124
|
+
file: fp, line: i + 1, fix: null,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return findings;
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// SEC-CSRF-006
|
|
136
|
+
{
|
|
137
|
+
id: 'SEC-CSRF-006', category: 'security', severity: 'medium', confidence: 'likely',
|
|
138
|
+
title: 'State-changing GET request',
|
|
139
|
+
check({ files }) {
|
|
140
|
+
const findings = [];
|
|
141
|
+
for (const [fp, content] of files) {
|
|
142
|
+
if (!isJS(fp) || isTest(fp)) continue;
|
|
143
|
+
const lines = content.split('\n');
|
|
144
|
+
for (let i = 0; i < lines.length; i++) {
|
|
145
|
+
// Flag GET routes where the inline handler body performs a state-changing DB operation.
|
|
146
|
+
// Check the route line + next 8 lines for actual mutations (deleteOne, destroy, UPDATE, etc.)
|
|
147
|
+
if (lines[i].match(/\.get\s*\(.*['"`]\/[^'"` ]+['"`]/i) &&
|
|
148
|
+
!lines[i].trim().startsWith('//')) {
|
|
149
|
+
const window = lines.slice(i, i + 9).join('\n');
|
|
150
|
+
const stateChange = /\.(?:deleteOne|deleteMany|findByIdAndDelete|destroy|remove|drop)\s*\(|db\.(?:query|run)\s*\(\s*['"`]\s*(?:DELETE|UPDATE|INSERT)/i;
|
|
151
|
+
if (stateChange.test(window)) {
|
|
152
|
+
findings.push({
|
|
153
|
+
ruleId: 'SEC-CSRF-006', category: 'security', severity: 'medium',
|
|
154
|
+
title: 'State-changing operation on GET endpoint — should be POST/PUT/DELETE',
|
|
155
|
+
file: fp, line: i + 1, fix: null,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return findings;
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// SEC-CSRF-007
|
|
166
|
+
{
|
|
167
|
+
id: 'SEC-CSRF-007', category: 'security', severity: 'medium', confidence: 'likely',
|
|
168
|
+
title: 'Missing Origin/Referer validation',
|
|
169
|
+
check({ files, stack }) {
|
|
170
|
+
const findings = [];
|
|
171
|
+
if (stack.runtime !== 'node') return findings;
|
|
172
|
+
const hasOriginCheck = [...files.values()].some(c =>
|
|
173
|
+
c.includes('origin') && (c.includes('req.headers') || c.includes('request.headers'))
|
|
174
|
+
);
|
|
175
|
+
const hasCors = Object.keys(stack.dependencies || {}).some(d => d === 'cors');
|
|
176
|
+
if (!hasOriginCheck && !hasCors) {
|
|
177
|
+
const hasMutations = [...files.values()].some(c =>
|
|
178
|
+
c.match(/\.(post|put|patch|delete)\s*\(/)
|
|
179
|
+
);
|
|
180
|
+
if (hasMutations) {
|
|
181
|
+
findings.push({
|
|
182
|
+
ruleId: 'SEC-CSRF-007', category: 'security', severity: 'medium',
|
|
183
|
+
title: 'No Origin or Referer header validation on state-changing endpoints',
|
|
184
|
+
fix: null,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return findings;
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
export default rules;
|