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,850 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go Security Rules (SEC-GO-001 through SEC-GO-050)
|
|
3
|
+
*
|
|
4
|
+
* Each rule scans Go source files for security vulnerabilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const isGo = (f) => f.endsWith('.go');
|
|
8
|
+
|
|
9
|
+
const SKIP_PATH = /[/\\](test|tests|__tests__|__mocks__|mocks|fixtures|__fixtures__|spec|__snapshots__|node_modules|vendor|dist|build)[/\\]/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 checkGo(rule, files, ...patterns) {
|
|
36
|
+
const findings = [];
|
|
37
|
+
for (const [path, content] of files) {
|
|
38
|
+
if (SKIP_PATH.test(path)) continue;
|
|
39
|
+
if (isGo(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-GO-001: SQL injection via fmt.Sprintf in db.Query
|
|
50
|
+
{
|
|
51
|
+
id: 'SEC-GO-001',
|
|
52
|
+
category: 'security',
|
|
53
|
+
severity: 'critical',
|
|
54
|
+
confidence: 'likely',
|
|
55
|
+
title: 'SQL Injection via fmt.Sprintf in Query',
|
|
56
|
+
description: 'Using fmt.Sprintf to build SQL queries passed to db.Query or db.Exec allows SQL injection.',
|
|
57
|
+
fix: { suggestion: 'Use parameterized queries with $1, $2 or ? placeholders instead of fmt.Sprintf.' },
|
|
58
|
+
check({ files }) {
|
|
59
|
+
return checkGo(this, files,
|
|
60
|
+
/db\.(?:Query|Exec|QueryRow)\s*\(\s*fmt\.Sprintf/,
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// SEC-GO-002: SQL injection via string concatenation
|
|
66
|
+
{
|
|
67
|
+
id: 'SEC-GO-002',
|
|
68
|
+
category: 'security',
|
|
69
|
+
severity: 'critical',
|
|
70
|
+
confidence: 'likely',
|
|
71
|
+
title: 'SQL Injection via String Concatenation',
|
|
72
|
+
description: 'Concatenating strings to build SQL queries allows injection attacks.',
|
|
73
|
+
fix: { suggestion: 'Use parameterized queries instead of string concatenation.' },
|
|
74
|
+
check({ files }) {
|
|
75
|
+
return checkGo(this, files,
|
|
76
|
+
/db\.(?:Query|Exec|QueryRow)\s*\(\s*["'].*["']\s*\+/,
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// SEC-GO-003: Command injection via exec.Command
|
|
82
|
+
{
|
|
83
|
+
id: 'SEC-GO-003',
|
|
84
|
+
category: 'security',
|
|
85
|
+
severity: 'critical',
|
|
86
|
+
confidence: 'likely',
|
|
87
|
+
title: 'Command Injection via exec.Command',
|
|
88
|
+
description: 'Passing user-controlled input to exec.Command can lead to arbitrary command execution.',
|
|
89
|
+
fix: { suggestion: 'Validate and sanitize input before passing to exec.Command. Avoid using shell execution.' },
|
|
90
|
+
check({ files }) {
|
|
91
|
+
return checkGo(this, files,
|
|
92
|
+
/exec\.Command\s*\(\s*(?:r\.|req\.|params\.|input|user|arg)/,
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// SEC-GO-004: Command injection via exec.CommandContext with shell
|
|
98
|
+
{
|
|
99
|
+
id: 'SEC-GO-004',
|
|
100
|
+
category: 'security',
|
|
101
|
+
severity: 'critical',
|
|
102
|
+
confidence: 'likely',
|
|
103
|
+
title: 'Command Injection via Shell Execution',
|
|
104
|
+
description: 'Using exec.Command with bash/sh -c and string interpolation enables command injection.',
|
|
105
|
+
fix: { suggestion: 'Use exec.Command with separate arguments instead of shell -c invocations.' },
|
|
106
|
+
check({ files }) {
|
|
107
|
+
return checkGo(this, files,
|
|
108
|
+
/exec\.Command(?:Context)?\s*\(\s*(?:ctx\s*,\s*)?["'](?:bash|sh|\/bin\/(?:ba)?sh)["']\s*,\s*["']-c["']/,
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// SEC-GO-005: Path traversal without filepath.Clean
|
|
114
|
+
{
|
|
115
|
+
id: 'SEC-GO-005',
|
|
116
|
+
category: 'security',
|
|
117
|
+
severity: 'high',
|
|
118
|
+
confidence: 'likely',
|
|
119
|
+
title: 'Path Traversal via filepath.Join Without Cleaning',
|
|
120
|
+
description: 'Using filepath.Join with user input without filepath.Clean may allow directory traversal.',
|
|
121
|
+
fix: { suggestion: 'Use filepath.Clean on user-supplied path segments and verify the result stays within the expected base directory.' },
|
|
122
|
+
check({ files }) {
|
|
123
|
+
return checkGo(this, files,
|
|
124
|
+
/filepath\.Join\s*\(\s*\w+\s*,\s*(?:r\.|req\.|params\.|c\.(?:Param|Query)|input|user)/,
|
|
125
|
+
);
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// SEC-GO-006: Path traversal via os.Open with user input
|
|
130
|
+
{
|
|
131
|
+
id: 'SEC-GO-006',
|
|
132
|
+
category: 'security',
|
|
133
|
+
severity: 'high',
|
|
134
|
+
confidence: 'likely',
|
|
135
|
+
title: 'Path Traversal via os.Open',
|
|
136
|
+
description: 'Opening files with unsanitized user input can allow reading arbitrary files.',
|
|
137
|
+
fix: { suggestion: 'Validate and sanitize the file path. Use filepath.Clean and verify the path prefix.' },
|
|
138
|
+
check({ files }) {
|
|
139
|
+
return checkGo(this, files,
|
|
140
|
+
/os\.(?:Open|ReadFile|Create)\s*\(\s*(?:r\.|req\.|params\.|c\.(?:Param|Query)|input|user)/,
|
|
141
|
+
);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// SEC-GO-007: SSRF via http.Get with user-controlled URL
|
|
146
|
+
{
|
|
147
|
+
id: 'SEC-GO-007',
|
|
148
|
+
category: 'security',
|
|
149
|
+
severity: 'high',
|
|
150
|
+
confidence: 'likely',
|
|
151
|
+
title: 'SSRF via http.Get with User-Controlled URL',
|
|
152
|
+
description: 'Passing user input directly to http.Get allows Server-Side Request Forgery.',
|
|
153
|
+
fix: { suggestion: 'Validate and whitelist allowed URLs/domains before making HTTP requests.' },
|
|
154
|
+
check({ files }) {
|
|
155
|
+
return checkGo(this, files,
|
|
156
|
+
/http\.(?:Get|Post|Head)\s*\(\s*(?:r\.|req\.|params\.|c\.(?:Param|Query)|input|user|fmt\.Sprintf)/,
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
// SEC-GO-008: SSRF via http.NewRequest with user URL
|
|
162
|
+
{
|
|
163
|
+
id: 'SEC-GO-008',
|
|
164
|
+
category: 'security',
|
|
165
|
+
severity: 'high',
|
|
166
|
+
confidence: 'likely',
|
|
167
|
+
title: 'SSRF via http.NewRequest with User URL',
|
|
168
|
+
description: 'Building HTTP requests with user-supplied URLs allows SSRF attacks.',
|
|
169
|
+
fix: { suggestion: 'Validate user-provided URLs against an allowlist of trusted hosts.' },
|
|
170
|
+
check({ files }) {
|
|
171
|
+
return checkGo(this, files,
|
|
172
|
+
/http\.NewRequest\s*\(\s*["']\w+["']\s*,\s*(?:r\.|req\.|params\.|input|user|fmt\.Sprintf)/,
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// SEC-GO-009: Ignored error return with blank identifier
|
|
178
|
+
{
|
|
179
|
+
id: 'SEC-GO-009',
|
|
180
|
+
category: 'security',
|
|
181
|
+
severity: 'medium',
|
|
182
|
+
confidence: 'likely',
|
|
183
|
+
title: 'Ignored Error Return Value',
|
|
184
|
+
description: 'Assigning errors to the blank identifier (_) ignores potential failures that may have security implications.',
|
|
185
|
+
fix: { suggestion: 'Handle error return values explicitly. At minimum, log the error.' },
|
|
186
|
+
check({ files }) {
|
|
187
|
+
return checkGo(this, files,
|
|
188
|
+
/\b_\s*=\s*\w+\.(?:Close|Write|Read|Exec|Query|Send|Flush|Remove|Mkdir)\b/,
|
|
189
|
+
);
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// SEC-GO-010: Missing error check on security-critical operations
|
|
194
|
+
{
|
|
195
|
+
id: 'SEC-GO-010',
|
|
196
|
+
category: 'security',
|
|
197
|
+
severity: 'high',
|
|
198
|
+
confidence: 'likely',
|
|
199
|
+
title: 'Missing Error Check on Critical Operation',
|
|
200
|
+
description: 'Not checking errors from crypto, TLS, or auth operations can lead to security bypasses.',
|
|
201
|
+
fix: { suggestion: 'Always check and handle errors from security-critical operations.' },
|
|
202
|
+
check({ files }) {
|
|
203
|
+
return checkGo(this, files,
|
|
204
|
+
/\b_\s*=\s*(?:tls|crypto|bcrypt|jwt|auth)\.\w+/,
|
|
205
|
+
);
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// SEC-GO-011: Weak crypto - MD5 usage
|
|
210
|
+
{
|
|
211
|
+
id: 'SEC-GO-011',
|
|
212
|
+
category: 'security',
|
|
213
|
+
severity: 'high',
|
|
214
|
+
confidence: 'definite',
|
|
215
|
+
title: 'Weak Cryptographic Hash: MD5',
|
|
216
|
+
description: 'MD5 is cryptographically broken and should not be used for security purposes.',
|
|
217
|
+
fix: { suggestion: 'Use SHA-256 or SHA-3 for hashing. Use bcrypt/scrypt/argon2 for password hashing.' },
|
|
218
|
+
check({ files }) {
|
|
219
|
+
return checkGo(this, files,
|
|
220
|
+
/md5\.(?:New|Sum)\s*\(/,
|
|
221
|
+
);
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// SEC-GO-012: Weak crypto - SHA1 for passwords
|
|
226
|
+
{
|
|
227
|
+
id: 'SEC-GO-012',
|
|
228
|
+
category: 'security',
|
|
229
|
+
severity: 'high',
|
|
230
|
+
confidence: 'likely',
|
|
231
|
+
title: 'Weak Cryptographic Hash: SHA1',
|
|
232
|
+
description: 'SHA1 is deprecated for security use and should not be used for password hashing.',
|
|
233
|
+
fix: { suggestion: 'Use bcrypt, scrypt, or argon2 for password hashing. Use SHA-256+ for integrity checks.' },
|
|
234
|
+
check({ files }) {
|
|
235
|
+
return checkGo(this, files,
|
|
236
|
+
/sha1\.(?:New|Sum)\s*\(/,
|
|
237
|
+
);
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
// SEC-GO-013: Weak crypto - DES usage
|
|
242
|
+
{
|
|
243
|
+
id: 'SEC-GO-013',
|
|
244
|
+
category: 'security',
|
|
245
|
+
severity: 'high',
|
|
246
|
+
confidence: 'definite',
|
|
247
|
+
title: 'Weak Encryption: DES',
|
|
248
|
+
description: 'DES has a 56-bit key and is trivially breakable with modern hardware.',
|
|
249
|
+
fix: { suggestion: 'Use AES-256 (crypto/aes) instead of DES.' },
|
|
250
|
+
check({ files }) {
|
|
251
|
+
return checkGo(this, files,
|
|
252
|
+
/des\.NewCipher\s*\(/,
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// SEC-GO-014: Insecure TLS - InsecureSkipVerify
|
|
258
|
+
{
|
|
259
|
+
id: 'SEC-GO-014',
|
|
260
|
+
category: 'security',
|
|
261
|
+
severity: 'critical',
|
|
262
|
+
confidence: 'definite',
|
|
263
|
+
title: 'Insecure TLS: Certificate Verification Disabled',
|
|
264
|
+
description: 'Setting InsecureSkipVerify to true disables TLS certificate validation, enabling MITM attacks.',
|
|
265
|
+
fix: { suggestion: 'Remove InsecureSkipVerify: true. Use proper CA certificates for verification.' },
|
|
266
|
+
check({ files }) {
|
|
267
|
+
return checkGo(this, files,
|
|
268
|
+
/InsecureSkipVerify\s*:\s*true/,
|
|
269
|
+
);
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
// SEC-GO-015: Insecure TLS - MinVersion too low
|
|
274
|
+
{
|
|
275
|
+
id: 'SEC-GO-015',
|
|
276
|
+
category: 'security',
|
|
277
|
+
severity: 'high',
|
|
278
|
+
confidence: 'definite',
|
|
279
|
+
title: 'Insecure TLS: Minimum Version Too Low',
|
|
280
|
+
description: 'Setting TLS MinVersion to TLS 1.0 or 1.1 allows use of deprecated protocols with known vulnerabilities.',
|
|
281
|
+
fix: { suggestion: 'Set MinVersion to tls.VersionTLS12 or tls.VersionTLS13.' },
|
|
282
|
+
check({ files }) {
|
|
283
|
+
return checkGo(this, files,
|
|
284
|
+
/MinVersion\s*:\s*tls\.Version(?:TLS10|TLS11|SSL30)/,
|
|
285
|
+
);
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
// SEC-GO-016: Hardcoded credentials - password
|
|
290
|
+
{
|
|
291
|
+
id: 'SEC-GO-016',
|
|
292
|
+
category: 'security',
|
|
293
|
+
severity: 'critical',
|
|
294
|
+
confidence: 'likely',
|
|
295
|
+
title: 'Hardcoded Password',
|
|
296
|
+
description: 'Passwords hardcoded in source code can be extracted and used to compromise systems.',
|
|
297
|
+
fix: { suggestion: 'Use environment variables, secret managers, or configuration files excluded from version control.' },
|
|
298
|
+
check({ files }) {
|
|
299
|
+
return checkGo(this, files,
|
|
300
|
+
/(?:password|passwd|pwd)\s*(?:=|:=)\s*["'][^"']{4,}["']/i,
|
|
301
|
+
);
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// SEC-GO-017: Hardcoded credentials - API key
|
|
306
|
+
{
|
|
307
|
+
id: 'SEC-GO-017',
|
|
308
|
+
category: 'security',
|
|
309
|
+
severity: 'critical',
|
|
310
|
+
confidence: 'likely',
|
|
311
|
+
title: 'Hardcoded API Key',
|
|
312
|
+
description: 'API keys hardcoded in source code can be extracted from binaries or repositories.',
|
|
313
|
+
fix: { suggestion: 'Use environment variables or a secrets manager for API keys.' },
|
|
314
|
+
check({ files }) {
|
|
315
|
+
return checkGo(this, files,
|
|
316
|
+
/(?:apiKey|api_key|apiSecret|api_secret|token)\s*(?:=|:=)\s*["'][A-Za-z0-9_\-]{16,}["']/,
|
|
317
|
+
);
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
// SEC-GO-018: Hardcoded connection string
|
|
322
|
+
{
|
|
323
|
+
id: 'SEC-GO-018',
|
|
324
|
+
category: 'security',
|
|
325
|
+
severity: 'high',
|
|
326
|
+
confidence: 'likely',
|
|
327
|
+
title: 'Hardcoded Database Connection String',
|
|
328
|
+
description: 'Database connection strings with credentials hardcoded in source code risk exposure.',
|
|
329
|
+
fix: { suggestion: 'Use environment variables for database connection strings.' },
|
|
330
|
+
check({ files }) {
|
|
331
|
+
return checkGo(this, files,
|
|
332
|
+
/(?:=|:=)\s*["'](?:postgres|mysql|mongodb|redis):\/\/\w+:\w+@/,
|
|
333
|
+
);
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
// SEC-GO-019: Race condition - shared variable without mutex
|
|
338
|
+
{
|
|
339
|
+
id: 'SEC-GO-019',
|
|
340
|
+
category: 'security',
|
|
341
|
+
severity: 'high',
|
|
342
|
+
confidence: 'suggestion',
|
|
343
|
+
title: 'Potential Race Condition: Global Variable Modified in Goroutine',
|
|
344
|
+
description: 'Modifying shared state inside a goroutine without proper synchronization leads to race conditions.',
|
|
345
|
+
fix: { suggestion: 'Use sync.Mutex, sync.RWMutex, or channels to protect shared state.' },
|
|
346
|
+
check({ files }) {
|
|
347
|
+
return checkGo(this, files,
|
|
348
|
+
/go\s+func\s*\([^)]*\)\s*\{[^}]*(?:counter|count|total|sum|shared|global)\s*(?:\+\+|\-\-|\+=|\-=|=)/,
|
|
349
|
+
);
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
// SEC-GO-020: Race condition - map access without sync
|
|
354
|
+
{
|
|
355
|
+
id: 'SEC-GO-020',
|
|
356
|
+
category: 'security',
|
|
357
|
+
severity: 'high',
|
|
358
|
+
confidence: 'suggestion',
|
|
359
|
+
title: 'Potential Race Condition: Concurrent Map Access',
|
|
360
|
+
description: 'Go maps are not safe for concurrent access. Use sync.Map or protect with mutex.',
|
|
361
|
+
fix: { suggestion: 'Use sync.Map or protect map access with sync.RWMutex.' },
|
|
362
|
+
check({ files }) {
|
|
363
|
+
return checkGo(this, files,
|
|
364
|
+
/go\s+func\s*\([^)]*\)\s*\{[^}]*\w+\[\w+\]\s*=/,
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
// SEC-GO-021: Template injection via template.HTML
|
|
370
|
+
{
|
|
371
|
+
id: 'SEC-GO-021',
|
|
372
|
+
category: 'security',
|
|
373
|
+
severity: 'high',
|
|
374
|
+
confidence: 'likely',
|
|
375
|
+
title: 'Template Injection via template.HTML',
|
|
376
|
+
description: 'Using template.HTML() with user input bypasses Go template auto-escaping, enabling XSS.',
|
|
377
|
+
fix: { suggestion: 'Avoid template.HTML with user-controlled data. Let the template engine auto-escape output.' },
|
|
378
|
+
check({ files }) {
|
|
379
|
+
return checkGo(this, files,
|
|
380
|
+
/template\.HTML\s*\(\s*(?:r\.|req\.|params\.|c\.(?:Param|Query)|input|user)/,
|
|
381
|
+
);
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
// SEC-GO-022: Template injection via template.JS
|
|
386
|
+
{
|
|
387
|
+
id: 'SEC-GO-022',
|
|
388
|
+
category: 'security',
|
|
389
|
+
severity: 'high',
|
|
390
|
+
confidence: 'likely',
|
|
391
|
+
title: 'Template Injection via template.JS',
|
|
392
|
+
description: 'Using template.JS() with user input bypasses auto-escaping in JavaScript contexts.',
|
|
393
|
+
fix: { suggestion: 'Do not pass user input to template.JS. Use proper escaping.' },
|
|
394
|
+
check({ files }) {
|
|
395
|
+
return checkGo(this, files,
|
|
396
|
+
/template\.JS\s*\(\s*(?:r\.|req\.|params\.|c\.(?:Param|Query)|input|user)/,
|
|
397
|
+
);
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
// SEC-GO-023: Missing input validation in Gin handler
|
|
402
|
+
{
|
|
403
|
+
id: 'SEC-GO-023',
|
|
404
|
+
category: 'security',
|
|
405
|
+
severity: 'medium',
|
|
406
|
+
confidence: 'suggestion',
|
|
407
|
+
title: 'Missing Input Validation in Gin Handler',
|
|
408
|
+
description: 'Gin handler uses c.Param or c.Query without validation or binding.',
|
|
409
|
+
fix: { suggestion: 'Use ShouldBindJSON, ShouldBindQuery, or validate input with go-playground/validator.' },
|
|
410
|
+
check({ files }) {
|
|
411
|
+
return checkGo(this, files,
|
|
412
|
+
/c\.(?:Param|Query|PostForm)\s*\(\s*["']\w+["']\s*\)/,
|
|
413
|
+
);
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
// SEC-GO-024: Missing input validation in Echo handler
|
|
418
|
+
{
|
|
419
|
+
id: 'SEC-GO-024',
|
|
420
|
+
category: 'security',
|
|
421
|
+
severity: 'medium',
|
|
422
|
+
confidence: 'suggestion',
|
|
423
|
+
title: 'Missing Input Validation in Echo Handler',
|
|
424
|
+
description: 'Echo handler uses c.Param or c.QueryParam without validation.',
|
|
425
|
+
fix: { suggestion: 'Use echo.Bind with validation tags or validate input manually.' },
|
|
426
|
+
check({ files }) {
|
|
427
|
+
return checkGo(this, files,
|
|
428
|
+
/c\.(?:QueryParam|FormValue)\s*\(\s*["']\w+["']\s*\)/,
|
|
429
|
+
);
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
// SEC-GO-025: CORS wildcard origin
|
|
434
|
+
{
|
|
435
|
+
id: 'SEC-GO-025',
|
|
436
|
+
category: 'security',
|
|
437
|
+
severity: 'high',
|
|
438
|
+
confidence: 'definite',
|
|
439
|
+
title: 'CORS Wildcard Origin',
|
|
440
|
+
description: 'Setting Access-Control-Allow-Origin to "*" allows any website to make cross-origin requests.',
|
|
441
|
+
fix: { suggestion: 'Specify allowed origins explicitly instead of using a wildcard.' },
|
|
442
|
+
check({ files }) {
|
|
443
|
+
return checkGo(this, files,
|
|
444
|
+
/(?:Access-Control-Allow-Origin|AllowOrigins).*["']\*["']/,
|
|
445
|
+
);
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
// SEC-GO-026: CORS allow credentials with wildcard
|
|
450
|
+
{
|
|
451
|
+
id: 'SEC-GO-026',
|
|
452
|
+
category: 'security',
|
|
453
|
+
severity: 'critical',
|
|
454
|
+
confidence: 'definite',
|
|
455
|
+
title: 'CORS Allow Credentials with Wildcard Origin',
|
|
456
|
+
description: 'Allowing credentials with wildcard origin is a severe misconfiguration enabling credential theft.',
|
|
457
|
+
fix: { suggestion: 'Never use AllowCredentials with wildcard AllowOrigins.' },
|
|
458
|
+
check({ files }) {
|
|
459
|
+
return checkGo(this, files,
|
|
460
|
+
/AllowCredentials\s*:\s*true/,
|
|
461
|
+
);
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
// SEC-GO-027: JWT parsing without validation
|
|
466
|
+
{
|
|
467
|
+
id: 'SEC-GO-027',
|
|
468
|
+
category: 'security',
|
|
469
|
+
severity: 'critical',
|
|
470
|
+
confidence: 'likely',
|
|
471
|
+
title: 'JWT Parsing Without Proper Validation',
|
|
472
|
+
description: 'Using jwt.Parse without specifying valid signing methods allows algorithm substitution attacks.',
|
|
473
|
+
fix: { suggestion: 'Use jwt.ParseWithClaims and specify the expected signing method in the key function.' },
|
|
474
|
+
check({ files }) {
|
|
475
|
+
return checkGo(this, files,
|
|
476
|
+
/jwt\.Parse\s*\(\s*\w+\s*,\s*func/,
|
|
477
|
+
);
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
// SEC-GO-028: JWT none algorithm acceptance
|
|
482
|
+
{
|
|
483
|
+
id: 'SEC-GO-028',
|
|
484
|
+
category: 'security',
|
|
485
|
+
severity: 'critical',
|
|
486
|
+
confidence: 'definite',
|
|
487
|
+
title: 'JWT None Algorithm Accepted',
|
|
488
|
+
description: 'Not validating the JWT signing method allows attackers to use the "none" algorithm to forge tokens.',
|
|
489
|
+
fix: { suggestion: 'Always verify the signing method in the key function: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { ... }' },
|
|
490
|
+
check({ files }) {
|
|
491
|
+
return checkGo(this, files,
|
|
492
|
+
/jwt\.UnsafeAllowNoneSignatureType/,
|
|
493
|
+
);
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
// SEC-GO-029: Goroutine leak - missing context cancellation
|
|
498
|
+
{
|
|
499
|
+
id: 'SEC-GO-029',
|
|
500
|
+
category: 'security',
|
|
501
|
+
severity: 'medium',
|
|
502
|
+
confidence: 'suggestion',
|
|
503
|
+
title: 'Goroutine Leak: Missing Context Cancellation',
|
|
504
|
+
description: 'Starting goroutines without context cancellation or timeout can cause resource exhaustion.',
|
|
505
|
+
fix: { suggestion: 'Use context.WithCancel or context.WithTimeout and pass the context to goroutines.' },
|
|
506
|
+
check({ files }) {
|
|
507
|
+
return checkGo(this, files,
|
|
508
|
+
/go\s+func\s*\(\s*\)\s*\{/,
|
|
509
|
+
);
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
// SEC-GO-030: Goroutine leak - no WaitGroup or channel
|
|
514
|
+
{
|
|
515
|
+
id: 'SEC-GO-030',
|
|
516
|
+
category: 'security',
|
|
517
|
+
severity: 'medium',
|
|
518
|
+
confidence: 'suggestion',
|
|
519
|
+
title: 'Goroutine Without Lifecycle Management',
|
|
520
|
+
description: 'Goroutines started without WaitGroup, context, or done channel may leak on shutdown.',
|
|
521
|
+
fix: { suggestion: 'Use sync.WaitGroup or a done channel to manage goroutine lifecycle.' },
|
|
522
|
+
check({ files }) {
|
|
523
|
+
return checkGo(this, files,
|
|
524
|
+
/go\s+\w+\s*\([^)]*\)\s*$/,
|
|
525
|
+
);
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
// SEC-GO-031: Deferred Close without error check
|
|
530
|
+
{
|
|
531
|
+
id: 'SEC-GO-031',
|
|
532
|
+
category: 'security',
|
|
533
|
+
severity: 'low',
|
|
534
|
+
confidence: 'suggestion',
|
|
535
|
+
title: 'Deferred Close Without Error Check',
|
|
536
|
+
description: 'Using defer file.Close() without checking the error may silently lose data on write failures.',
|
|
537
|
+
fix: { suggestion: 'Use a named return and check the error: defer func() { err = f.Close() }()' },
|
|
538
|
+
check({ files }) {
|
|
539
|
+
return checkGo(this, files,
|
|
540
|
+
/defer\s+\w+\.Close\s*\(\s*\)/,
|
|
541
|
+
);
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
// SEC-GO-032: Deferred Unlock ordering issue
|
|
546
|
+
{
|
|
547
|
+
id: 'SEC-GO-032',
|
|
548
|
+
category: 'security',
|
|
549
|
+
severity: 'medium',
|
|
550
|
+
confidence: 'suggestion',
|
|
551
|
+
title: 'Deferred Unlock May Cause Extended Lock Hold',
|
|
552
|
+
description: 'Deferring mutex unlock at function start can hold locks longer than necessary.',
|
|
553
|
+
fix: { suggestion: 'Consider explicit unlock after the critical section instead of defer.' },
|
|
554
|
+
check({ files }) {
|
|
555
|
+
return checkGo(this, files,
|
|
556
|
+
/defer\s+\w+\.(?:RUnlock|Unlock)\s*\(\s*\)/,
|
|
557
|
+
);
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
// SEC-GO-033: Listening on 0.0.0.0
|
|
562
|
+
{
|
|
563
|
+
id: 'SEC-GO-033',
|
|
564
|
+
category: 'security',
|
|
565
|
+
severity: 'medium',
|
|
566
|
+
confidence: 'likely',
|
|
567
|
+
title: 'Server Listening on All Interfaces',
|
|
568
|
+
description: 'Binding to 0.0.0.0 exposes the service to all network interfaces, increasing attack surface.',
|
|
569
|
+
fix: { suggestion: 'Bind to 127.0.0.1 for local-only access, or use specific interface addresses.' },
|
|
570
|
+
check({ files }) {
|
|
571
|
+
return checkGo(this, files,
|
|
572
|
+
/net\.Listen\s*\(\s*["']\w+["']\s*,\s*["']0\.0\.0\.0:/,
|
|
573
|
+
);
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
// SEC-GO-034: HTTP server on 0.0.0.0
|
|
578
|
+
{
|
|
579
|
+
id: 'SEC-GO-034',
|
|
580
|
+
category: 'security',
|
|
581
|
+
severity: 'medium',
|
|
582
|
+
confidence: 'likely',
|
|
583
|
+
title: 'HTTP Server Listening on All Interfaces',
|
|
584
|
+
description: 'ListenAndServe on 0.0.0.0 or :port exposes the server on all network interfaces.',
|
|
585
|
+
fix: { suggestion: 'Specify the bind address explicitly, e.g., 127.0.0.1:8080.' },
|
|
586
|
+
check({ files }) {
|
|
587
|
+
return checkGo(this, files,
|
|
588
|
+
/http\.ListenAndServe\s*\(\s*["']:?\d+["']/,
|
|
589
|
+
);
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
// SEC-GO-035: Unsafe pointer usage
|
|
594
|
+
{
|
|
595
|
+
id: 'SEC-GO-035',
|
|
596
|
+
category: 'security',
|
|
597
|
+
severity: 'high',
|
|
598
|
+
confidence: 'likely',
|
|
599
|
+
title: 'Unsafe Pointer Usage',
|
|
600
|
+
description: 'Using unsafe.Pointer bypasses Go type safety and can lead to memory corruption.',
|
|
601
|
+
fix: { suggestion: 'Avoid unsafe.Pointer unless absolutely necessary. Document the safety invariants.' },
|
|
602
|
+
check({ files }) {
|
|
603
|
+
return checkGo(this, files,
|
|
604
|
+
/unsafe\.Pointer\s*\(/,
|
|
605
|
+
);
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
|
|
609
|
+
// SEC-GO-036: Unsafe Sizeof/Offsetof
|
|
610
|
+
{
|
|
611
|
+
id: 'SEC-GO-036',
|
|
612
|
+
category: 'security',
|
|
613
|
+
severity: 'medium',
|
|
614
|
+
confidence: 'suggestion',
|
|
615
|
+
title: 'Unsafe Package Usage',
|
|
616
|
+
description: 'Using the unsafe package circumvents Go memory safety guarantees.',
|
|
617
|
+
fix: { suggestion: 'Minimize unsafe usage. Consider safe alternatives using reflect or encoding/binary.' },
|
|
618
|
+
check({ files }) {
|
|
619
|
+
return checkGo(this, files,
|
|
620
|
+
/unsafe\.(?:Sizeof|Offsetof|Alignof)\s*\(/,
|
|
621
|
+
);
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
// SEC-GO-037: CGo - import "C" usage
|
|
626
|
+
{
|
|
627
|
+
id: 'SEC-GO-037',
|
|
628
|
+
category: 'security',
|
|
629
|
+
severity: 'high',
|
|
630
|
+
confidence: 'suggestion',
|
|
631
|
+
title: 'CGo Usage May Introduce Memory Safety Issues',
|
|
632
|
+
description: 'CGo imports C code that lacks Go memory safety, potentially introducing buffer overflows and other C vulnerabilities.',
|
|
633
|
+
fix: { suggestion: 'Minimize CGo usage. Validate all inputs passed to C functions. Consider pure Go alternatives.' },
|
|
634
|
+
check({ files }) {
|
|
635
|
+
return checkGo(this, files,
|
|
636
|
+
/import\s+"C"/,
|
|
637
|
+
);
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
|
|
641
|
+
// SEC-GO-038: CGo - C.CString without C.free
|
|
642
|
+
{
|
|
643
|
+
id: 'SEC-GO-038',
|
|
644
|
+
category: 'security',
|
|
645
|
+
severity: 'high',
|
|
646
|
+
confidence: 'likely',
|
|
647
|
+
title: 'CGo Memory Leak: CString Without Free',
|
|
648
|
+
description: 'C.CString allocates C memory that must be freed with C.free to prevent memory leaks.',
|
|
649
|
+
fix: { suggestion: 'Always pair C.CString with defer C.free(unsafe.Pointer(cstr)).' },
|
|
650
|
+
check({ files }) {
|
|
651
|
+
return checkGo(this, files,
|
|
652
|
+
/C\.CString\s*\(/,
|
|
653
|
+
);
|
|
654
|
+
},
|
|
655
|
+
},
|
|
656
|
+
|
|
657
|
+
// SEC-GO-039: Unvalidated redirect
|
|
658
|
+
{
|
|
659
|
+
id: 'SEC-GO-039',
|
|
660
|
+
category: 'security',
|
|
661
|
+
severity: 'high',
|
|
662
|
+
confidence: 'likely',
|
|
663
|
+
title: 'Open Redirect',
|
|
664
|
+
description: 'Redirecting to a user-supplied URL without validation enables phishing attacks.',
|
|
665
|
+
fix: { suggestion: 'Validate redirect URLs against an allowlist of trusted domains.' },
|
|
666
|
+
check({ files }) {
|
|
667
|
+
return checkGo(this, files,
|
|
668
|
+
/http\.Redirect\s*\(\s*\w+\s*,\s*\w+\s*,\s*(?:r\.|req\.|c\.(?:Param|Query)|input|user)/,
|
|
669
|
+
);
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
|
|
673
|
+
// SEC-GO-040: Weak random for security
|
|
674
|
+
{
|
|
675
|
+
id: 'SEC-GO-040',
|
|
676
|
+
category: 'security',
|
|
677
|
+
severity: 'high',
|
|
678
|
+
confidence: 'likely',
|
|
679
|
+
title: 'Insecure Random Number Generator',
|
|
680
|
+
description: 'math/rand is not cryptographically secure and should not be used for security-sensitive operations.',
|
|
681
|
+
fix: { suggestion: 'Use crypto/rand for generating tokens, keys, and other security-sensitive values.' },
|
|
682
|
+
check({ files }) {
|
|
683
|
+
return checkGo(this, files,
|
|
684
|
+
/math\/rand/,
|
|
685
|
+
);
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
|
|
689
|
+
// SEC-GO-041: Missing CSRF protection
|
|
690
|
+
{
|
|
691
|
+
id: 'SEC-GO-041',
|
|
692
|
+
category: 'security',
|
|
693
|
+
severity: 'high',
|
|
694
|
+
confidence: 'suggestion',
|
|
695
|
+
title: 'Missing CSRF Protection',
|
|
696
|
+
description: 'POST/PUT/DELETE handlers without CSRF token validation are vulnerable to cross-site request forgery.',
|
|
697
|
+
fix: { suggestion: 'Use gorilla/csrf or a similar CSRF middleware.' },
|
|
698
|
+
check({ files }) {
|
|
699
|
+
return checkGo(this, files,
|
|
700
|
+
/\.(?:POST|PUT|DELETE|PATCH)\s*\(\s*["']/,
|
|
701
|
+
);
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
|
|
705
|
+
// SEC-GO-042: Cookie without Secure flag
|
|
706
|
+
{
|
|
707
|
+
id: 'SEC-GO-042',
|
|
708
|
+
category: 'security',
|
|
709
|
+
severity: 'high',
|
|
710
|
+
confidence: 'likely',
|
|
711
|
+
title: 'Cookie Without Secure Flag',
|
|
712
|
+
description: 'Cookies without the Secure flag can be sent over unencrypted HTTP connections.',
|
|
713
|
+
fix: { suggestion: 'Set Secure: true on cookies, especially session cookies.' },
|
|
714
|
+
check({ files }) {
|
|
715
|
+
return checkGo(this, files,
|
|
716
|
+
/http\.Cookie\s*\{[^}]*Secure\s*:\s*false/,
|
|
717
|
+
);
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
|
|
721
|
+
// SEC-GO-043: Cookie without HttpOnly flag
|
|
722
|
+
{
|
|
723
|
+
id: 'SEC-GO-043',
|
|
724
|
+
category: 'security',
|
|
725
|
+
severity: 'medium',
|
|
726
|
+
confidence: 'likely',
|
|
727
|
+
title: 'Cookie Without HttpOnly Flag',
|
|
728
|
+
description: 'Cookies without HttpOnly can be accessed by JavaScript, increasing XSS impact.',
|
|
729
|
+
fix: { suggestion: 'Set HttpOnly: true on session and authentication cookies.' },
|
|
730
|
+
check({ files }) {
|
|
731
|
+
return checkGo(this, files,
|
|
732
|
+
/http\.Cookie\s*\{[^}]*HttpOnly\s*:\s*false/,
|
|
733
|
+
);
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
// SEC-GO-044: SQL injection in GORM raw query
|
|
738
|
+
{
|
|
739
|
+
id: 'SEC-GO-044',
|
|
740
|
+
category: 'security',
|
|
741
|
+
severity: 'critical',
|
|
742
|
+
confidence: 'likely',
|
|
743
|
+
title: 'SQL Injection in GORM Raw Query',
|
|
744
|
+
description: 'Using db.Raw with fmt.Sprintf allows SQL injection through GORM.',
|
|
745
|
+
fix: { suggestion: 'Use db.Raw with ? placeholders and pass values as additional arguments.' },
|
|
746
|
+
check({ files }) {
|
|
747
|
+
return checkGo(this, files,
|
|
748
|
+
/db\.Raw\s*\(\s*fmt\.Sprintf/,
|
|
749
|
+
);
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
|
|
753
|
+
// SEC-GO-045: Logging sensitive data
|
|
754
|
+
{
|
|
755
|
+
id: 'SEC-GO-045',
|
|
756
|
+
category: 'security',
|
|
757
|
+
severity: 'medium',
|
|
758
|
+
confidence: 'likely',
|
|
759
|
+
title: 'Sensitive Data in Log Output',
|
|
760
|
+
description: 'Logging passwords, tokens, or keys can expose sensitive data in log files.',
|
|
761
|
+
fix: { suggestion: 'Redact sensitive fields before logging. Use structured logging with field filtering.' },
|
|
762
|
+
check({ files }) {
|
|
763
|
+
return checkGo(this, files,
|
|
764
|
+
/log\.(?:Print|Fatal|Panic|Info|Debug|Warn|Error)(?:f|ln)?\s*\([^)]*(?:password|token|secret|key|credential)/i,
|
|
765
|
+
);
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
|
|
769
|
+
// SEC-GO-046: Uncontrolled resource consumption - no timeout
|
|
770
|
+
{
|
|
771
|
+
id: 'SEC-GO-046',
|
|
772
|
+
category: 'security',
|
|
773
|
+
severity: 'medium',
|
|
774
|
+
confidence: 'suggestion',
|
|
775
|
+
title: 'HTTP Server Without Timeouts',
|
|
776
|
+
description: 'HTTP server without read/write timeouts is vulnerable to slowloris and resource exhaustion attacks.',
|
|
777
|
+
fix: { suggestion: 'Set ReadTimeout, WriteTimeout, and IdleTimeout on http.Server.' },
|
|
778
|
+
check({ files }) {
|
|
779
|
+
return checkGo(this, files,
|
|
780
|
+
/&http\.Server\s*\{[^}]*(?:Addr|Handler)[^}]*\}/,
|
|
781
|
+
);
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
|
|
785
|
+
// SEC-GO-047: Eval-like dynamic code execution
|
|
786
|
+
{
|
|
787
|
+
id: 'SEC-GO-047',
|
|
788
|
+
category: 'security',
|
|
789
|
+
severity: 'high',
|
|
790
|
+
confidence: 'likely',
|
|
791
|
+
title: 'Dynamic Code via Plugin Loading',
|
|
792
|
+
description: 'Loading plugins dynamically from user-controlled paths can execute arbitrary code.',
|
|
793
|
+
fix: { suggestion: 'Validate plugin paths against an allowlist. Sign plugins and verify signatures.' },
|
|
794
|
+
check({ files }) {
|
|
795
|
+
return checkGo(this, files,
|
|
796
|
+
/plugin\.Open\s*\(\s*(?:r\.|req\.|params\.|input|user)/,
|
|
797
|
+
);
|
|
798
|
+
},
|
|
799
|
+
},
|
|
800
|
+
|
|
801
|
+
// SEC-GO-048: Embedded credentials in struct tags
|
|
802
|
+
{
|
|
803
|
+
id: 'SEC-GO-048',
|
|
804
|
+
category: 'security',
|
|
805
|
+
severity: 'medium',
|
|
806
|
+
confidence: 'suggestion',
|
|
807
|
+
title: 'Sensitive Field Without JSON Omit',
|
|
808
|
+
description: 'Struct fields containing passwords or secrets without json:"-" may be serialized in API responses.',
|
|
809
|
+
fix: { suggestion: 'Add `json:"-"` tag to sensitive fields to exclude them from JSON serialization.' },
|
|
810
|
+
check({ files }) {
|
|
811
|
+
return checkGo(this, files,
|
|
812
|
+
/(?:Password|Secret|Token|Key)\s+string\s+`json:"(?!-)(?:\w+)"/i,
|
|
813
|
+
);
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
|
|
817
|
+
// SEC-GO-049: Missing rate limiting
|
|
818
|
+
{
|
|
819
|
+
id: 'SEC-GO-049',
|
|
820
|
+
category: 'security',
|
|
821
|
+
severity: 'medium',
|
|
822
|
+
confidence: 'suggestion',
|
|
823
|
+
title: 'Missing Rate Limiting on HTTP Handler',
|
|
824
|
+
description: 'HTTP handlers without rate limiting are vulnerable to brute force and denial of service attacks.',
|
|
825
|
+
fix: { suggestion: 'Use golang.org/x/time/rate or a middleware like tollbooth for rate limiting.' },
|
|
826
|
+
check({ files }) {
|
|
827
|
+
return checkGo(this, files,
|
|
828
|
+
/func\s+\w+\s*\(\s*w\s+http\.ResponseWriter\s*,\s*r\s+\*http\.Request\s*\)/,
|
|
829
|
+
);
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
|
|
833
|
+
// SEC-GO-050: XML external entity injection
|
|
834
|
+
{
|
|
835
|
+
id: 'SEC-GO-050',
|
|
836
|
+
category: 'security',
|
|
837
|
+
severity: 'high',
|
|
838
|
+
confidence: 'likely',
|
|
839
|
+
title: 'XML External Entity (XXE) Processing',
|
|
840
|
+
description: 'Using xml.NewDecoder without disabling external entities may allow XXE attacks.',
|
|
841
|
+
fix: { suggestion: 'Use xml.NewDecoder with a custom entity resolver or consider JSON for untrusted input.' },
|
|
842
|
+
check({ files }) {
|
|
843
|
+
return checkGo(this, files,
|
|
844
|
+
/xml\.NewDecoder\s*\(\s*(?:r\.|req\.|c\.Request)/,
|
|
845
|
+
);
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
];
|
|
849
|
+
|
|
850
|
+
export default rules;
|