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,862 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# Security Rules (SEC-CS-001 through SEC-CS-050)
|
|
3
|
+
*
|
|
4
|
+
* Each rule scans C# source files for security vulnerabilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const isCS = (f) => f.endsWith('.cs');
|
|
8
|
+
|
|
9
|
+
const SKIP_PATH = /[/\\](test|tests|__tests__|__mocks__|mocks|fixtures|__fixtures__|spec|__snapshots__|node_modules|vendor|dist|build|bin|obj)[/\\]/i;
|
|
10
|
+
const COMMENT_LINE = /^\s*(\/\/|\/\*|\*)/;
|
|
11
|
+
|
|
12
|
+
function scanLines(content, regex, file, rule) {
|
|
13
|
+
const findings = [];
|
|
14
|
+
const lines = content.split('\n');
|
|
15
|
+
for (let i = 0; i < lines.length; i++) {
|
|
16
|
+
const line = lines[i];
|
|
17
|
+
if (COMMENT_LINE.test(line)) continue;
|
|
18
|
+
if (regex.test(line)) {
|
|
19
|
+
findings.push({
|
|
20
|
+
ruleId: rule.id,
|
|
21
|
+
category: rule.category,
|
|
22
|
+
severity: rule.severity,
|
|
23
|
+
title: rule.title,
|
|
24
|
+
description: rule.description,
|
|
25
|
+
confidence: rule.confidence,
|
|
26
|
+
file,
|
|
27
|
+
line: i + 1,
|
|
28
|
+
fix: rule.fix || null,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return findings;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function checkCS(rule, files, ...patterns) {
|
|
36
|
+
const findings = [];
|
|
37
|
+
for (const [path, content] of files) {
|
|
38
|
+
if (SKIP_PATH.test(path)) continue;
|
|
39
|
+
if (isCS(path)) {
|
|
40
|
+
for (const p of patterns) {
|
|
41
|
+
findings.push(...scanLines(content, p, path, rule));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return findings;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const rules = [
|
|
49
|
+
// SEC-CS-001: SQL injection via string concatenation in SqlCommand
|
|
50
|
+
{
|
|
51
|
+
id: 'SEC-CS-001',
|
|
52
|
+
category: 'security',
|
|
53
|
+
severity: 'critical',
|
|
54
|
+
confidence: 'likely',
|
|
55
|
+
title: 'SQL Injection via String Concatenation in SqlCommand',
|
|
56
|
+
description: 'Building SQL queries with string concatenation in SqlCommand allows SQL injection.',
|
|
57
|
+
fix: { suggestion: 'Use parameterized queries with SqlParameter instead of string concatenation.' },
|
|
58
|
+
check({ files }) {
|
|
59
|
+
return checkCS(this, files,
|
|
60
|
+
/new\s+SqlCommand\s*\(\s*["'][^"']*["']\s*\+/,
|
|
61
|
+
/new\s+SqlCommand\s*\(\s*\$"/,
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// SEC-CS-002: SQL injection via string interpolation
|
|
67
|
+
{
|
|
68
|
+
id: 'SEC-CS-002',
|
|
69
|
+
category: 'security',
|
|
70
|
+
severity: 'critical',
|
|
71
|
+
confidence: 'likely',
|
|
72
|
+
title: 'SQL Injection via String Interpolation',
|
|
73
|
+
description: 'Using string interpolation ($"...") in SQL commands allows injection.',
|
|
74
|
+
fix: { suggestion: 'Use parameterized queries with @param placeholders and cmd.Parameters.Add().' },
|
|
75
|
+
check({ files }) {
|
|
76
|
+
return checkCS(this, files,
|
|
77
|
+
/CommandText\s*=\s*\$"/,
|
|
78
|
+
/\.ExecuteSqlRaw\s*\(\s*\$"/,
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// SEC-CS-003: SQL injection via string.Format
|
|
84
|
+
{
|
|
85
|
+
id: 'SEC-CS-003',
|
|
86
|
+
category: 'security',
|
|
87
|
+
severity: 'critical',
|
|
88
|
+
confidence: 'likely',
|
|
89
|
+
title: 'SQL Injection via String.Format',
|
|
90
|
+
description: 'Using String.Format to build SQL queries enables injection attacks.',
|
|
91
|
+
fix: { suggestion: 'Use parameterized queries instead of String.Format for SQL.' },
|
|
92
|
+
check({ files }) {
|
|
93
|
+
return checkCS(this, files,
|
|
94
|
+
/(?:SqlCommand|CommandText|ExecuteSql).*string\.Format\s*\(/i,
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// SEC-CS-004: XSS via Html.Raw
|
|
100
|
+
{
|
|
101
|
+
id: 'SEC-CS-004',
|
|
102
|
+
category: 'security',
|
|
103
|
+
severity: 'high',
|
|
104
|
+
confidence: 'likely',
|
|
105
|
+
title: 'XSS via Html.Raw()',
|
|
106
|
+
description: 'Using Html.Raw() with user-controlled data disables HTML encoding, enabling XSS.',
|
|
107
|
+
fix: { suggestion: 'Avoid Html.Raw with user input. Use @Html.Encode or Razor auto-encoding.' },
|
|
108
|
+
check({ files }) {
|
|
109
|
+
return checkCS(this, files,
|
|
110
|
+
/Html\.Raw\s*\(/,
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// SEC-CS-005: XSS via Response.Write
|
|
116
|
+
{
|
|
117
|
+
id: 'SEC-CS-005',
|
|
118
|
+
category: 'security',
|
|
119
|
+
severity: 'high',
|
|
120
|
+
confidence: 'likely',
|
|
121
|
+
title: 'XSS via Response.Write',
|
|
122
|
+
description: 'Writing user input directly to Response.Write without encoding enables XSS.',
|
|
123
|
+
fix: { suggestion: 'Use HttpUtility.HtmlEncode before writing to the response.' },
|
|
124
|
+
check({ files }) {
|
|
125
|
+
return checkCS(this, files,
|
|
126
|
+
/Response\.Write\s*\(\s*(?:Request|input|user|param)/,
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// SEC-CS-006: Insecure deserialization - BinaryFormatter
|
|
132
|
+
{
|
|
133
|
+
id: 'SEC-CS-006',
|
|
134
|
+
category: 'security',
|
|
135
|
+
severity: 'critical',
|
|
136
|
+
confidence: 'definite',
|
|
137
|
+
title: 'Insecure Deserialization: BinaryFormatter',
|
|
138
|
+
description: 'BinaryFormatter is inherently insecure and can execute arbitrary code during deserialization.',
|
|
139
|
+
fix: { suggestion: 'Use System.Text.Json or JsonSerializer. BinaryFormatter is obsolete in .NET 5+.' },
|
|
140
|
+
check({ files }) {
|
|
141
|
+
return checkCS(this, files,
|
|
142
|
+
/new\s+BinaryFormatter\s*\(\s*\)/,
|
|
143
|
+
/BinaryFormatter\s+\w+\s*=\s*new/,
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
// SEC-CS-007: Insecure deserialization - JsonConvert without type handling
|
|
149
|
+
{
|
|
150
|
+
id: 'SEC-CS-007',
|
|
151
|
+
category: 'security',
|
|
152
|
+
severity: 'high',
|
|
153
|
+
confidence: 'likely',
|
|
154
|
+
title: 'Insecure Deserialization: TypeNameHandling',
|
|
155
|
+
description: 'JsonConvert with TypeNameHandling.All or Auto allows type injection attacks.',
|
|
156
|
+
fix: { suggestion: 'Avoid TypeNameHandling.All/Auto. Use TypeNameHandling.None (default) or a custom SerializationBinder.' },
|
|
157
|
+
check({ files }) {
|
|
158
|
+
return checkCS(this, files,
|
|
159
|
+
/TypeNameHandling\s*=\s*TypeNameHandling\.(?:All|Auto|Objects|Arrays)/,
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// SEC-CS-008: Insecure deserialization - JavaScriptSerializer
|
|
165
|
+
{
|
|
166
|
+
id: 'SEC-CS-008',
|
|
167
|
+
category: 'security',
|
|
168
|
+
severity: 'high',
|
|
169
|
+
confidence: 'likely',
|
|
170
|
+
title: 'Insecure Deserialization: JavaScriptSerializer',
|
|
171
|
+
description: 'JavaScriptSerializer with type resolution can be exploited for remote code execution.',
|
|
172
|
+
fix: { suggestion: 'Use System.Text.Json or Newtonsoft.Json with safe settings.' },
|
|
173
|
+
check({ files }) {
|
|
174
|
+
return checkCS(this, files,
|
|
175
|
+
/new\s+JavaScriptSerializer\s*\(\s*new\s+SimpleTypeResolver/,
|
|
176
|
+
);
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// SEC-CS-009: Command injection via Process.Start
|
|
181
|
+
{
|
|
182
|
+
id: 'SEC-CS-009',
|
|
183
|
+
category: 'security',
|
|
184
|
+
severity: 'critical',
|
|
185
|
+
confidence: 'likely',
|
|
186
|
+
title: 'Command Injection via Process.Start',
|
|
187
|
+
description: 'Passing user-controlled data to Process.Start enables arbitrary command execution.',
|
|
188
|
+
fix: { suggestion: 'Validate and sanitize input. Avoid UseShellExecute = true with user input.' },
|
|
189
|
+
check({ files }) {
|
|
190
|
+
return checkCS(this, files,
|
|
191
|
+
/Process\.Start\s*\(\s*(?:request|input|user|param|Request)/,
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// SEC-CS-010: Command injection via ProcessStartInfo
|
|
197
|
+
{
|
|
198
|
+
id: 'SEC-CS-010',
|
|
199
|
+
category: 'security',
|
|
200
|
+
severity: 'critical',
|
|
201
|
+
confidence: 'likely',
|
|
202
|
+
title: 'Command Injection via ProcessStartInfo',
|
|
203
|
+
description: 'Setting ProcessStartInfo.Arguments with user input enables command injection.',
|
|
204
|
+
fix: { suggestion: 'Validate and sanitize arguments. Set UseShellExecute = false.' },
|
|
205
|
+
check({ files }) {
|
|
206
|
+
return checkCS(this, files,
|
|
207
|
+
/Arguments\s*=\s*(?:\$"|.*\+\s*(?:request|input|user|param|Request))/,
|
|
208
|
+
);
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// SEC-CS-011: Path traversal via Path.Combine
|
|
213
|
+
{
|
|
214
|
+
id: 'SEC-CS-011',
|
|
215
|
+
category: 'security',
|
|
216
|
+
severity: 'high',
|
|
217
|
+
confidence: 'likely',
|
|
218
|
+
title: 'Path Traversal via Path.Combine',
|
|
219
|
+
description: 'Path.Combine with user input can be exploited if the second argument is an absolute path.',
|
|
220
|
+
fix: { suggestion: 'Use Path.GetFullPath and verify the result starts with the expected base directory.' },
|
|
221
|
+
check({ files }) {
|
|
222
|
+
return checkCS(this, files,
|
|
223
|
+
/Path\.Combine\s*\(\s*\w+\s*,\s*(?:request|input|user|param|Request)/i,
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
// SEC-CS-012: Path traversal via File operations
|
|
229
|
+
{
|
|
230
|
+
id: 'SEC-CS-012',
|
|
231
|
+
category: 'security',
|
|
232
|
+
severity: 'high',
|
|
233
|
+
confidence: 'likely',
|
|
234
|
+
title: 'Path Traversal via File Operations',
|
|
235
|
+
description: 'File.ReadAllText, File.Open, etc. with user-controlled paths allow reading arbitrary files.',
|
|
236
|
+
fix: { suggestion: 'Validate and canonicalize paths. Ensure they remain within the expected directory.' },
|
|
237
|
+
check({ files }) {
|
|
238
|
+
return checkCS(this, files,
|
|
239
|
+
/File\.(?:ReadAll|WriteAll|Open|Delete|Copy|Move)\w*\s*\(\s*(?:request|input|user|param|Request)/i,
|
|
240
|
+
);
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// SEC-CS-013: LDAP injection
|
|
245
|
+
{
|
|
246
|
+
id: 'SEC-CS-013',
|
|
247
|
+
category: 'security',
|
|
248
|
+
severity: 'critical',
|
|
249
|
+
confidence: 'likely',
|
|
250
|
+
title: 'LDAP Injection',
|
|
251
|
+
description: 'Building LDAP filters with string concatenation allows injection attacks.',
|
|
252
|
+
fix: { suggestion: 'Use parameterized LDAP queries or sanitize input with LDAP encoding.' },
|
|
253
|
+
check({ files }) {
|
|
254
|
+
return checkCS(this, files,
|
|
255
|
+
/DirectorySearcher.*Filter\s*=\s*.*\+\s*(?:request|input|user|param|Request)/i,
|
|
256
|
+
/new\s+DirectorySearcher\s*\(\s*["'].*["']\s*\+/,
|
|
257
|
+
);
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// SEC-CS-014: LDAP injection via string interpolation
|
|
262
|
+
{
|
|
263
|
+
id: 'SEC-CS-014',
|
|
264
|
+
category: 'security',
|
|
265
|
+
severity: 'critical',
|
|
266
|
+
confidence: 'likely',
|
|
267
|
+
title: 'LDAP Injection via String Interpolation',
|
|
268
|
+
description: 'Using string interpolation in LDAP filters enables injection.',
|
|
269
|
+
fix: { suggestion: 'Sanitize input using LDAP encoding before including in filters.' },
|
|
270
|
+
check({ files }) {
|
|
271
|
+
return checkCS(this, files,
|
|
272
|
+
/Filter\s*=\s*\$".*\{/,
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
// SEC-CS-015: XXE via XmlDocument
|
|
278
|
+
{
|
|
279
|
+
id: 'SEC-CS-015',
|
|
280
|
+
category: 'security',
|
|
281
|
+
severity: 'critical',
|
|
282
|
+
confidence: 'likely',
|
|
283
|
+
title: 'XXE: XmlDocument Without Safe Settings',
|
|
284
|
+
description: 'XmlDocument processes external entities by default, enabling XXE attacks.',
|
|
285
|
+
fix: { suggestion: 'Set XmlResolver = null on XmlDocument before loading XML.' },
|
|
286
|
+
check({ files }) {
|
|
287
|
+
return checkCS(this, files,
|
|
288
|
+
/new\s+XmlDocument\s*\(\s*\)/,
|
|
289
|
+
);
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// SEC-CS-016: XXE via XmlTextReader
|
|
294
|
+
{
|
|
295
|
+
id: 'SEC-CS-016',
|
|
296
|
+
category: 'security',
|
|
297
|
+
severity: 'critical',
|
|
298
|
+
confidence: 'likely',
|
|
299
|
+
title: 'XXE: XmlTextReader Without Safe Settings',
|
|
300
|
+
description: 'XmlTextReader processes DTDs and external entities by default.',
|
|
301
|
+
fix: { suggestion: 'Set DtdProcessing = DtdProcessing.Prohibit on XmlReaderSettings.' },
|
|
302
|
+
check({ files }) {
|
|
303
|
+
return checkCS(this, files,
|
|
304
|
+
/new\s+XmlTextReader\s*\(/,
|
|
305
|
+
);
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
// SEC-CS-017: Weak crypto - MD5
|
|
310
|
+
{
|
|
311
|
+
id: 'SEC-CS-017',
|
|
312
|
+
category: 'security',
|
|
313
|
+
severity: 'high',
|
|
314
|
+
confidence: 'definite',
|
|
315
|
+
title: 'Weak Cryptographic Hash: MD5',
|
|
316
|
+
description: 'MD5 is cryptographically broken and must not be used for security purposes.',
|
|
317
|
+
fix: { suggestion: 'Use SHA256 or SHA512 for hashing. Use PBKDF2, bcrypt, or Argon2 for passwords.' },
|
|
318
|
+
check({ files }) {
|
|
319
|
+
return checkCS(this, files,
|
|
320
|
+
/MD5\.Create\s*\(\s*\)/,
|
|
321
|
+
/new\s+MD5CryptoServiceProvider\s*\(\s*\)/,
|
|
322
|
+
);
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
// SEC-CS-018: Weak crypto - SHA1
|
|
327
|
+
{
|
|
328
|
+
id: 'SEC-CS-018',
|
|
329
|
+
category: 'security',
|
|
330
|
+
severity: 'high',
|
|
331
|
+
confidence: 'definite',
|
|
332
|
+
title: 'Weak Cryptographic Hash: SHA1',
|
|
333
|
+
description: 'SHA1 has known collision attacks and should not be used for security.',
|
|
334
|
+
fix: { suggestion: 'Use SHA256 or SHA512 instead of SHA1.' },
|
|
335
|
+
check({ files }) {
|
|
336
|
+
return checkCS(this, files,
|
|
337
|
+
/SHA1\.Create\s*\(\s*\)/,
|
|
338
|
+
/new\s+SHA1(?:Managed|CryptoServiceProvider)\s*\(\s*\)/,
|
|
339
|
+
);
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
// SEC-CS-019: Weak crypto - DES
|
|
344
|
+
{
|
|
345
|
+
id: 'SEC-CS-019',
|
|
346
|
+
category: 'security',
|
|
347
|
+
severity: 'high',
|
|
348
|
+
confidence: 'definite',
|
|
349
|
+
title: 'Weak Encryption: DES',
|
|
350
|
+
description: 'DES has a 56-bit key and is trivially breakable.',
|
|
351
|
+
fix: { suggestion: 'Use AES (Aes.Create()) instead of DES.' },
|
|
352
|
+
check({ files }) {
|
|
353
|
+
return checkCS(this, files,
|
|
354
|
+
/DES\.Create\s*\(\s*\)/,
|
|
355
|
+
/new\s+DESCryptoServiceProvider\s*\(\s*\)/,
|
|
356
|
+
);
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
// SEC-CS-020: Weak crypto - TripleDES
|
|
361
|
+
{
|
|
362
|
+
id: 'SEC-CS-020',
|
|
363
|
+
category: 'security',
|
|
364
|
+
severity: 'medium',
|
|
365
|
+
confidence: 'definite',
|
|
366
|
+
title: 'Weak Encryption: TripleDES',
|
|
367
|
+
description: 'TripleDES is deprecated and slower than AES with comparable security.',
|
|
368
|
+
fix: { suggestion: 'Use AES (Aes.Create()) instead of TripleDES.' },
|
|
369
|
+
check({ files }) {
|
|
370
|
+
return checkCS(this, files,
|
|
371
|
+
/TripleDES\.Create\s*\(\s*\)/,
|
|
372
|
+
/new\s+TripleDESCryptoServiceProvider\s*\(\s*\)/,
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
// SEC-CS-021: Insecure random - System.Random
|
|
378
|
+
{
|
|
379
|
+
id: 'SEC-CS-021',
|
|
380
|
+
category: 'security',
|
|
381
|
+
severity: 'high',
|
|
382
|
+
confidence: 'likely',
|
|
383
|
+
title: 'Insecure Random: System.Random',
|
|
384
|
+
description: 'System.Random is predictable and must not be used for security-sensitive operations.',
|
|
385
|
+
fix: { suggestion: 'Use System.Security.Cryptography.RandomNumberGenerator for cryptographic randomness.' },
|
|
386
|
+
check({ files }) {
|
|
387
|
+
return checkCS(this, files,
|
|
388
|
+
/new\s+Random\s*\(\s*\)/,
|
|
389
|
+
);
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
// SEC-CS-022: Missing Authorize attribute
|
|
394
|
+
{
|
|
395
|
+
id: 'SEC-CS-022',
|
|
396
|
+
category: 'security',
|
|
397
|
+
severity: 'high',
|
|
398
|
+
confidence: 'suggestion',
|
|
399
|
+
title: 'Missing [Authorize] on Controller',
|
|
400
|
+
description: 'Controllers without [Authorize] allow unauthenticated access to all actions.',
|
|
401
|
+
fix: { suggestion: 'Add [Authorize] attribute to controllers or actions that require authentication.' },
|
|
402
|
+
check({ files }) {
|
|
403
|
+
return checkCS(this, files,
|
|
404
|
+
/public\s+class\s+\w+Controller\s*:\s*Controller(?!Base)/,
|
|
405
|
+
);
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
// SEC-CS-023: AllowAnonymous on sensitive endpoint
|
|
410
|
+
{
|
|
411
|
+
id: 'SEC-CS-023',
|
|
412
|
+
category: 'security',
|
|
413
|
+
severity: 'medium',
|
|
414
|
+
confidence: 'suggestion',
|
|
415
|
+
title: 'AllowAnonymous on Potentially Sensitive Endpoint',
|
|
416
|
+
description: '[AllowAnonymous] bypasses authentication. Ensure it is intentional.',
|
|
417
|
+
fix: { suggestion: 'Review endpoints with [AllowAnonymous] to ensure they should be publicly accessible.' },
|
|
418
|
+
check({ files }) {
|
|
419
|
+
return checkCS(this, files,
|
|
420
|
+
/\[AllowAnonymous\]/,
|
|
421
|
+
);
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
|
|
425
|
+
// SEC-CS-024: Missing ValidateAntiForgeryToken
|
|
426
|
+
{
|
|
427
|
+
id: 'SEC-CS-024',
|
|
428
|
+
category: 'security',
|
|
429
|
+
severity: 'high',
|
|
430
|
+
confidence: 'likely',
|
|
431
|
+
title: 'Missing [ValidateAntiForgeryToken]',
|
|
432
|
+
description: 'POST actions without [ValidateAntiForgeryToken] are vulnerable to CSRF attacks.',
|
|
433
|
+
fix: { suggestion: 'Add [ValidateAntiForgeryToken] to all POST/PUT/DELETE actions, or use AutoValidateAntiforgeryToken globally.' },
|
|
434
|
+
check({ files }) {
|
|
435
|
+
return checkCS(this, files,
|
|
436
|
+
/\[HttpPost\]/,
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
// SEC-CS-025: Missing ValidateAntiForgeryToken on PUT/DELETE
|
|
442
|
+
{
|
|
443
|
+
id: 'SEC-CS-025',
|
|
444
|
+
category: 'security',
|
|
445
|
+
severity: 'high',
|
|
446
|
+
confidence: 'likely',
|
|
447
|
+
title: 'Missing CSRF Protection on State-Changing Action',
|
|
448
|
+
description: 'PUT/DELETE actions without anti-forgery token validation are vulnerable to CSRF.',
|
|
449
|
+
fix: { suggestion: 'Add [ValidateAntiForgeryToken] or use the AutoValidateAntiforgeryToken filter.' },
|
|
450
|
+
check({ files }) {
|
|
451
|
+
return checkCS(this, files,
|
|
452
|
+
/\[Http(?:Put|Delete|Patch)\]/,
|
|
453
|
+
);
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
// SEC-CS-026: Missing Required attribute
|
|
458
|
+
{
|
|
459
|
+
id: 'SEC-CS-026',
|
|
460
|
+
category: 'security',
|
|
461
|
+
severity: 'medium',
|
|
462
|
+
confidence: 'suggestion',
|
|
463
|
+
title: 'Missing Input Validation Attributes',
|
|
464
|
+
description: 'Model properties without [Required], [StringLength] may accept invalid input.',
|
|
465
|
+
fix: { suggestion: 'Add data annotation attributes like [Required], [StringLength], [Range] to model properties.' },
|
|
466
|
+
check({ files }) {
|
|
467
|
+
return checkCS(this, files,
|
|
468
|
+
/public\s+string\s+\w+\s*\{\s*get\s*;\s*set\s*;\s*\}/,
|
|
469
|
+
);
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
// SEC-CS-027: Missing StringLength attribute
|
|
474
|
+
{
|
|
475
|
+
id: 'SEC-CS-027',
|
|
476
|
+
category: 'security',
|
|
477
|
+
severity: 'medium',
|
|
478
|
+
confidence: 'suggestion',
|
|
479
|
+
title: 'Missing String Length Validation',
|
|
480
|
+
description: 'String properties without [StringLength] can accept arbitrarily long input.',
|
|
481
|
+
fix: { suggestion: 'Add [StringLength(maxLength)] to string properties to prevent oversized input.' },
|
|
482
|
+
check({ files }) {
|
|
483
|
+
return checkCS(this, files,
|
|
484
|
+
/public\s+string\s+(?:Name|Title|Description|Comment|Message|Input|Query|Search)\s*\{/i,
|
|
485
|
+
);
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
// SEC-CS-028: Hardcoded connection string
|
|
490
|
+
{
|
|
491
|
+
id: 'SEC-CS-028',
|
|
492
|
+
category: 'security',
|
|
493
|
+
severity: 'critical',
|
|
494
|
+
confidence: 'likely',
|
|
495
|
+
title: 'Hardcoded Connection String',
|
|
496
|
+
description: 'Database connection strings with credentials hardcoded in source code.',
|
|
497
|
+
fix: { suggestion: 'Store connection strings in appsettings.json or use environment variables with User Secrets in development.' },
|
|
498
|
+
check({ files }) {
|
|
499
|
+
return checkCS(this, files,
|
|
500
|
+
/(?:connectionString|ConnectionString)\s*=\s*["'](?:Server|Data Source|Host).*(?:Password|Pwd)=/i,
|
|
501
|
+
);
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
// SEC-CS-029: Hardcoded credentials
|
|
506
|
+
{
|
|
507
|
+
id: 'SEC-CS-029',
|
|
508
|
+
category: 'security',
|
|
509
|
+
severity: 'critical',
|
|
510
|
+
confidence: 'likely',
|
|
511
|
+
title: 'Hardcoded Credentials',
|
|
512
|
+
description: 'Passwords or API keys hardcoded in source code risk exposure.',
|
|
513
|
+
fix: { suggestion: 'Use IConfiguration, environment variables, or Azure Key Vault for secrets.' },
|
|
514
|
+
check({ files }) {
|
|
515
|
+
return checkCS(this, files,
|
|
516
|
+
/(?:password|apiKey|secret|token)\s*=\s*["'][^"']{4,}["']/i,
|
|
517
|
+
);
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
// SEC-CS-030: ViewBag/ViewData for sensitive data
|
|
522
|
+
{
|
|
523
|
+
id: 'SEC-CS-030',
|
|
524
|
+
category: 'security',
|
|
525
|
+
severity: 'medium',
|
|
526
|
+
confidence: 'suggestion',
|
|
527
|
+
title: 'Sensitive Data in ViewBag/ViewData',
|
|
528
|
+
description: 'Passing sensitive data through ViewBag or ViewData can expose it in views.',
|
|
529
|
+
fix: { suggestion: 'Use strongly-typed view models and avoid passing sensitive data to views.' },
|
|
530
|
+
check({ files }) {
|
|
531
|
+
return checkCS(this, files,
|
|
532
|
+
/ViewBag\.(?:Password|Secret|Token|Key|Credential)/i,
|
|
533
|
+
/ViewData\s*\[\s*["'](?:password|secret|token|key|credential)["']\s*\]/i,
|
|
534
|
+
);
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
// SEC-CS-031: Missing HTTPS redirect
|
|
539
|
+
{
|
|
540
|
+
id: 'SEC-CS-031',
|
|
541
|
+
category: 'security',
|
|
542
|
+
severity: 'high',
|
|
543
|
+
confidence: 'suggestion',
|
|
544
|
+
title: 'Missing HTTPS Redirect',
|
|
545
|
+
description: 'Without UseHttpsRedirection, HTTP requests may not be redirected to HTTPS.',
|
|
546
|
+
fix: { suggestion: 'Add app.UseHttpsRedirection() in the request pipeline.' },
|
|
547
|
+
check({ files }) {
|
|
548
|
+
return checkCS(this, files,
|
|
549
|
+
/app\.UseRouting\s*\(\s*\)/,
|
|
550
|
+
);
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
// SEC-CS-032: Insecure cookie - missing Secure
|
|
555
|
+
{
|
|
556
|
+
id: 'SEC-CS-032',
|
|
557
|
+
category: 'security',
|
|
558
|
+
severity: 'high',
|
|
559
|
+
confidence: 'likely',
|
|
560
|
+
title: 'Insecure Cookie: Secure Flag Disabled',
|
|
561
|
+
description: 'Cookies with Secure = false can be sent over unencrypted HTTP connections.',
|
|
562
|
+
fix: { suggestion: 'Set cookie.Secure = true, especially for authentication cookies.' },
|
|
563
|
+
check({ files }) {
|
|
564
|
+
return checkCS(this, files,
|
|
565
|
+
/\.Secure\s*=\s*false/,
|
|
566
|
+
);
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
// SEC-CS-033: Insecure cookie - missing HttpOnly
|
|
571
|
+
{
|
|
572
|
+
id: 'SEC-CS-033',
|
|
573
|
+
category: 'security',
|
|
574
|
+
severity: 'medium',
|
|
575
|
+
confidence: 'likely',
|
|
576
|
+
title: 'Insecure Cookie: HttpOnly Disabled',
|
|
577
|
+
description: 'Cookies without HttpOnly can be accessed by JavaScript, increasing XSS impact.',
|
|
578
|
+
fix: { suggestion: 'Set cookie.HttpOnly = true for session and authentication cookies.' },
|
|
579
|
+
check({ files }) {
|
|
580
|
+
return checkCS(this, files,
|
|
581
|
+
/\.HttpOnly\s*=\s*false/,
|
|
582
|
+
);
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
|
|
586
|
+
// SEC-CS-034: Missing CORS policy
|
|
587
|
+
{
|
|
588
|
+
id: 'SEC-CS-034',
|
|
589
|
+
category: 'security',
|
|
590
|
+
severity: 'high',
|
|
591
|
+
confidence: 'suggestion',
|
|
592
|
+
title: 'Permissive CORS Policy',
|
|
593
|
+
description: 'AllowAnyOrigin enables any website to make cross-origin requests.',
|
|
594
|
+
fix: { suggestion: 'Specify allowed origins explicitly with WithOrigins().' },
|
|
595
|
+
check({ files }) {
|
|
596
|
+
return checkCS(this, files,
|
|
597
|
+
/\.AllowAnyOrigin\s*\(\s*\)/,
|
|
598
|
+
);
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
|
|
602
|
+
// SEC-CS-035: CORS with credentials and any origin
|
|
603
|
+
{
|
|
604
|
+
id: 'SEC-CS-035',
|
|
605
|
+
category: 'security',
|
|
606
|
+
severity: 'critical',
|
|
607
|
+
confidence: 'definite',
|
|
608
|
+
title: 'CORS: Credentials with Any Origin',
|
|
609
|
+
description: 'AllowCredentials with AllowAnyOrigin is a severe misconfiguration.',
|
|
610
|
+
fix: { suggestion: 'Never combine AllowCredentials with AllowAnyOrigin. Specify allowed origins.' },
|
|
611
|
+
check({ files }) {
|
|
612
|
+
return checkCS(this, files,
|
|
613
|
+
/\.AllowCredentials\s*\(\s*\)/,
|
|
614
|
+
);
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
|
|
618
|
+
// SEC-CS-036: Regex without timeout
|
|
619
|
+
{
|
|
620
|
+
id: 'SEC-CS-036',
|
|
621
|
+
category: 'security',
|
|
622
|
+
severity: 'medium',
|
|
623
|
+
confidence: 'likely',
|
|
624
|
+
title: 'Regex Without Timeout',
|
|
625
|
+
description: 'Regex without a timeout can cause catastrophic backtracking and denial of service.',
|
|
626
|
+
fix: { suggestion: 'Use the Regex constructor overload with a TimeSpan timeout parameter.' },
|
|
627
|
+
check({ files }) {
|
|
628
|
+
return checkCS(this, files,
|
|
629
|
+
/new\s+Regex\s*\(\s*["'][^"']+["']\s*\)/,
|
|
630
|
+
);
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
|
|
634
|
+
// SEC-CS-037: Regex with user input
|
|
635
|
+
{
|
|
636
|
+
id: 'SEC-CS-037',
|
|
637
|
+
category: 'security',
|
|
638
|
+
severity: 'high',
|
|
639
|
+
confidence: 'likely',
|
|
640
|
+
title: 'Regex with User-Controlled Pattern',
|
|
641
|
+
description: 'Creating Regex from user input allows ReDoS attacks via crafted patterns.',
|
|
642
|
+
fix: { suggestion: 'Validate or escape user input with Regex.Escape(). Always set a timeout.' },
|
|
643
|
+
check({ files }) {
|
|
644
|
+
return checkCS(this, files,
|
|
645
|
+
/new\s+Regex\s*\(\s*(?:request|input|user|param|Request)/i,
|
|
646
|
+
);
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
// SEC-CS-038: Assembly.Load with user input
|
|
651
|
+
{
|
|
652
|
+
id: 'SEC-CS-038',
|
|
653
|
+
category: 'security',
|
|
654
|
+
severity: 'critical',
|
|
655
|
+
confidence: 'likely',
|
|
656
|
+
title: 'Dynamic Assembly Loading with User Input',
|
|
657
|
+
description: 'Loading assemblies from user-controlled paths enables arbitrary code execution.',
|
|
658
|
+
fix: { suggestion: 'Validate assembly names against an allowlist. Avoid loading assemblies from user input.' },
|
|
659
|
+
check({ files }) {
|
|
660
|
+
return checkCS(this, files,
|
|
661
|
+
/Assembly\.(?:Load|LoadFrom|LoadFile)\s*\(\s*(?:request|input|user|param|Request)/i,
|
|
662
|
+
);
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
|
|
666
|
+
// SEC-CS-039: Activator.CreateInstance with user input
|
|
667
|
+
{
|
|
668
|
+
id: 'SEC-CS-039',
|
|
669
|
+
category: 'security',
|
|
670
|
+
severity: 'critical',
|
|
671
|
+
confidence: 'likely',
|
|
672
|
+
title: 'Dynamic Type Instantiation with User Input',
|
|
673
|
+
description: 'Activator.CreateInstance with user-controlled type names can instantiate malicious types.',
|
|
674
|
+
fix: { suggestion: 'Validate type names against an allowlist.' },
|
|
675
|
+
check({ files }) {
|
|
676
|
+
return checkCS(this, files,
|
|
677
|
+
/Activator\.CreateInstance\s*\(\s*(?:Type\.GetType\s*\(|.*(?:request|input|user|param))/i,
|
|
678
|
+
);
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
|
|
682
|
+
// SEC-CS-040: Information exposure via exception
|
|
683
|
+
{
|
|
684
|
+
id: 'SEC-CS-040',
|
|
685
|
+
category: 'security',
|
|
686
|
+
severity: 'medium',
|
|
687
|
+
confidence: 'likely',
|
|
688
|
+
title: 'Information Exposure via Exception Details',
|
|
689
|
+
description: 'Returning exception details to users can expose internal implementation details.',
|
|
690
|
+
fix: { suggestion: 'Log the full exception internally. Return generic error messages to users.' },
|
|
691
|
+
check({ files }) {
|
|
692
|
+
return checkCS(this, files,
|
|
693
|
+
/return\s+(?:Ok|Content|Json)\s*\(\s*(?:ex|exception)\.(?:Message|StackTrace|ToString)/i,
|
|
694
|
+
);
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
|
|
698
|
+
// SEC-CS-041: Missing model validation
|
|
699
|
+
{
|
|
700
|
+
id: 'SEC-CS-041',
|
|
701
|
+
category: 'security',
|
|
702
|
+
severity: 'medium',
|
|
703
|
+
confidence: 'suggestion',
|
|
704
|
+
title: 'Missing ModelState Validation',
|
|
705
|
+
description: 'Not checking ModelState.IsValid may process invalid input.',
|
|
706
|
+
fix: { suggestion: 'Check ModelState.IsValid at the start of controller actions, or use [ApiController] for automatic validation.' },
|
|
707
|
+
check({ files }) {
|
|
708
|
+
return checkCS(this, files,
|
|
709
|
+
/public\s+(?:async\s+)?(?:Task<)?(?:IActionResult|ActionResult).*\(/,
|
|
710
|
+
);
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
|
|
714
|
+
// SEC-CS-042: Open redirect
|
|
715
|
+
{
|
|
716
|
+
id: 'SEC-CS-042',
|
|
717
|
+
category: 'security',
|
|
718
|
+
severity: 'high',
|
|
719
|
+
confidence: 'likely',
|
|
720
|
+
title: 'Open Redirect',
|
|
721
|
+
description: 'Redirecting to user-supplied URLs without validation enables phishing.',
|
|
722
|
+
fix: { suggestion: 'Use LocalRedirect() or Url.IsLocalUrl() to validate redirect targets.' },
|
|
723
|
+
check({ files }) {
|
|
724
|
+
return checkCS(this, files,
|
|
725
|
+
/Redirect\s*\(\s*(?:request|input|user|param|Request|returnUrl|url)/i,
|
|
726
|
+
);
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
// SEC-CS-043: Insecure SSL/TLS
|
|
731
|
+
{
|
|
732
|
+
id: 'SEC-CS-043',
|
|
733
|
+
category: 'security',
|
|
734
|
+
severity: 'critical',
|
|
735
|
+
confidence: 'definite',
|
|
736
|
+
title: 'TLS Certificate Validation Disabled',
|
|
737
|
+
description: 'Disabling certificate validation allows man-in-the-middle attacks.',
|
|
738
|
+
fix: { suggestion: 'Remove ServerCertificateValidationCallback that returns true. Use proper CA certificates.' },
|
|
739
|
+
check({ files }) {
|
|
740
|
+
return checkCS(this, files,
|
|
741
|
+
/ServerCertificateValidationCallback\s*=\s*\(\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*=>\s*true/,
|
|
742
|
+
/ServicePointManager\.ServerCertificateValidationCallback\s*\+?=\s*delegate\s*\{[^}]*return\s+true/,
|
|
743
|
+
);
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
|
|
747
|
+
// SEC-CS-044: Logging sensitive data
|
|
748
|
+
{
|
|
749
|
+
id: 'SEC-CS-044',
|
|
750
|
+
category: 'security',
|
|
751
|
+
severity: 'medium',
|
|
752
|
+
confidence: 'likely',
|
|
753
|
+
title: 'Sensitive Data in Log Output',
|
|
754
|
+
description: 'Logging passwords, tokens, or keys exposes sensitive data in log files.',
|
|
755
|
+
fix: { suggestion: 'Redact sensitive fields before logging.' },
|
|
756
|
+
check({ files }) {
|
|
757
|
+
return checkCS(this, files,
|
|
758
|
+
/_?logger\.(?:Log|Info|Debug|Warn|Error|Fatal)\w*\s*\(.*(?:password|token|secret|key|credential)/i,
|
|
759
|
+
);
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
|
|
763
|
+
// SEC-CS-045: Hardcoded encryption key
|
|
764
|
+
{
|
|
765
|
+
id: 'SEC-CS-045',
|
|
766
|
+
category: 'security',
|
|
767
|
+
severity: 'critical',
|
|
768
|
+
confidence: 'likely',
|
|
769
|
+
title: 'Hardcoded Encryption Key',
|
|
770
|
+
description: 'Encryption keys hardcoded in source code can be extracted from assemblies.',
|
|
771
|
+
fix: { suggestion: 'Use Azure Key Vault, DPAPI, or configuration management for encryption keys.' },
|
|
772
|
+
check({ files }) {
|
|
773
|
+
return checkCS(this, files,
|
|
774
|
+
/\.Key\s*=\s*(?:Encoding\.\w+\.GetBytes\s*\(\s*["']|new\s+byte\s*\[\s*\]\s*\{)/,
|
|
775
|
+
);
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
|
|
779
|
+
// SEC-CS-046: Weak password hashing
|
|
780
|
+
{
|
|
781
|
+
id: 'SEC-CS-046',
|
|
782
|
+
category: 'security',
|
|
783
|
+
severity: 'high',
|
|
784
|
+
confidence: 'likely',
|
|
785
|
+
title: 'Weak Password Hashing',
|
|
786
|
+
description: 'Using MD5 or SHA for password hashing is insecure without salting and stretching.',
|
|
787
|
+
fix: { suggestion: 'Use PasswordHasher<T>, bcrypt, or Argon2 for password hashing.' },
|
|
788
|
+
check({ files }) {
|
|
789
|
+
return checkCS(this, files,
|
|
790
|
+
/(?:MD5|SHA\d+)\.(?:Create|ComputeHash).*(?:password|passwd)/i,
|
|
791
|
+
);
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
|
|
795
|
+
// SEC-CS-047: Server header exposure
|
|
796
|
+
{
|
|
797
|
+
id: 'SEC-CS-047',
|
|
798
|
+
category: 'security',
|
|
799
|
+
severity: 'low',
|
|
800
|
+
confidence: 'suggestion',
|
|
801
|
+
title: 'Server Header Exposed',
|
|
802
|
+
description: 'Exposing server version headers helps attackers identify vulnerabilities.',
|
|
803
|
+
fix: { suggestion: 'Remove or suppress server version headers using UseKestrel options.' },
|
|
804
|
+
check({ files }) {
|
|
805
|
+
return checkCS(this, files,
|
|
806
|
+
/AddServerHeader\s*=\s*true/,
|
|
807
|
+
);
|
|
808
|
+
},
|
|
809
|
+
},
|
|
810
|
+
|
|
811
|
+
// SEC-CS-048: Mass assignment
|
|
812
|
+
{
|
|
813
|
+
id: 'SEC-CS-048',
|
|
814
|
+
category: 'security',
|
|
815
|
+
severity: 'high',
|
|
816
|
+
confidence: 'suggestion',
|
|
817
|
+
title: 'Mass Assignment Vulnerability',
|
|
818
|
+
description: 'Binding request data directly to domain models without [Bind] can allow overposting.',
|
|
819
|
+
fix: { suggestion: 'Use [Bind(Include = ...)] or separate view models/DTOs to control which properties are bound.' },
|
|
820
|
+
check({ files }) {
|
|
821
|
+
return checkCS(this, files,
|
|
822
|
+
/public\s+(?:async\s+)?(?:Task<)?IActionResult\s+\w+\s*\(\s*(?!\[Bind)[A-Z]\w+\s+\w+\s*\)/,
|
|
823
|
+
);
|
|
824
|
+
},
|
|
825
|
+
},
|
|
826
|
+
|
|
827
|
+
// SEC-CS-049: Insecure file upload
|
|
828
|
+
{
|
|
829
|
+
id: 'SEC-CS-049',
|
|
830
|
+
category: 'security',
|
|
831
|
+
severity: 'high',
|
|
832
|
+
confidence: 'likely',
|
|
833
|
+
title: 'Insecure File Upload',
|
|
834
|
+
description: 'Saving uploaded files without validating content type, extension, and size is dangerous.',
|
|
835
|
+
fix: { suggestion: 'Validate file extension, content type, and size. Store outside web root with generated names.' },
|
|
836
|
+
check({ files }) {
|
|
837
|
+
return checkCS(this, files,
|
|
838
|
+
/IFormFile.*\.CopyTo\s*\(/,
|
|
839
|
+
/\.SaveAs\s*\(\s*(?:Path\.Combine|Server\.MapPath)/,
|
|
840
|
+
);
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
|
|
844
|
+
// SEC-CS-050: Entity Framework raw SQL
|
|
845
|
+
{
|
|
846
|
+
id: 'SEC-CS-050',
|
|
847
|
+
category: 'security',
|
|
848
|
+
severity: 'critical',
|
|
849
|
+
confidence: 'likely',
|
|
850
|
+
title: 'SQL Injection via Entity Framework Raw SQL',
|
|
851
|
+
description: 'Using FromSqlRaw or ExecuteSqlRaw with string interpolation bypasses EF parameterization.',
|
|
852
|
+
fix: { suggestion: 'Use FromSqlInterpolated or ExecuteSqlInterpolated which auto-parameterize interpolated strings.' },
|
|
853
|
+
check({ files }) {
|
|
854
|
+
return checkCS(this, files,
|
|
855
|
+
/\.FromSqlRaw\s*\(\s*\$"/,
|
|
856
|
+
/\.ExecuteSqlRaw\s*\(\s*\$"/,
|
|
857
|
+
);
|
|
858
|
+
},
|
|
859
|
+
},
|
|
860
|
+
];
|
|
861
|
+
|
|
862
|
+
export default rules;
|