narvy-cli 1.0.0__py3-none-any.whl

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.
Files changed (93) hide show
  1. narvy/__init__.py +3 -0
  2. narvy/android/__init__.py +0 -0
  3. narvy/android/source_analyzer.py +268 -0
  4. narvy/android/split_bundle.py +475 -0
  5. narvy/android_rule_context.py +196 -0
  6. narvy/apk_memory_preflight.py +248 -0
  7. narvy/auth.py +40 -0
  8. narvy/ci_templates/bitbucket.yml +25 -0
  9. narvy/ci_templates/github.yml +60 -0
  10. narvy/ci_templates/gitlab.yml +32 -0
  11. narvy/cloud/__init__.py +0 -0
  12. narvy/cloud/aws_scan.py +1188 -0
  13. narvy/comment_filter.py +134 -0
  14. narvy/crypto_taint_lite.py +82 -0
  15. narvy/decompiler.py +337 -0
  16. narvy/doctor.py +244 -0
  17. narvy/host/__init__.py +0 -0
  18. narvy/host/audit.py +157 -0
  19. narvy/host/host_knowledge.py +982 -0
  20. narvy/host/lynis_bootstrap.py +400 -0
  21. narvy/host/lynis_parser.py +358 -0
  22. narvy/host/narvy_checks.py +789 -0
  23. narvy/host/report.py +69 -0
  24. narvy/host/ssh_exec.py +219 -0
  25. narvy/ios/__init__.py +0 -0
  26. narvy/ios/binary_analyzer.py +1201 -0
  27. narvy/ios/plist_checks.py +249 -0
  28. narvy/ios/source_analyzer.py +301 -0
  29. narvy/ios/third_party_filter.py +242 -0
  30. narvy/ios/trust_all_context.py +66 -0
  31. narvy/main.py +1983 -0
  32. narvy/native_hardening.py +503 -0
  33. narvy/reporter.py +151 -0
  34. narvy/rule_engine.py +57 -0
  35. narvy/rules/android/config.yml +77 -0
  36. narvy/rules/android/crypto.yml +34 -0
  37. narvy/rules/android/secrets.yml +161 -0
  38. narvy/rules/android/storage.yml +24 -0
  39. narvy/rules/android/webview.yml +23 -0
  40. narvy/rules/android.yml +219 -0
  41. narvy/rules/ios/objc/crypto.yml +56 -0
  42. narvy/rules/ios/objc/network.yml +67 -0
  43. narvy/rules/ios/objc/secrets.yml +79 -0
  44. narvy/rules/ios/objc/storage.yml +45 -0
  45. narvy/rules/ios/objc/webview.yml +45 -0
  46. narvy/rules/ios_swift.yml +327 -0
  47. narvy/rules/web/go.yml +2837 -0
  48. narvy/rules/web/java.yml +1576 -0
  49. narvy/rules/web/javascript.yml +3683 -0
  50. narvy/rules/web/kotlin.yml +413 -0
  51. narvy/rules/web/local/csharp_narvy/config.yml +61 -0
  52. narvy/rules/web/local/csharp_narvy/crypto.yml +64 -0
  53. narvy/rules/web/local/csharp_narvy/deserialization.yml +59 -0
  54. narvy/rules/web/local/csharp_narvy/injection.yml +122 -0
  55. narvy/rules/web/local/csharp_narvy/xxe.yml +48 -0
  56. narvy/rules/web/local/java_narvy/auth_jwt.yml +134 -0
  57. narvy/rules/web/local/java_narvy/deserialization.yml +108 -0
  58. narvy/rules/web/local/java_narvy/mybatis.yml +39 -0
  59. narvy/rules/web/local/java_narvy/snakeyaml.yml +34 -0
  60. narvy/rules/web/local/java_narvy/spring_authz.yml +32 -0
  61. narvy/rules/web/local/java_narvy/spring_config.yml +67 -0
  62. narvy/rules/web/local/java_narvy/spring_hardening.yml +291 -0
  63. narvy/rules/web/local/java_narvy/sqli.yml +235 -0
  64. narvy/rules/web/local/java_narvy/xxe.yml +212 -0
  65. narvy/rules/web/php.yml +1644 -0
  66. narvy/rules/web/python.yml +3967 -0
  67. narvy/rules/web/ruby.yml +703 -0
  68. narvy/rules/web/rust.yml +258 -0
  69. narvy/rules/web/secrets.yml +1420 -0
  70. narvy/rules/web/secrets_supplement.yml +383 -0
  71. narvy/sca/__init__.py +1 -0
  72. narvy/sca/android_deps.py +349 -0
  73. narvy/sca/ios_deps.py +578 -0
  74. narvy/sca/osv_client.py +617 -0
  75. narvy/sca/web_deps.py +955 -0
  76. narvy/scope_config.py +195 -0
  77. narvy/semgrep_engine.py +219 -0
  78. narvy/stack_protector_evidence.py +96 -0
  79. narvy/third_party_filter.py +211 -0
  80. narvy/uploader.py +92 -0
  81. narvy/weak_prng_context.py +270 -0
  82. narvy/web/__init__.py +0 -0
  83. narvy/web/nuclei_binary.py +105 -0
  84. narvy/web/scan_blocklist.py +96 -0
  85. narvy/web/scanner.py +842 -0
  86. narvy/web/source_analyzer.py +714 -0
  87. narvy/web/ssrf_guard.py +374 -0
  88. narvy_cli-1.0.0.dist-info/METADATA +165 -0
  89. narvy_cli-1.0.0.dist-info/RECORD +93 -0
  90. narvy_cli-1.0.0.dist-info/WHEEL +5 -0
  91. narvy_cli-1.0.0.dist-info/entry_points.txt +2 -0
  92. narvy_cli-1.0.0.dist-info/licenses/LICENSE +202 -0
  93. narvy_cli-1.0.0.dist-info/top_level.txt +1 -0
narvy/rules/web/go.yml ADDED
@@ -0,0 +1,2837 @@
1
+ # Go security rules, authored by Narvy.
2
+ # Injection, path traversal, SSRF, CORS, cookies, weak crypto, secrets.
3
+
4
+ rules:
5
+ - id: narvy.go.auth.insecure-string-compare-secret.insecure-string-compare-secret
6
+ message: A secret/token/signature/HMAC comparison uses `==` (or `strings.Compare`) instead of a constant-time comparison.
7
+ `==` short-circuits on the first mismatched byte, so response timing leaks how many leading bytes were correct - a timing
8
+ side-channel an attacker can use to recover the secret byte-by-byte (CWE-208). Use `subtle.ConstantTimeCompare()` or `hmac.Equal()`.
9
+ severity: WARNING
10
+ languages:
11
+ - go
12
+ metadata:
13
+ category: security
14
+ cwe:
15
+ - 'CWE-208: Observable Timing Discrepancy'
16
+ owasp:
17
+ - A07:2021 - Identification and Authentication Failures
18
+ subcategory:
19
+ - vuln
20
+ likelihood: LOW
21
+ impact: MEDIUM
22
+ confidence: LOW
23
+ technology:
24
+ - go
25
+ patterns:
26
+ - pattern-either:
27
+ - pattern: $SIG == $EXPECTED
28
+ - metavariable-regex:
29
+ metavariable: $SIG
30
+ regex: (?i).*(sig|signature|hmac|mac|token|secret)$
31
+ - pattern-not: $SIG == nil
32
+ - pattern-not: nil == $SIG
33
+ - pattern-not: $SIG == ""
34
+ - pattern-not: '"" == $SIG'
35
+ - metavariable-pattern:
36
+ metavariable: $EXPECTED
37
+ patterns:
38
+ - pattern-not: nil
39
+ - pattern-not: '""'
40
+ - id: narvy.go.auth.cookie-httponly-unset.cookie-httponly-unset
41
+ message: 'A session cookie is set (`http.SetCookie` with a name containing `session`) without `HttpOnly: true`. Without
42
+ it, JavaScript on the page (including an attacker''s, via any XSS) can read the cookie and exfiltrate it (CWE-1004). Set
43
+ `HttpOnly: true` on every session cookie.'
44
+ severity: WARNING
45
+ languages:
46
+ - go
47
+ metadata:
48
+ category: security
49
+ cwe:
50
+ - 'CWE-1004: Sensitive Cookie Without ''HttpOnly'' Flag'
51
+ owasp:
52
+ - A05:2021 - Security Misconfiguration
53
+ subcategory:
54
+ - vuln
55
+ likelihood: LOW
56
+ impact: MEDIUM
57
+ confidence: LOW
58
+ technology:
59
+ - go
60
+ patterns:
61
+ - pattern: 'http.SetCookie($W, &http.Cookie{..., Name: $NAME, ...})
62
+
63
+ '
64
+ - pattern-not: 'http.SetCookie($W, &http.Cookie{..., HttpOnly: true, ...})
65
+
66
+ '
67
+ - metavariable-regex:
68
+ metavariable: $NAME
69
+ regex: (?i).*session.*
70
+ - id: narvy.go.auth.cookie-secure-unset.cookie-secure-unset
71
+ message: 'A session cookie is set without `Secure: true`. Without it, the browser will also send the cookie over a plaintext
72
+ `http://` connection, letting a network attacker capture it (CWE-614). Set `Secure: true` on every session cookie in production.'
73
+ severity: WARNING
74
+ languages:
75
+ - go
76
+ metadata:
77
+ category: security
78
+ cwe:
79
+ - 'CWE-614: Sensitive Cookie in HTTPS Session Without ''Secure'' Attribute'
80
+ owasp:
81
+ - A05:2021 - Security Misconfiguration
82
+ subcategory:
83
+ - vuln
84
+ likelihood: LOW
85
+ impact: MEDIUM
86
+ confidence: LOW
87
+ technology:
88
+ - go
89
+ patterns:
90
+ - pattern: 'http.SetCookie($W, &http.Cookie{..., Name: $NAME, ...})
91
+
92
+ '
93
+ - pattern-not: 'http.SetCookie($W, &http.Cookie{..., Secure: true, ...})
94
+
95
+ '
96
+ - metavariable-regex:
97
+ metavariable: $NAME
98
+ regex: (?i).*session.*
99
+ - id: narvy.go.auth.cookie-samesite-none-mode-set.cookie-samesite-none-mode-set
100
+ message: 'A session cookie is set with `SameSite: http.SameSiteNoneMode`. `None` disables the CSRF-mitigating same-site
101
+ restriction entirely, sending the cookie on every cross-site request (CWE-352). Use `http.SameSiteLaxMode` (or `Strict`
102
+ for the most sensitive flows) unless the cookie genuinely needs to be sent from a third-party context, and pair `None`
103
+ with a separate CSRF token if it is required.'
104
+ severity: WARNING
105
+ languages:
106
+ - go
107
+ metadata:
108
+ category: security
109
+ cwe:
110
+ - 'CWE-352: Cross-Site Request Forgery (CSRF)'
111
+ owasp:
112
+ - A01:2021 - Broken Access Control
113
+ subcategory:
114
+ - vuln
115
+ likelihood: LOW
116
+ impact: MEDIUM
117
+ confidence: MEDIUM
118
+ technology:
119
+ - go
120
+ pattern: 'http.SetCookie($W, &http.Cookie{..., SameSite: http.SameSiteNoneMode, ...})
121
+
122
+ '
123
+ - id: narvy-go-cmdi-splat-args
124
+ patterns:
125
+ - pattern-either:
126
+ - pattern: 'exec.Command($ARGS[$IDX], ...)
127
+
128
+ '
129
+ - pattern: 'exec.CommandContext($CTX, $ARGS[$IDX], ...)
130
+
131
+ '
132
+ - pattern-not: 'exec.Command("...", ...)
133
+
134
+ '
135
+ - pattern-not: 'exec.CommandContext($CTX, "...", ...)
136
+
137
+ '
138
+ message: 'Command injection: `exec.Command` first argument is an indexed slice expression (e.g. `args[0]`), which means
139
+ the executable path itself is user-controlled. Even with subsequent arguments fixed, the attacker can run arbitrary binaries.
140
+ Use an allow-list of binary names.'
141
+ metadata:
142
+ cwe:
143
+ - 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
144
+ owasp:
145
+ - A03:2021 - Injection
146
+ category: security
147
+ technology:
148
+ - go
149
+ confidence: HIGH
150
+ subcategory:
151
+ - vuln
152
+ likelihood: HIGH
153
+ impact: HIGH
154
+ severity: ERROR
155
+ languages:
156
+ - go
157
+ - id: narvy-go-syscall-exec-tainted
158
+ patterns:
159
+ - pattern-either:
160
+ - pattern: syscall.Exec($BIN, ...)
161
+ - pattern: syscall.ForkExec($BIN, ...)
162
+ - pattern: os.StartProcess($BIN, ...)
163
+ - pattern-not: syscall.Exec("...", ...)
164
+ - pattern-not: syscall.ForkExec("...", ...)
165
+ - pattern-not: os.StartProcess("...", ...)
166
+ message: Low-level process execution (`syscall.Exec` / `os.StartProcess`) with a non-literal binary path. If the path comes
167
+ from user input this is a command injection. Validate with an allow-list of approved binaries.
168
+ metadata:
169
+ cwe:
170
+ - 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
171
+ owasp:
172
+ - A03:2021 - Injection
173
+ category: security
174
+ technology:
175
+ - go
176
+ confidence: MEDIUM
177
+ subcategory:
178
+ - audit
179
+ likelihood: MEDIUM
180
+ impact: HIGH
181
+ severity: ERROR
182
+ languages:
183
+ - go
184
+ - id: narvy-go-cmdi-shell-flag
185
+ patterns:
186
+ - pattern-either:
187
+ - pattern: 'exec.Command("sh", "-c", $X)
188
+
189
+ '
190
+ - pattern: 'exec.Command("bash", "-c", $X)
191
+
192
+ '
193
+ - pattern: 'exec.Command("/bin/sh", "-c", $X)
194
+
195
+ '
196
+ - pattern: 'exec.Command("/bin/bash", "-c", $X)
197
+
198
+ '
199
+ - pattern: 'exec.CommandContext($CTX, "sh", "-c", $X)
200
+
201
+ '
202
+ - pattern: 'exec.CommandContext($CTX, "bash", "-c", $X)
203
+
204
+ '
205
+ - pattern-not: 'exec.Command($S, "-c", "...")
206
+
207
+ '
208
+ - pattern-not: 'exec.CommandContext($CTX, $S, "-c", "...")
209
+
210
+ '
211
+ message: Shell expansion via `sh -c` / `bash -c` with a non-literal command string. Even properly-quoted user input is interpreted
212
+ by the shell (subshells, redirection, env expansion). Invoke the target binary directly without `-c` and pass arguments
213
+ as a slice.
214
+ metadata:
215
+ cwe:
216
+ - 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
217
+ owasp:
218
+ - A03:2021 - Injection
219
+ category: security
220
+ technology:
221
+ - go
222
+ confidence: HIGH
223
+ subcategory:
224
+ - vuln
225
+ likelihood: HIGH
226
+ impact: HIGH
227
+ severity: ERROR
228
+ languages:
229
+ - go
230
+ - id: narvy.go.code-injection.embedded-js-run-non-literal.embedded-js-run-non-literal
231
+ message: An embedded JavaScript interpreter (goja / otto) executes a non-literal script string. If the script text is attacker-
232
+ influenced, this is full code execution inside your Go process (CWE-94) - these interpreters are not a real security sandbox
233
+ against a determined attacker and have full access to whatever Go functions you registered on the runtime. Never run request-derived
234
+ text as a script.
235
+ severity: ERROR
236
+ languages:
237
+ - go
238
+ metadata:
239
+ cwe:
240
+ - 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
241
+ owasp:
242
+ - A03:2021 - Injection
243
+ category: security
244
+ subcategory:
245
+ - vuln
246
+ technology:
247
+ - go
248
+ - goja
249
+ - otto
250
+ likelihood: LOW
251
+ impact: HIGH
252
+ confidence: MEDIUM
253
+ vulnerability_class:
254
+ - Code Injection
255
+ patterns:
256
+ - pattern-either:
257
+ - pattern: $VM.RunString($X)
258
+ - pattern: $VM.Run($X)
259
+ - pattern-not: $VM.RunString("...")
260
+ - pattern-not: $VM.Run("...")
261
+ - metavariable-regex:
262
+ metavariable: $VM
263
+ regex: (?i)(vm|runtime|interp|js|script|otto)
264
+ - id: narvy.go.code-injection.plugin-open-non-literal-path.plugin-open-non-literal-path
265
+ message: '`plugin.Open()` loads a Go shared-object (.so) plugin from a non-literal path. Loading a plugin runs its `init()`
266
+ functions immediately with the full privileges of your process - there is no sandboxing. If the path is attacker-influenced
267
+ (even indirectly, e.g. a filename derived from a request), this is remote code execution (CWE-94). Load plugins only from
268
+ a fixed, trusted, non-writable directory.'
269
+ severity: ERROR
270
+ languages:
271
+ - go
272
+ metadata:
273
+ cwe:
274
+ - 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
275
+ owasp:
276
+ - A08:2021 - Software and Data Integrity Failures
277
+ category: security
278
+ subcategory:
279
+ - vuln
280
+ technology:
281
+ - go
282
+ likelihood: LOW
283
+ impact: HIGH
284
+ confidence: MEDIUM
285
+ vulnerability_class:
286
+ - Code Injection
287
+ patterns:
288
+ - pattern: plugin.Open($PATH)
289
+ - pattern-not: plugin.Open("...")
290
+ - id: narvy.go.code-injection.reflect-methodbyname-non-literal.reflect-methodbyname-non-literal
291
+ message: '`reflect.ValueOf(...).MethodByName($NAME)` looks up and invokes a method whose name is not a literal. If `$NAME`
292
+ is influenced by request data, an attacker can call any exported method on the target object - including ones never meant
293
+ to be reachable from outside (unsafe reflection / unintended dispatch, CWE-470). Use an explicit allow-list mapping request
294
+ values to a fixed set of permitted method names.'
295
+ severity: WARNING
296
+ languages:
297
+ - go
298
+ metadata:
299
+ cwe:
300
+ - 'CWE-470: Use of Externally-Controlled Input to Select Classes or Code (''Unsafe Reflection'')'
301
+ owasp:
302
+ - A03:2021 - Injection
303
+ category: security
304
+ subcategory:
305
+ - audit
306
+ technology:
307
+ - go
308
+ - reflect
309
+ likelihood: LOW
310
+ impact: HIGH
311
+ confidence: LOW
312
+ vulnerability_class:
313
+ - Code Injection
314
+ patterns:
315
+ - pattern: reflect.ValueOf($OBJ).MethodByName($NAME)
316
+ - pattern-not: reflect.ValueOf($OBJ).MethodByName("...")
317
+ - id: narvy.go.cmdi.dangerous-exec-command.dangerous-exec-command
318
+ message: '`exec.Command()` / `exec.CommandContext()` called with a command or argument built from request-derived data.
319
+ `exec.Command` does not invoke a shell, so classic `;`/`&&` shell metacharacter injection does not apply directly - but
320
+ if the *binary name itself* or an argument that changes program behavior (e.g. a flag) is attacker-controlled, this is
321
+ still command injection / argument injection (CWE-78/CWE-88). Validate the binary against a fixed allow-list and treat
322
+ every argument as untrusted data, never as a flag.'
323
+ severity: WARNING
324
+ languages:
325
+ - go
326
+ metadata:
327
+ category: security
328
+ cwe:
329
+ - 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
330
+ - 'CWE-88: Argument Injection or Modification'
331
+ owasp:
332
+ - A03:2021 - Injection
333
+ subcategory:
334
+ - vuln
335
+ likelihood: MEDIUM
336
+ impact: HIGH
337
+ confidence: LOW
338
+ technology:
339
+ - go
340
+ patterns:
341
+ - pattern-either:
342
+ - pattern: exec.Command($BIN, ...)
343
+ - pattern: exec.CommandContext($CTX, $BIN, ...)
344
+ - pattern-not: exec.Command("...", ...)
345
+ - pattern-not: exec.CommandContext($CTX, "...", ...)
346
+ - id: narvy.go.reflect.reflect-methodby-name-tainted.reflect-methodby-name-tainted
347
+ message: '`reflect.ValueOf(...).MethodByName()` (or `.FieldByName()`) called with a name taken from request data. This lets
348
+ an attacker invoke any exported method reachable through the reflected value by naming it in the request - the same class
349
+ of risk as PHP''s tainted `call_user_func()` (CWE-470). Restrict the name to a fixed allow-list keyed by a validated token,
350
+ never the raw input.'
351
+ severity: WARNING
352
+ languages:
353
+ - go
354
+ metadata:
355
+ category: security
356
+ cwe:
357
+ - 'CWE-470: Use of Externally-Controlled Input to Select Classes or Code (''Unsafe Reflection'')'
358
+ owasp:
359
+ - A03:2021 - Injection
360
+ subcategory:
361
+ - vuln
362
+ likelihood: LOW
363
+ impact: HIGH
364
+ confidence: LOW
365
+ technology:
366
+ - go
367
+ patterns:
368
+ - pattern-either:
369
+ - pattern: reflect.ValueOf($OBJ).MethodByName($NAME)
370
+ - pattern: reflect.ValueOf($OBJ).FieldByName($NAME)
371
+ - pattern-not: reflect.ValueOf($OBJ).MethodByName("...")
372
+ - pattern-not: reflect.ValueOf($OBJ).FieldByName("...")
373
+ - id: narvy.go.plugin.plugin-open-tainted-path.plugin-open-tainted-path
374
+ message: '`plugin.Open()` called with a path built from request data. Go plugins are arbitrary native code loaded and executed
375
+ in-process - if an attacker can influence which `.so` file gets opened (directly, or via a directory they can also write
376
+ to), this is remote code execution (CWE-94). Load plugins only from a fixed, non-writable-by-request path.'
377
+ severity: ERROR
378
+ languages:
379
+ - go
380
+ metadata:
381
+ category: security
382
+ cwe:
383
+ - 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
384
+ owasp:
385
+ - A03:2021 - Injection
386
+ subcategory:
387
+ - vuln
388
+ likelihood: LOW
389
+ impact: HIGH
390
+ confidence: MEDIUM
391
+ technology:
392
+ - go
393
+ patterns:
394
+ - pattern: plugin.Open($PATH)
395
+ - pattern-not: plugin.Open("...")
396
+ - id: narvy.go.misc.use-of-unsafe-block.use-of-unsafe-block
397
+ message: '`unsafe.$FUNC(...)` call (Pointer conversion, Slice/SliceData/String/StringData zero-copy cast, or Add/Sizeof/Alignof/Offsetof).
398
+ The `unsafe` package steps outside Go''s type and memory safety guarantees, so the compiler no longer checks size or
399
+ layout for you. Manual pointer arithmetic on a size that is not a compile-time constant or independently validated can
400
+ read or write past the intended buffer (CWE-119/CWE-787). Confirm every offset and length is bounded and trusted; prefer
401
+ `encoding/binary` for request-derived byte layouts, and avoid `unsafe` entirely unless a benchmarked hot path requires it.'
402
+ severity: INFO
403
+ languages:
404
+ - go
405
+ metadata:
406
+ category: security
407
+ cwe:
408
+ - 'CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer'
409
+ owasp:
410
+ - A06:2021 - Vulnerable and Outdated Components
411
+ subcategory:
412
+ - audit
413
+ likelihood: LOW
414
+ impact: MEDIUM
415
+ confidence: LOW
416
+ technology:
417
+ - go
418
+ paths:
419
+ exclude:
420
+ - '*.pb.go'
421
+ - '*_generated.go'
422
+ - '*.gen.go'
423
+ - zz_generated*.go
424
+ - zsyscall_*.go
425
+ - '*_string.go'
426
+ pattern: unsafe.$FUNC(...)
427
+ - id: narvy.go.cors.wildcard-allow-origin.wildcard-allow-origin
428
+ message: '`Access-Control-Allow-Origin` header set to `*`. This lets any origin read the response via a cross-origin XHR/fetch
429
+ - safe only for genuinely public, non-authenticated endpoints. If the endpoint uses cookies/sessions or returns non-public
430
+ data, this exposes it to any site the victim visits (CWE-942). Echo back a validated origin from an allow-list instead
431
+ of a wildcard.'
432
+ severity: WARNING
433
+ languages:
434
+ - go
435
+ metadata:
436
+ category: security
437
+ cwe:
438
+ - 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
439
+ owasp:
440
+ - A05:2021 - Security Misconfiguration
441
+ subcategory:
442
+ - vuln
443
+ likelihood: MEDIUM
444
+ impact: MEDIUM
445
+ confidence: MEDIUM
446
+ technology:
447
+ - go
448
+ paths:
449
+ exclude:
450
+ - '**/*_test.go'
451
+ patterns:
452
+ - pattern-either:
453
+ - pattern: $W.Header().Set("Access-Control-Allow-Origin", "*")
454
+ - pattern: $W.Header().Add("Access-Control-Allow-Origin", "*")
455
+ - pattern: $CTX.Header("Access-Control-Allow-Origin", "*")
456
+ - id: narvy.go.cors.reflected-origin-header.reflected-origin-header
457
+ message: The incoming `Origin` request header is echoed straight back as `Access-Control-Allow-Origin` with no allow-list
458
+ check. This grants every origin CORS access - functionally equivalent to a wildcard but easy to miss in review because
459
+ there is no literal `*` (CWE-942). Validate the origin against a fixed allow-list before echoing it.
460
+ severity: ERROR
461
+ languages:
462
+ - go
463
+ metadata:
464
+ category: security
465
+ cwe:
466
+ - 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
467
+ owasp:
468
+ - A05:2021 - Security Misconfiguration
469
+ subcategory:
470
+ - vuln
471
+ likelihood: MEDIUM
472
+ impact: HIGH
473
+ confidence: MEDIUM
474
+ technology:
475
+ - go
476
+ patterns:
477
+ - pattern-either:
478
+ - pattern: $W.Header().Set("Access-Control-Allow-Origin", $R.Header.Get("Origin"))
479
+ - pattern: $W.Header().Set("Access-Control-Allow-Origin", $ORIGIN)
480
+ - pattern-inside: '$ORIGIN := $R.Header.Get("Origin")
481
+
482
+ ...
483
+
484
+ '
485
+ - id: narvy.go.cors.wildcard-with-credentials.wildcard-with-credentials
486
+ message: '`Access-Control-Allow-Credentials: true` set alongside a wildcard/ reflected `Access-Control-Allow-Origin`. Per
487
+ the Fetch spec browsers reject the literal combination of `*` with credentials, so code relying on this is usually reflecting
488
+ the Origin header dynamically instead - which grants every origin authenticated, cookie-bearing cross-site access (CWE-942).
489
+ Restrict `Allow-Origin` to a fixed allow-list when `Allow-Credentials` is `true`.'
490
+ severity: ERROR
491
+ languages:
492
+ - go
493
+ metadata:
494
+ category: security
495
+ cwe:
496
+ - 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
497
+ owasp:
498
+ - A05:2021 - Security Misconfiguration
499
+ subcategory:
500
+ - vuln
501
+ likelihood: LOW
502
+ impact: HIGH
503
+ confidence: LOW
504
+ technology:
505
+ - go
506
+ patterns:
507
+ - pattern-either:
508
+ - pattern: '$W.Header().Set("Access-Control-Allow-Credentials", "true")
509
+
510
+ ...
511
+
512
+ $W.Header().Set("Access-Control-Allow-Origin", $R.Header.Get("Origin"))
513
+
514
+ '
515
+ - pattern: '$W.Header().Set("Access-Control-Allow-Origin", $R.Header.Get("Origin"))
516
+
517
+ ...
518
+
519
+ $W.Header().Set("Access-Control-Allow-Credentials", "true")
520
+
521
+ '
522
+ - id: narvy.go.cors.gin-permissive-config.gin-permissive-config
523
+ message: '`cors.Config{AllowAllOrigins: true}` (gin-contrib/cors) combined with `AllowCredentials: true`. This grants any
524
+ origin authenticated, cookie-bearing cross-site access to the API - a full CORS bypass (CWE-942). Set `AllowOrigins` to
525
+ a fixed list of trusted origins instead of allowing all.'
526
+ severity: ERROR
527
+ languages:
528
+ - go
529
+ metadata:
530
+ category: security
531
+ cwe:
532
+ - 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
533
+ owasp:
534
+ - A05:2021 - Security Misconfiguration
535
+ subcategory:
536
+ - vuln
537
+ likelihood: LOW
538
+ impact: HIGH
539
+ confidence: MEDIUM
540
+ technology:
541
+ - go
542
+ pattern: 'cors.Config{..., AllowAllOrigins: true, ..., AllowCredentials: true, ...}
543
+
544
+ '
545
+ - id: narvy.go.crypto.hardcoded-env-fallback-to-crypto.hardcoded-env-fallback-to-crypto
546
+ message: 'Hardcoded fallback secret ''$LITERAL'' is used when the environment variable is unset, and is then fed into a
547
+ cryptographic primitive (HMAC / AES / JWT signing). If the env var is missing in any deployment, the key reduces to a
548
+ public constant. Fix: fail fast at startup with `log.Fatal("$ENV must be set")` if the env var is empty - never silently
549
+ fall back to a literal.'
550
+ severity: ERROR
551
+ languages:
552
+ - go
553
+ metadata:
554
+ cwe:
555
+ - 'CWE-321: Use of Hard-coded Cryptographic Key'
556
+ - 'CWE-798: Use of Hard-coded Credentials'
557
+ owasp:
558
+ - A02:2021 - Cryptographic Failures
559
+ - A07:2021 - Identification and Authentication Failures
560
+ category: security
561
+ subcategory:
562
+ - vuln
563
+ technology:
564
+ - go
565
+ - golang
566
+ likelihood: HIGH
567
+ impact: HIGH
568
+ confidence: HIGH
569
+ vulnerability_class:
570
+ - Cryptographic Issues
571
+ - Hard-coded Credentials
572
+ patterns:
573
+ - pattern-either:
574
+ - pattern: "$K := os.Getenv($ENV)\n...\nif $K == \"\" {\n $K = \"$LITERAL\"\n}\n...\nhmac.New($H, []byte($K))\n"
575
+ - pattern: "$K := os.Getenv($ENV)\n...\nif $K == \"\" {\n $K = \"$LITERAL\"\n}\n...\naes.NewCipher([]byte($K))\n"
576
+ - pattern: "$K := os.Getenv($ENV)\n...\nif len($K) == 0 {\n $K = \"$LITERAL\"\n}\n...\nhmac.New($H, []byte($K))\n"
577
+ - pattern: "$K := os.Getenv($ENV)\n...\nif len($K) == 0 {\n $K = \"$LITERAL\"\n}\n...\naes.NewCipher([]byte($K))\n"
578
+ - pattern: "$K := os.Getenv($ENV)\n...\nif $K == \"\" {\n $K = \"$LITERAL\"\n}\n...\n$TOKEN.SignedString([]byte($K))\n"
579
+ - pattern: "$K := os.Getenv($ENV)\n...\nif len($K) == 0 {\n $K = \"$LITERAL\"\n}\n...\n$TOKEN.SignedString([]byte($K))\n"
580
+ - pattern: "$K := os.Getenv($ENV)\n...\nif \"\" == $K {\n $K = \"$LITERAL\"\n}\n...\nhmac.New($H, []byte($K))\n"
581
+ - pattern: "$K := os.Getenv($ENV)\n...\nif $K == \"\" {\n $K = \"$LITERAL\"\n}\n...\nargon2.IDKey([]byte($K), ...)\n"
582
+ - pattern: "$K := os.Getenv($ENV)\n...\nif $K == \"\" {\n $K = \"$LITERAL\"\n}\n...\nargon2.Key([]byte($K), ...)\n"
583
+ - metavariable-regex:
584
+ metavariable: $LITERAL
585
+ regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
586
+ - id: narvy.go.crypto.helper-env-fallback-to-crypto.helper-env-fallback-to-crypto
587
+ message: 'Helper-function fallback (e.g. `getEnvOrDefault("$ENV", "$LITERAL")`) is used to read a secret with a hardcoded
588
+ default, and the result is fed into a crypto primitive. If the env var is unset the key is a public constant. Fix: fail
589
+ fast at startup instead of silently defaulting.'
590
+ severity: ERROR
591
+ languages:
592
+ - go
593
+ metadata:
594
+ cwe:
595
+ - 'CWE-321: Use of Hard-coded Cryptographic Key'
596
+ - 'CWE-798: Use of Hard-coded Credentials'
597
+ owasp:
598
+ - A02:2021 - Cryptographic Failures
599
+ category: security
600
+ subcategory:
601
+ - vuln
602
+ technology:
603
+ - go
604
+ - golang
605
+ likelihood: HIGH
606
+ impact: HIGH
607
+ confidence: MEDIUM
608
+ vulnerability_class:
609
+ - Cryptographic Issues
610
+ - Hard-coded Credentials
611
+ patterns:
612
+ - pattern-either:
613
+ - pattern: 'hmac.New($H, []byte(getEnvOrDefault($ENV, "$LITERAL")))
614
+
615
+ '
616
+ - pattern: 'hmac.New($H, []byte(GetEnvOrDefault($ENV, "$LITERAL")))
617
+
618
+ '
619
+ - pattern: 'hmac.New($H, []byte(getEnv($ENV, "$LITERAL")))
620
+
621
+ '
622
+ - pattern: 'aes.NewCipher([]byte(getEnvOrDefault($ENV, "$LITERAL")))
623
+
624
+ '
625
+ - pattern: '$TOKEN.SignedString([]byte(getEnvOrDefault($ENV, "$LITERAL")))
626
+
627
+ '
628
+ - pattern: '$K := getEnvOrDefault($ENV, "$LITERAL")
629
+
630
+ ...
631
+
632
+ hmac.New($H, []byte($K))
633
+
634
+ '
635
+ - pattern: '$K := getEnvOrDefault($ENV, "$LITERAL")
636
+
637
+ ...
638
+
639
+ $TOKEN.SignedString([]byte($K))
640
+
641
+ '
642
+ - pattern: '$K := getEnv($ENV, "$LITERAL")
643
+
644
+ ...
645
+
646
+ hmac.New($H, []byte($K))
647
+
648
+ '
649
+ - metavariable-regex:
650
+ metavariable: $LITERAL
651
+ regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
652
+ - id: narvy.go.misc.pprof-registered-on-public-mux.pprof-registered-on-public-mux
653
+ message: '`net/http/pprof` imported for its side-effecting `init()` (which registers `/debug/pprof/*` on `http.DefaultServeMux`)
654
+ in a binary that also serves that mux to the public internet. `/debug/pprof/*` exposes goroutine stacks, heap dumps, and
655
+ command-line arguments, and its CPU profile endpoint is a trivial DoS lever (CWE-200). Register pprof only on a separate
656
+ listener bound to localhost, or behind auth.'
657
+ severity: WARNING
658
+ languages:
659
+ - go
660
+ metadata:
661
+ category: security
662
+ cwe:
663
+ - 'CWE-200: Exposure of Sensitive Information to an Unauthorized Actor'
664
+ owasp:
665
+ - A05:2021 - Security Misconfiguration
666
+ subcategory:
667
+ - vuln
668
+ likelihood: LOW
669
+ impact: MEDIUM
670
+ confidence: LOW
671
+ technology:
672
+ - go
673
+ patterns:
674
+ - pattern: 'import _ "net/http/pprof"
675
+
676
+ '
677
+ - pattern-inside: '...
678
+
679
+ http.ListenAndServe($ADDR, nil)
680
+
681
+ '
682
+ - id: narvy.go.misc.listen-bind-all-interfaces.listen-bind-all-interfaces
683
+ message: Server bound to `0.0.0.0` (or an empty host, e.g. `":8080"` combined with an explicit all-interfaces comment/config)
684
+ instead of a specific interface. For anything not meant to be internet-facing (internal admin APIs, debug/metrics endpoints,
685
+ databases), binding to all interfaces is an unintended external-exposure risk if the host also has a public NIC (CWE-1327
686
+ class). Bind to `127.0.0.1`/a specific internal IP unless the service is deliberately public.
687
+ severity: INFO
688
+ languages:
689
+ - go
690
+ metadata:
691
+ category: security
692
+ cwe:
693
+ - 'CWE-668: Exposure of Resource to Wrong Sphere'
694
+ owasp:
695
+ - A05:2021 - Security Misconfiguration
696
+ subcategory:
697
+ - audit
698
+ likelihood: LOW
699
+ impact: MEDIUM
700
+ confidence: LOW
701
+ technology:
702
+ - go
703
+ patterns:
704
+ - pattern-either:
705
+ - pattern: net.Listen("tcp", "0.0.0.0:...")
706
+ - pattern: http.ListenAndServe("0.0.0.0:...", ...)
707
+ - id: narvy.go.misc.fileserver-directory-listing-unguarded.fileserver-directory-listing-unguarded
708
+ message: '`http.FileServer(http.Dir($DIR))` mounted directly with no directory- listing guard. Without an `index.html` in
709
+ every directory, Go''s default file server returns a browsable directory listing for any path with no index file, potentially
710
+ exposing source files, config, or backups that were only meant to be served by direct link (CWE-548). Wrap the `http.FileSystem`
711
+ to return 404 on directory requests missing an index, or serve from an explicit allow-list of files.'
712
+ severity: INFO
713
+ languages:
714
+ - go
715
+ metadata:
716
+ category: security
717
+ cwe:
718
+ - 'CWE-548: Exposure of Information Through Directory Listing'
719
+ owasp:
720
+ - A05:2021 - Security Misconfiguration
721
+ subcategory:
722
+ - audit
723
+ likelihood: LOW
724
+ impact: LOW
725
+ confidence: LOW
726
+ technology:
727
+ - go
728
+ pattern: http.FileServer(http.Dir($DIR))
729
+ - id: narvy.go.misc.websocket-checkorigin-always-true.websocket-checkorigin-always-true
730
+ message: '`gorilla/websocket.Upgrader` constructed with `CheckOrigin` left unset (nil) or hardcoded to always return `true`.
731
+ The default/permissive behavior accepts a WebSocket upgrade from any origin, enabling cross-site WebSocket hijacking against
732
+ cookie-authenticated endpoints (CWE-346). Set `CheckOrigin` to validate the `Origin` header against a fixed allow-list.'
733
+ severity: WARNING
734
+ languages:
735
+ - go
736
+ metadata:
737
+ category: security
738
+ cwe:
739
+ - 'CWE-346: Origin Validation Error'
740
+ owasp:
741
+ - A01:2021 - Broken Access Control
742
+ subcategory:
743
+ - vuln
744
+ likelihood: LOW
745
+ impact: MEDIUM
746
+ confidence: LOW
747
+ technology:
748
+ - go
749
+ patterns:
750
+ - pattern-either:
751
+ - pattern: 'websocket.Upgrader{..., CheckOrigin: func(r *http.Request) bool { return true }, ...}
752
+
753
+ '
754
+ - id: narvy.go.misc.predictable-tmp-path-not-createtemp.predictable-tmp-path-not-createtemp
755
+ message: '`os.Create()` / `os.MkdirAll()` / `os.WriteFile()` used on a path under `/tmp` built with a predictable name (fixed
756
+ string, PID, timestamp with no random component, or a format-string with no random component) instead of `os.CreateTemp()`/`ioutil.TempFile()`.
757
+ A predictable temp-file or temp-directory name in a world-writable directory is a classic symlink-race / TOCTOU target
758
+ (CWE-377). Use `os.CreateTemp()` for files (unpredictable name, opens with `O_EXCL`) or `os.MkdirTemp()` for directories.
759
+ '
760
+ severity: INFO
761
+ languages:
762
+ - go
763
+ metadata:
764
+ category: security
765
+ cwe:
766
+ - 'CWE-377: Insecure Temporary File'
767
+ owasp:
768
+ - A01:2021 - Broken Access Control
769
+ subcategory:
770
+ - audit
771
+ likelihood: LOW
772
+ impact: LOW
773
+ confidence: LOW
774
+ technology:
775
+ - go
776
+ patterns:
777
+ - pattern-either:
778
+ - patterns:
779
+ - pattern: os.Create($PATH)
780
+ - metavariable-regex:
781
+ metavariable: $PATH
782
+ regex: ^"/tmp/
783
+ - pattern: os.Create("/tmp/" + ...)
784
+ - patterns:
785
+ - pattern: os.Create(fmt.Sprintf($FMT, ...))
786
+ - metavariable-regex:
787
+ metavariable: $FMT
788
+ regex: ^"/tmp/
789
+ - patterns:
790
+ - pattern: os.MkdirAll($PATH, ...)
791
+ - metavariable-regex:
792
+ metavariable: $PATH
793
+ regex: ^"/tmp/
794
+ - pattern: os.MkdirAll("/tmp/" + ..., ...)
795
+ - patterns:
796
+ - pattern: os.MkdirAll(fmt.Sprintf($FMT, ...), ...)
797
+ - metavariable-regex:
798
+ metavariable: $FMT
799
+ regex: ^"/tmp/
800
+ - patterns:
801
+ - pattern: os.WriteFile($PATH, ...)
802
+ - metavariable-regex:
803
+ metavariable: $PATH
804
+ regex: ^"/tmp/
805
+ - pattern: os.WriteFile("/tmp/" + ..., ...)
806
+ - patterns:
807
+ - pattern: os.WriteFile(fmt.Sprintf($FMT, ...), ...)
808
+ - metavariable-regex:
809
+ metavariable: $FMT
810
+ regex: ^"/tmp/
811
+ - id: narvy.go.misc.insecure-file-permission.insecure-file-permission
812
+ message: File permission bits are set to world-writable (0777/0666) via `os.Chmod` / `os.OpenFile` / `os.WriteFile` / `ioutil.WriteFile`.
813
+ Any local user or process can then modify or read this file, which is dangerous for config files, key material, or scripts
814
+ that get executed later (CWE-732). Use the narrowest mode that works, e.g. 0600 for secrets or 0644 for regular files.
815
+ severity: WARNING
816
+ languages:
817
+ - go
818
+ metadata:
819
+ cwe:
820
+ - 'CWE-732: Incorrect Permission Assignment for Critical Resource'
821
+ owasp:
822
+ - A01:2021 - Broken Access Control
823
+ category: security
824
+ subcategory:
825
+ - audit
826
+ technology:
827
+ - go
828
+ likelihood: LOW
829
+ impact: MEDIUM
830
+ confidence: HIGH
831
+ vulnerability_class:
832
+ - Improper Access Control
833
+ patterns:
834
+ - pattern-either:
835
+ - pattern: os.Chmod($PATH, 0777)
836
+ - pattern: os.Chmod($PATH, 0666)
837
+ - pattern: os.OpenFile($PATH, $FLAG, 0777)
838
+ - pattern: os.OpenFile($PATH, $FLAG, 0666)
839
+ - pattern: os.WriteFile($PATH, $DATA, 0777)
840
+ - pattern: os.WriteFile($PATH, $DATA, 0666)
841
+ - pattern: ioutil.WriteFile($PATH, $DATA, 0777)
842
+ - pattern: ioutil.WriteFile($PATH, $DATA, 0666)
843
+ - id: narvy.go.misc.pprof-blank-import-exposure.pprof-blank-import-exposure
844
+ message: '`net/http/pprof` is blank-imported, which registers profiling handlers (`/debug/pprof/*`, including goroutine
845
+ dumps, heap profiles, and a CPU-profile trigger) onto `http.DefaultServeMux` with zero authentication. If that mux is
846
+ served on a publicly-reachable listener, this leaks internal application state and provides a CPU-exhaustion knob to anyone
847
+ who can reach it (CWE-200). Serve pprof on a separate listener bound to localhost, or behind authentication middleware.'
848
+ severity: WARNING
849
+ languages:
850
+ - go
851
+ metadata:
852
+ cwe:
853
+ - 'CWE-200: Exposure of Sensitive Information to an Unauthorized Actor'
854
+ owasp:
855
+ - A05:2021 - Security Misconfiguration
856
+ category: security
857
+ subcategory:
858
+ - audit
859
+ technology:
860
+ - go
861
+ likelihood: LOW
862
+ impact: MEDIUM
863
+ confidence: LOW
864
+ vulnerability_class:
865
+ - Information Exposure
866
+ patterns:
867
+ - pattern: import _ "net/http/pprof"
868
+ - id: narvy.go.ssh.insecure-hostkey-callback.insecure-hostkey-callback
869
+ message: 'golang.org/x/crypto/ssh.InsecureIgnoreHostKey() accepts whatever host key the remote end presents, with no verification
870
+ at all. Anyone able to sit on the network path between this client and the SSH server can present their own key and the
871
+ client will treat the connection as trusted, defeating the entire point of the SSH handshake (CWE-322: Key Exchange without
872
+ Entity Authentication). Verify against a pinned key with ssh.FixedHostKey(), or check the remote key against a known_hosts
873
+ file with golang.org/x/crypto/ssh/knownhosts instead.'
874
+ severity: ERROR
875
+ languages:
876
+ - go
877
+ metadata:
878
+ category: security
879
+ cwe:
880
+ - 'CWE-322: Key Exchange without Entity Authentication'
881
+ owasp:
882
+ - A02:2021 - Cryptographic Failures
883
+ subcategory:
884
+ - vuln
885
+ likelihood: LOW
886
+ impact: HIGH
887
+ confidence: HIGH
888
+ technology:
889
+ - go
890
+ paths:
891
+ exclude:
892
+ - '**/*_test.go'
893
+ - '**/testdata/**'
894
+ pattern: ssh.InsecureIgnoreHostKey()
895
+ - id: narvy.go.http.plaintext-server-no-tls-anywhere.plaintext-server-no-tls-anywhere
896
+ message: 'An HTTP server is started with no TLS involved at all - either http.ListenAndServe() (the plaintext entry point,
897
+ as opposed to http.ListenAndServeTLS()) or an *http.Server started via .ListenAndServe() on a Server value that never
898
+ had a TLSConfig set. Every request and response on this listener, including any auth cookie, bearer token, or session
899
+ id, crosses the wire unencrypted and can be read or tampered with by anyone on the network path (CWE-319: Cleartext Transmission
900
+ of Sensitive Information). Switch to ListenAndServeTLS()/set TLSConfig, or if TLS is deliberately terminated in front
901
+ of this listener (a reverse proxy, a service mesh sidecar, a Unix socket only reachable locally), suppress this finding
902
+ with a comment explaining that.'
903
+ severity: WARNING
904
+ languages:
905
+ - go
906
+ metadata:
907
+ category: security
908
+ cwe:
909
+ - 'CWE-319: Cleartext Transmission of Sensitive Information'
910
+ owasp:
911
+ - A02:2021 - Cryptographic Failures
912
+ subcategory:
913
+ - audit
914
+ likelihood: LOW
915
+ impact: MEDIUM
916
+ confidence: LOW
917
+ technology:
918
+ - go
919
+ paths:
920
+ exclude:
921
+ - '**/*_test.go'
922
+ - '**/testdata/**'
923
+ - '**/fixtures/**'
924
+ - '**/examples/**'
925
+ patterns:
926
+ - pattern-either:
927
+ - pattern: http.ListenAndServe($ADDR, $HANDLER)
928
+ - patterns:
929
+ - pattern: $SRV.ListenAndServe()
930
+ - pattern-not-inside: '$SRV := &http.Server{..., TLSConfig: $CFG, ...}
931
+
932
+ ...
933
+
934
+ '
935
+ - pattern-not-inside: '$SRV.TLSConfig = $CFG
936
+
937
+ ...
938
+
939
+ '
940
+ - id: narvy.go.reflect.makefunc-dynamic-code.makefunc-dynamic-code
941
+ message: 'reflect.MakeFunc() builds a function value at runtime from a reflect.Type and a Go closure, bypassing the normal
942
+ compile-time type system. If any part of what gets wired into that closure (which argument gets used, what it does with
943
+ it, which downstream call it makes) is influenced by untrusted input, this is a code-generation-adjacent sink and a much
944
+ larger attack surface than a normal function call (CWE-913: Improper Control of Dynamically-Managed Code Resources). Confirm
945
+ the reflect.Type and the closure body are both fully static (no request-derived data feeding either), or replace this
946
+ with a plain typed function / a small static dispatch table.'
947
+ severity: WARNING
948
+ languages:
949
+ - go
950
+ metadata:
951
+ category: security
952
+ cwe:
953
+ - 'CWE-913: Improper Control of Dynamically-Managed Code Resources'
954
+ owasp:
955
+ - A08:2021 - Software and Data Integrity Failures
956
+ subcategory:
957
+ - audit
958
+ likelihood: LOW
959
+ impact: MEDIUM
960
+ confidence: LOW
961
+ technology:
962
+ - go
963
+ paths:
964
+ exclude:
965
+ - '**/*_test.go'
966
+ - '**/testdata/**'
967
+ pattern: reflect.MakeFunc(...)
968
+ - id: narvy.go.httptrace.trace-not-literal-at-callsite.trace-not-literal-at-callsite
969
+ message: 'httptrace.WithClientTrace() is handed a trace value that is not a struct literal written directly at the call
970
+ site (it is a variable, a function return value, or otherwise assembled elsewhere). A *httptrace.ClientTrace carries hook
971
+ functions that run automatically for connection/DNS/TLS events on every request using this context; if the trace (or any
972
+ hook inside it) can be influenced by request data or config an attacker controls, code runs that the reviewer cannot see
973
+ just by reading this call (CWE-913: Improper Control of Dynamically-Managed Code Resources). Either inline the &httptrace.ClientTrace{...}
974
+ literal here so it is auditable at the call site, or confirm the variable is assigned exactly once, from a static literal,
975
+ with no request-derived input anywhere in it.'
976
+ severity: INFO
977
+ languages:
978
+ - go
979
+ metadata:
980
+ category: security
981
+ cwe:
982
+ - 'CWE-913: Improper Control of Dynamically-Managed Code Resources'
983
+ owasp:
984
+ - A08:2021 - Software and Data Integrity Failures
985
+ subcategory:
986
+ - audit
987
+ likelihood: LOW
988
+ impact: LOW
989
+ confidence: LOW
990
+ technology:
991
+ - go
992
+ paths:
993
+ exclude:
994
+ - '**/*_test.go'
995
+ - '**/testdata/**'
996
+ patterns:
997
+ - pattern: httptrace.WithClientTrace($CTX, $TRACE)
998
+ - pattern-not: httptrace.WithClientTrace($CTX, &httptrace.ClientTrace{...})
999
+ - id: narvy.go.open-redirect.request-param-to-http-redirect.request-param-to-http-redirect
1000
+ message: A value read directly from a request query parameter, form value, or path variable flows into `http.Redirect`'s
1001
+ target URL. An attacker can craft a link that redirects victims to an attacker-controlled site (open redirect, CWE-601),
1002
+ a common phishing / OAuth-token-theft primitive. Validate the target against an allow-list of known paths or hosts before
1003
+ redirecting.
1004
+ severity: WARNING
1005
+ languages:
1006
+ - go
1007
+ mode: taint
1008
+ metadata:
1009
+ cwe:
1010
+ - 'CWE-601: URL Redirection to Untrusted Site (''Open Redirect'')'
1011
+ owasp:
1012
+ - A01:2021 - Broken Access Control
1013
+ category: security
1014
+ subcategory:
1015
+ - vuln
1016
+ technology:
1017
+ - go
1018
+ likelihood: MEDIUM
1019
+ impact: MEDIUM
1020
+ confidence: MEDIUM
1021
+ vulnerability_class:
1022
+ - Open Redirect
1023
+ references:
1024
+ - https://cwe.mitre.org/data/definitions/601.html
1025
+ paths:
1026
+ exclude:
1027
+ - '**/*_test.go'
1028
+ pattern-sources:
1029
+ - pattern: $R.URL.Query().Get(...)
1030
+ - pattern: $R.FormValue(...)
1031
+ - pattern: $R.URL.Query()[...]
1032
+ pattern-sanitizers:
1033
+ - patterns:
1034
+ - pattern: $V
1035
+ - pattern-inside: 'if (<... strings.HasPrefix($V, "/") ...>) { ... }
1036
+
1037
+ '
1038
+ - pattern: url.Parse(...)
1039
+ pattern-sinks:
1040
+ - pattern: http.Redirect($W, $R, $SINK, ...)
1041
+ - id: narvy-go-pathtraversal-os-open
1042
+ patterns:
1043
+ - pattern-either:
1044
+ - pattern: os.Open($PATH)
1045
+ - pattern: os.OpenFile($PATH, ...)
1046
+ - pattern-not: os.Open("...")
1047
+ - pattern-not: os.OpenFile("...", ...)
1048
+ - pattern-not-inside: '$PATH = "..."
1049
+
1050
+ ...
1051
+
1052
+ '
1053
+ - pattern-not: os.Open(filepath.Join("...", "..."))
1054
+ - pattern-either:
1055
+ - pattern-inside: "func $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1056
+ - pattern-inside: "func $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1057
+ - pattern-inside: "func ($RECV $T) $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1058
+ - pattern-inside: "func ($RECV $T) $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1059
+ - pattern-inside: "func $FN(..., $C echo.Context, ...) $RET {\n ...\n}\n"
1060
+ - pattern-inside: "func $FN(..., $C *gin.Context, ...) {\n ...\n}\n"
1061
+ - pattern-inside: "func $FN($X interface{}, $PAYLOAD string) (data string, raw bool, err error) {\n ...\n}\n"
1062
+ message: 'Path traversal: `os.Open` is called with a non-literal path inside an HTTP handler. If the path comes from a request
1063
+ parameter an attacker can submit `../../../etc/passwd` and read arbitrary files. Validate the path with `filepath.Clean`
1064
+ AND restrict it to a safe base directory via `strings.HasPrefix(cleanedPath, baseDir)`.'
1065
+ metadata:
1066
+ cwe:
1067
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1068
+ owasp:
1069
+ - A01:2021 - Broken Access Control
1070
+ category: security
1071
+ technology:
1072
+ - go
1073
+ confidence: HIGH
1074
+ subcategory:
1075
+ - vuln
1076
+ likelihood: HIGH
1077
+ impact: HIGH
1078
+ severity: ERROR
1079
+ languages:
1080
+ - go
1081
+ - id: narvy-go-pathtraversal-readfile
1082
+ patterns:
1083
+ - pattern-either:
1084
+ - pattern: os.ReadFile($PATH)
1085
+ - pattern: ioutil.ReadFile($PATH)
1086
+ - pattern-not: os.ReadFile("...")
1087
+ - pattern-not: ioutil.ReadFile("...")
1088
+ - pattern-not-inside: '$PATH = "..."
1089
+
1090
+ ...
1091
+
1092
+ '
1093
+ - pattern-not: os.ReadFile(filepath.Join("...", "..."))
1094
+ - pattern-either:
1095
+ - pattern-inside: "func $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1096
+ - pattern-inside: "func $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1097
+ - pattern-inside: "func ($RECV $T) $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1098
+ - pattern-inside: "func ($RECV $T) $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1099
+ - pattern-inside: "func $FN(..., $C echo.Context, ...) $RET {\n ...\n}\n"
1100
+ - pattern-inside: "func $FN(..., $C *gin.Context, ...) {\n ...\n}\n"
1101
+ - pattern-inside: "func $FN($X interface{}, $PAYLOAD string) (data string, raw bool, err error) {\n ...\n}\n"
1102
+ message: 'Path traversal: `os.ReadFile` / `ioutil.ReadFile` is called with a non-literal path inside an HTTP handler. Same
1103
+ risk as `os.Open` - file disclosure. Validate with `filepath.Clean` + base-prefix check.'
1104
+ metadata:
1105
+ cwe:
1106
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1107
+ owasp:
1108
+ - A01:2021 - Broken Access Control
1109
+ category: security
1110
+ technology:
1111
+ - go
1112
+ confidence: HIGH
1113
+ subcategory:
1114
+ - vuln
1115
+ likelihood: HIGH
1116
+ impact: HIGH
1117
+ severity: ERROR
1118
+ languages:
1119
+ - go
1120
+ - id: narvy-go-pathtraversal-writefile
1121
+ patterns:
1122
+ - pattern-either:
1123
+ - pattern: os.WriteFile($PATH, ...)
1124
+ - pattern: ioutil.WriteFile($PATH, ...)
1125
+ - pattern: os.Create($PATH)
1126
+ - pattern-not: os.WriteFile("...", ...)
1127
+ - pattern-not: ioutil.WriteFile("...", ...)
1128
+ - pattern-not: os.Create("...")
1129
+ - pattern-not-inside: '$PATH = "..."
1130
+
1131
+ ...
1132
+
1133
+ '
1134
+ - pattern-not: os.Create(filepath.Join("...", "..."))
1135
+ - pattern-either:
1136
+ - pattern-inside: "func $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1137
+ - pattern-inside: "func $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1138
+ - pattern-inside: "func ($RECV $T) $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1139
+ - pattern-inside: "func ($RECV $T) $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1140
+ - pattern-inside: "func $FN(..., $C echo.Context, ...) $RET {\n ...\n}\n"
1141
+ - pattern-inside: "func $FN(..., $C *gin.Context, ...) {\n ...\n}\n"
1142
+ - pattern-inside: "func $FN($X interface{}, $PAYLOAD string) (data string, raw bool, err error) {\n ...\n}\n"
1143
+ message: 'Path traversal (write): `os.WriteFile` / `os.Create` is called with a non-literal path inside an HTTP handler.
1144
+ An attacker can overwrite arbitrary files (e.g. /root/.ssh/authorized_keys). Validate the path with `filepath.Clean` +
1145
+ base-prefix check before writing.'
1146
+ metadata:
1147
+ cwe:
1148
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1149
+ owasp:
1150
+ - A01:2021 - Broken Access Control
1151
+ category: security
1152
+ technology:
1153
+ - go
1154
+ confidence: HIGH
1155
+ subcategory:
1156
+ - vuln
1157
+ likelihood: HIGH
1158
+ impact: HIGH
1159
+ severity: ERROR
1160
+ languages:
1161
+ - go
1162
+ - id: narvy-go-pathtraversal-filepath-join-tainted
1163
+ patterns:
1164
+ - pattern: filepath.Join($BASE, $UNTRUSTED)
1165
+ - pattern-not: filepath.Join("...", "...")
1166
+ - pattern-not: filepath.Join($X, "...")
1167
+ - pattern-either:
1168
+ - pattern: '$P := filepath.Join($BASE, $UNTRUSTED)
1169
+
1170
+ ...
1171
+
1172
+ os.Open($P)
1173
+
1174
+ '
1175
+ - pattern: '$P := filepath.Join($BASE, $UNTRUSTED)
1176
+
1177
+ ...
1178
+
1179
+ os.ReadFile($P)
1180
+
1181
+ '
1182
+ - pattern: '$P := filepath.Join($BASE, $UNTRUSTED)
1183
+
1184
+ ...
1185
+
1186
+ os.WriteFile($P, ...)
1187
+
1188
+ '
1189
+ - pattern: '$P := filepath.Join($BASE, $UNTRUSTED)
1190
+
1191
+ ...
1192
+
1193
+ os.Create($P)
1194
+
1195
+ '
1196
+ - pattern: '$P := filepath.Join($BASE, $UNTRUSTED)
1197
+
1198
+ ...
1199
+
1200
+ os.OpenFile($P, ...)
1201
+
1202
+ '
1203
+ - pattern-not: '$P := filepath.Join($BASE, $UNTRUSTED)
1204
+
1205
+ ...
1206
+
1207
+ $P = filepath.Clean($P)
1208
+
1209
+ ...
1210
+
1211
+ '
1212
+ - pattern-either:
1213
+ - pattern-inside: "func $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1214
+ - pattern-inside: "func $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1215
+ - pattern-inside: "func ($RECV $T) $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1216
+ - pattern-inside: "func ($RECV $T) $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1217
+ - pattern-inside: "func $FN(..., $C echo.Context, ...) $RET {\n ...\n}\n"
1218
+ - pattern-inside: "func $FN(..., $C *gin.Context, ...) {\n ...\n}\n"
1219
+ message: 'Path traversal: `filepath.Join` accepts a variable then flows to a file I/O sink without intermediate `filepath.Clean`
1220
+ validation, inside an HTTP handler. `Join` normalises but does NOT prevent `../` escape past the base; you must `Clean`
1221
+ and then verify `strings.HasPrefix(cleaned, baseDir)`.'
1222
+ metadata:
1223
+ cwe:
1224
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1225
+ owasp:
1226
+ - A01:2021 - Broken Access Control
1227
+ category: security
1228
+ technology:
1229
+ - go
1230
+ confidence: MEDIUM
1231
+ subcategory:
1232
+ - audit
1233
+ likelihood: MEDIUM
1234
+ impact: HIGH
1235
+ severity: WARNING
1236
+ languages:
1237
+ - go
1238
+ - id: narvy.go.pathtraversal.zip-slip-extraction.zip-slip-extraction
1239
+ message: A file's path is taken from a `zip.Reader`/`tar.Reader` entry name and joined onto a destination directory with
1240
+ no check that the result stays inside it. A malicious archive entry named `../../etc/cron.d/x` escapes the extraction
1241
+ directory and writes anywhere the process can (`zip-slip`, CWE-22). After `filepath.Join(destDir, entry.Name)`, verify
1242
+ the result is still prefixed by `destDir` (e.g. via `filepath.Rel` + reject any leading `..`) before writing.
1243
+ severity: ERROR
1244
+ languages:
1245
+ - go
1246
+ metadata:
1247
+ category: security
1248
+ cwe:
1249
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1250
+ owasp:
1251
+ - A01:2021 - Broken Access Control
1252
+ subcategory:
1253
+ - vuln
1254
+ likelihood: MEDIUM
1255
+ impact: HIGH
1256
+ confidence: MEDIUM
1257
+ technology:
1258
+ - go
1259
+ patterns:
1260
+ - pattern-either:
1261
+ - pattern: '$DST := filepath.Join($DESTDIR, $ENTRY.Name)
1262
+
1263
+ ...
1264
+
1265
+ os.Create($DST)
1266
+
1267
+ '
1268
+ - pattern: '$DST := filepath.Join($DESTDIR, $HDR.Name)
1269
+
1270
+ ...
1271
+
1272
+ os.OpenFile($DST, ...)
1273
+
1274
+ '
1275
+ - pattern-not-inside: "...\nif !strings.HasPrefix($DST, ...) {\n ...\n}\n...\n"
1276
+ - id: narvy.go.pathtraversal.serve-file-tainted.serve-file-tainted
1277
+ message: '`http.ServeFile()` called with a filesystem path built directly from the request URL/query. `ServeFile` has its
1278
+ own `../` guard for the exact `r.URL.Path` case, but once the path is rebuilt via concatenation or `filepath.Join` with
1279
+ a query-parameter value, that guard no longer applies - path traversal to any file the process can read (CWE-22). Confine
1280
+ the served root and reject `..` segments explicitly, or use `http.FileServer` with `http.Dir` on a fixed root.'
1281
+ severity: ERROR
1282
+ languages:
1283
+ - go
1284
+ metadata:
1285
+ category: security
1286
+ cwe:
1287
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1288
+ owasp:
1289
+ - A01:2021 - Broken Access Control
1290
+ subcategory:
1291
+ - vuln
1292
+ likelihood: MEDIUM
1293
+ impact: HIGH
1294
+ confidence: MEDIUM
1295
+ technology:
1296
+ - go
1297
+ patterns:
1298
+ - pattern-either:
1299
+ - pattern: http.ServeFile($W, $R, filepath.Join($ROOT, $R.URL.Query().Get(...)))
1300
+ - pattern: http.ServeFile($W, $R, "..." + $R.URL.Query().Get(...))
1301
+ - pattern: http.ServeFile($W, $R, filepath.Join($ROOT, $R.FormValue(...)))
1302
+ - id: narvy.go.pathtraversal.remove-tainted-path.remove-tainted-path
1303
+ message: '`os.Remove()` / `os.RemoveAll()` called with a path built directly from request data. An attacker who controls
1304
+ the path (or a `../` segment within it) can delete files outside the intended directory - path traversal with a destructive
1305
+ sink (CWE-22). Confine to a fixed base directory and reject any resolved path that escapes it.'
1306
+ severity: ERROR
1307
+ languages:
1308
+ - go
1309
+ metadata:
1310
+ category: security
1311
+ cwe:
1312
+ - 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
1313
+ owasp:
1314
+ - A01:2021 - Broken Access Control
1315
+ subcategory:
1316
+ - vuln
1317
+ likelihood: LOW
1318
+ impact: HIGH
1319
+ confidence: MEDIUM
1320
+ technology:
1321
+ - go
1322
+ patterns:
1323
+ - pattern-either:
1324
+ - pattern: os.Remove(filepath.Join($ROOT, $R.URL.Query().Get(...)))
1325
+ - pattern: os.RemoveAll(filepath.Join($ROOT, $R.URL.Query().Get(...)))
1326
+ - pattern: os.Remove($R.FormValue(...))
1327
+ - pattern: os.RemoveAll($R.FormValue(...))
1328
+ - id: narvy.go.secrets.hardcoded-auth-header-value.hardcoded-auth-header-value
1329
+ message: An `Authorization` (or `X-Api-Key`) header is set to a hardcoded string literal that looks like a credential (contains
1330
+ `Bearer `, `Basic `, or is a long opaque token). Anyone with source or binary access recovers the live credential (CWE-798).
1331
+ Load it from an environment variable or secrets manager at startup.
1332
+ severity: WARNING
1333
+ languages:
1334
+ - go
1335
+ metadata:
1336
+ category: security
1337
+ cwe:
1338
+ - 'CWE-798: Use of Hard-coded Credentials'
1339
+ owasp:
1340
+ - A07:2021 - Identification and Authentication Failures
1341
+ subcategory:
1342
+ - vuln
1343
+ likelihood: LOW
1344
+ impact: HIGH
1345
+ confidence: MEDIUM
1346
+ technology:
1347
+ - go
1348
+ patterns:
1349
+ - pattern-either:
1350
+ - pattern: $REQ.Header.Set("Authorization", "Bearer ...")
1351
+ - pattern: $REQ.Header.Set("Authorization", "Basic ...")
1352
+ - pattern: $REQ.Header.Set("X-Api-Key", "...")
1353
+ - id: narvy.go.secrets.hardcoded-dsn-password.hardcoded-dsn-password
1354
+ message: A database connection string (DSN) literal contains an embedded password (`user:password@host` / `password=...`).
1355
+ This ships the live database credential in source control and in the compiled binary (CWE-798). Build the DSN from environment
1356
+ variables / a secrets manager at startup, never a literal.
1357
+ severity: ERROR
1358
+ languages:
1359
+ - go
1360
+ metadata:
1361
+ category: security
1362
+ cwe:
1363
+ - 'CWE-798: Use of Hard-coded Credentials'
1364
+ - 'CWE-259: Use of Hard-coded Password'
1365
+ owasp:
1366
+ - A07:2021 - Identification and Authentication Failures
1367
+ subcategory:
1368
+ - vuln
1369
+ likelihood: LOW
1370
+ impact: HIGH
1371
+ confidence: MEDIUM
1372
+ technology:
1373
+ - go
1374
+ patterns:
1375
+ - pattern-either:
1376
+ - pattern: sql.Open("...", "...:...@...")
1377
+ - pattern: sql.Open("...", "...password=...")
1378
+ - id: narvy.go.secrets.hardcoded-private-key-pem.hardcoded-private-key-pem
1379
+ message: A PEM-formatted private key block (`-----BEGIN ... PRIVATE KEY-----`) is embedded as a Go string literal. This
1380
+ ships the live key in source control and in the compiled binary - anyone with either can decrypt traffic or forge signatures
1381
+ made with it (CWE-798/CWE-321). Load the key from a file/secrets manager at startup, outside the repository.
1382
+ severity: ERROR
1383
+ languages:
1384
+ - go
1385
+ metadata:
1386
+ category: security
1387
+ cwe:
1388
+ - 'CWE-798: Use of Hard-coded Credentials'
1389
+ - 'CWE-321: Use of Hard-coded Cryptographic Key'
1390
+ owasp:
1391
+ - A02:2021 - Cryptographic Failures
1392
+ subcategory:
1393
+ - vuln
1394
+ likelihood: LOW
1395
+ impact: HIGH
1396
+ confidence: HIGH
1397
+ technology:
1398
+ - go
1399
+ patterns:
1400
+ - pattern-regex: (?s)-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----.*-----END (RSA |EC |OPENSSH )?PRIVATE KEY-----
1401
+ - id: narvy.go.session.cookie-secure-explicitly-false.cookie-secure-explicitly-false
1402
+ message: '`http.Cookie{Secure: false}` explicitly disables the Secure flag, allowing this cookie to be sent over plain HTTP.
1403
+ If it carries a session token, an on-path attacker on an unencrypted network segment can capture it (CWE-614). Set `Secure:
1404
+ true` unless this cookie is provably non-sensitive.'
1405
+ severity: WARNING
1406
+ languages:
1407
+ - go
1408
+ metadata:
1409
+ cwe:
1410
+ - 'CWE-614: Sensitive Cookie in HTTPS Session Without ''Secure'' Attribute'
1411
+ owasp:
1412
+ - A05:2021 - Security Misconfiguration
1413
+ category: security
1414
+ subcategory:
1415
+ - vuln
1416
+ technology:
1417
+ - go
1418
+ likelihood: LOW
1419
+ impact: MEDIUM
1420
+ confidence: HIGH
1421
+ vulnerability_class:
1422
+ - Insecure Communication
1423
+ references:
1424
+ - https://cwe.mitre.org/data/definitions/614.html
1425
+ paths:
1426
+ exclude:
1427
+ - '**/*_test.go'
1428
+ patterns:
1429
+ - pattern: "http.Cookie{\n ...,\n Secure: false,\n ...\n}\n"
1430
+ - id: narvy.go.session.cookie-httponly-explicitly-false.cookie-httponly-explicitly-false
1431
+ message: '`http.Cookie{HttpOnly: false}` explicitly disables the HttpOnly flag, allowing client-side JavaScript to read
1432
+ this cookie. If it carries a session token, any XSS on the page can exfiltrate it (CWE-1004). Set `HttpOnly: true` unless
1433
+ client-side JS provably needs to read this cookie.'
1434
+ severity: WARNING
1435
+ languages:
1436
+ - go
1437
+ metadata:
1438
+ cwe:
1439
+ - 'CWE-1004: Sensitive Cookie Without ''HttpOnly'' Flag'
1440
+ owasp:
1441
+ - A05:2021 - Security Misconfiguration
1442
+ category: security
1443
+ subcategory:
1444
+ - vuln
1445
+ technology:
1446
+ - go
1447
+ likelihood: LOW
1448
+ impact: MEDIUM
1449
+ confidence: HIGH
1450
+ vulnerability_class:
1451
+ - Security Misconfiguration
1452
+ references:
1453
+ - https://cwe.mitre.org/data/definitions/1004.html
1454
+ paths:
1455
+ exclude:
1456
+ - '**/*_test.go'
1457
+ patterns:
1458
+ - pattern: "http.Cookie{\n ...,\n HttpOnly: false,\n ...\n}\n"
1459
+ - id: narvy-go-sqli-concat
1460
+ patterns:
1461
+ - pattern-either:
1462
+ - pattern: $DB.Query($A + $B)
1463
+ - pattern: $DB.QueryContext($CTX, $A + $B)
1464
+ - pattern: $DB.QueryRow($A + $B)
1465
+ - pattern: $DB.QueryRowContext($CTX, $A + $B)
1466
+ - pattern: $DB.Exec($A + $B)
1467
+ - pattern: $DB.ExecContext($CTX, $A + $B)
1468
+ - pattern-not: $DB.Query("..." + "...")
1469
+ - pattern-not: $DB.QueryContext($CTX, "..." + "...")
1470
+ - pattern-not: $DB.QueryRow("..." + "...")
1471
+ - pattern-not: $DB.QueryRowContext($CTX, "..." + "...")
1472
+ - pattern-not: $DB.Exec("..." + "...")
1473
+ - pattern-not: $DB.ExecContext($CTX, "..." + "...")
1474
+ message: 'Dynamic SQL: query string is built with the Go concat operator (`+`) rather than a bound placeholder. This is
1475
+ a code-smell audit signal (HIGH), not a proven CRITICAL: without data-flow proof the concatenated operand may be an
1476
+ internal constant (e.g. a table name in a migration) rather than user input. IF the operand is request-derived this is
1477
+ SQL injection (CWE-89) - see the CRITICAL taint rule narvy-go-sqli-request-taint. Prefer placeholders regardless:
1478
+ `db.Query("SELECT ... WHERE id = ?", userID)`.'
1479
+ metadata:
1480
+ cwe:
1481
+ - 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
1482
+ owasp:
1483
+ - A03:2021 - Injection
1484
+ category: security
1485
+ technology:
1486
+ - go
1487
+ confidence: MEDIUM
1488
+ subcategory:
1489
+ - audit
1490
+ likelihood: MEDIUM
1491
+ impact: HIGH
1492
+ severity: WARNING
1493
+ languages:
1494
+ - go
1495
+ - id: narvy-go-sqli-sprintf-via-var
1496
+ patterns:
1497
+ - pattern-either:
1498
+ - pattern: '$QUERY := fmt.Sprintf($FMT, ...)
1499
+
1500
+ ...
1501
+
1502
+ $DB.Query($QUERY)
1503
+
1504
+ '
1505
+ - pattern: '$QUERY := fmt.Sprintf($FMT, ...)
1506
+
1507
+ ...
1508
+
1509
+ $DB.QueryContext($CTX, $QUERY)
1510
+
1511
+ '
1512
+ - pattern: '$QUERY := fmt.Sprintf($FMT, ...)
1513
+
1514
+ ...
1515
+
1516
+ $DB.QueryRow($QUERY)
1517
+
1518
+ '
1519
+ - pattern: '$QUERY := fmt.Sprintf($FMT, ...)
1520
+
1521
+ ...
1522
+
1523
+ $DB.Exec($QUERY)
1524
+
1525
+ '
1526
+ - pattern: '$QUERY := fmt.Sprintf($FMT, ...)
1527
+
1528
+ ...
1529
+
1530
+ $DB.ExecContext($CTX, $QUERY)
1531
+
1532
+ '
1533
+ - pattern: '$QUERY = fmt.Sprintf($FMT, ...)
1534
+
1535
+ ...
1536
+
1537
+ $DB.Exec($QUERY)
1538
+
1539
+ '
1540
+ - pattern: '$QUERY = fmt.Sprintf($FMT, ...)
1541
+
1542
+ ...
1543
+
1544
+ $DB.Query($QUERY)
1545
+
1546
+ '
1547
+ message: 'Dynamic SQL: `fmt.Sprintf` builds the query, then the result is passed to `db.Query`/`db.Exec`. Format-string
1548
+ quoting is not driver escaping. This is a code-smell audit signal (HIGH), not a proven CRITICAL: the interpolated
1549
+ args may be internal constants (e.g. a migration table name) rather than user input. IF an arg is request-derived
1550
+ this is SQL injection (CWE-89) - the taint-proven case is reported at CRITICAL by narvy-go-sqli-request-taint.
1551
+ Use parameterised queries with placeholders regardless.'
1552
+ metadata:
1553
+ cwe:
1554
+ - 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
1555
+ owasp:
1556
+ - A03:2021 - Injection
1557
+ category: security
1558
+ technology:
1559
+ - go
1560
+ confidence: MEDIUM
1561
+ subcategory:
1562
+ - audit
1563
+ likelihood: MEDIUM
1564
+ impact: HIGH
1565
+ severity: WARNING
1566
+ languages:
1567
+ - go
1568
+ - id: narvy-go-sqlx-tainted-query
1569
+ patterns:
1570
+ - pattern-inside: "import (\n ...\n $X \"github.com/jmoiron/sqlx\"\n ...\n)\n...\n"
1571
+ - pattern-either:
1572
+ - pattern: $DB.Queryx(fmt.Sprintf("...", ...))
1573
+ - pattern: $DB.MustExec(fmt.Sprintf("...", ...))
1574
+ - pattern: $DB.Queryx($A + $B)
1575
+ - pattern: $DB.QueryxContext($CTX, fmt.Sprintf("...", ...))
1576
+ - pattern: $DB.QueryxContext($CTX, $A + $B)
1577
+ - pattern-not: $DB.Queryx("..." + "...")
1578
+ - pattern-not: $DB.MustExec("..." + "...")
1579
+ - pattern-not: $DB.QueryxContext($CTX, "..." + "...")
1580
+ message: 'Dynamic SQL via sqlx: query built with `fmt.Sprintf` or concatenation. Code-smell audit signal (HIGH), not a
1581
+ proven CRITICAL - without data-flow proof the operand may be an internal constant. IF request-derived it is SQL
1582
+ injection (CWE-89); the taint-proven case is at CRITICAL in narvy-go-sqli-request-taint. sqlx supports named
1583
+ parameters and `?` placeholders - use those instead.'
1584
+ metadata:
1585
+ cwe:
1586
+ - 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
1587
+ owasp:
1588
+ - A03:2021 - Injection
1589
+ category: security
1590
+ technology:
1591
+ - go
1592
+ - sqlx
1593
+ confidence: MEDIUM
1594
+ subcategory:
1595
+ - audit
1596
+ likelihood: MEDIUM
1597
+ impact: HIGH
1598
+ severity: WARNING
1599
+ languages:
1600
+ - go
1601
+ - id: narvy.go.sqli.queryrow-exec-concat.queryrow-exec-concat
1602
+ message: 'Dynamic SQL: `db.QueryRow()` / `db.Exec()` called with a query string built by concatenation or `fmt.Sprintf`
1603
+ instead of using a `?`/`$1` placeholder with a separate argument. Code-smell audit signal (HIGH), not a proven
1604
+ CRITICAL - without data-flow proof the interpolated operand may be an internal constant (e.g. a migration table
1605
+ name) rather than user input. IF request-derived the operand is interpreted as SQL syntax, not data - SQL injection
1606
+ (CWE-89), reported at CRITICAL by narvy-go-sqli-request-taint. Move the value to a bound parameter regardless:
1607
+ `db.QueryRow("... WHERE id = ?", id)`.'
1608
+ severity: WARNING
1609
+ languages:
1610
+ - go
1611
+ metadata:
1612
+ category: security
1613
+ cwe:
1614
+ - 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
1615
+ owasp:
1616
+ - A03:2021 - Injection
1617
+ subcategory:
1618
+ - audit
1619
+ likelihood: MEDIUM
1620
+ impact: HIGH
1621
+ confidence: MEDIUM
1622
+ technology:
1623
+ - go
1624
+ patterns:
1625
+ - pattern-either:
1626
+ - pattern: $DB.QueryRow("..." + $X)
1627
+ - pattern: $DB.QueryRow("..." + $X + "...")
1628
+ - pattern: $DB.QueryRow(fmt.Sprintf("...%s...", $X))
1629
+ - pattern: $DB.Exec("..." + $X)
1630
+ - pattern: $DB.Exec("..." + $X + "...")
1631
+ - pattern: $DB.Exec(fmt.Sprintf("...%s...", $X))
1632
+ # Taint-proven CRITICAL tier: the two rules below use mode:taint to fire only when a request-derived value reaches the sink.
1633
+ - id: narvy-go-sqli-request-taint
1634
+ mode: taint
1635
+ message: 'SQL injection (taint-proven): a value read from the HTTP request reaches a `db.Query`/`Exec`/`QueryRow`
1636
+ (or sqlx) call as part of a dynamically-built query string, not as a bound `?`/`$1` parameter. The attacker
1637
+ controls SQL syntax (CWE-89). Bind the value: `db.Query("... WHERE id = ?", id)`.'
1638
+ severity: ERROR
1639
+ languages:
1640
+ - go
1641
+ metadata:
1642
+ category: security
1643
+ cwe:
1644
+ - 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
1645
+ owasp:
1646
+ - A03:2021 - Injection
1647
+ subcategory:
1648
+ - vuln
1649
+ likelihood: HIGH
1650
+ impact: HIGH
1651
+ confidence: HIGH
1652
+ technology:
1653
+ - go
1654
+ pattern-sources:
1655
+ - patterns:
1656
+ - pattern-either:
1657
+ - pattern: $R.URL.Query().Get(...)
1658
+ - pattern: $R.URL.Query()
1659
+ - pattern: $R.FormValue(...)
1660
+ - pattern: $R.PostFormValue(...)
1661
+ - pattern: $R.Form.Get(...)
1662
+ - pattern: $R.PostForm.Get(...)
1663
+ - pattern: $R.Header.Get(...)
1664
+ - pattern: $C.Param(...)
1665
+ - pattern: $C.PostForm(...)
1666
+ - pattern: $C.QueryParam(...)
1667
+ - pattern: $C.DefaultQuery(...)
1668
+ - pattern: $C.GetHeader(...)
1669
+ - pattern: $C.Params(...)
1670
+ - pattern: mux.Vars($R)[...]
1671
+ - pattern: chi.URLParam(...)
1672
+ pattern-sinks:
1673
+ - patterns:
1674
+ - pattern-either:
1675
+ - pattern: $DB.Query($X)
1676
+ - pattern: $DB.QueryContext($C, $X)
1677
+ - pattern: $DB.QueryRow($X)
1678
+ - pattern: $DB.QueryRowContext($C, $X)
1679
+ - pattern: $DB.Exec($X)
1680
+ - pattern: $DB.ExecContext($C, $X)
1681
+ - pattern: $DB.Queryx($X)
1682
+ - pattern: $DB.QueryxContext($C, $X)
1683
+ - pattern: $DB.MustExec($X)
1684
+ - pattern-not: $DB.Query("...")
1685
+ - pattern-not: $DB.QueryContext($C, "...")
1686
+ - pattern-not: $DB.QueryRow("...")
1687
+ - pattern-not: $DB.QueryRowContext($C, "...")
1688
+ - pattern-not: $DB.Exec("...")
1689
+ - pattern-not: $DB.ExecContext($C, "...")
1690
+ - pattern-not: $DB.Queryx("...")
1691
+ - pattern-not: $DB.QueryxContext($C, "...")
1692
+ - pattern-not: $DB.MustExec("...")
1693
+ - id: narvy-go-xss-template-html-request-taint
1694
+ mode: taint
1695
+ message: 'Reflected/stored XSS (taint-proven): a value read from the HTTP request is cast to `template.HTML` (or
1696
+ `JS` / `URL` / `HTMLAttr` / `CSS`), suppressing `html/template` auto-escaping and emitting attacker input verbatim
1697
+ (CWE-79). Sanitise with `bluemonday` before casting, or drop the cast and let `html/template` escape.'
1698
+ severity: ERROR
1699
+ languages:
1700
+ - go
1701
+ metadata:
1702
+ category: security
1703
+ cwe:
1704
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
1705
+ owasp:
1706
+ - A03:2021 - Injection
1707
+ subcategory:
1708
+ - vuln
1709
+ likelihood: HIGH
1710
+ impact: HIGH
1711
+ confidence: HIGH
1712
+ technology:
1713
+ - go
1714
+ - html/template
1715
+ pattern-sources:
1716
+ - patterns:
1717
+ - pattern-either:
1718
+ - pattern: $R.URL.Query().Get(...)
1719
+ - pattern: $R.URL.Query()
1720
+ - pattern: $R.FormValue(...)
1721
+ - pattern: $R.PostFormValue(...)
1722
+ - pattern: $R.Form.Get(...)
1723
+ - pattern: $R.PostForm.Get(...)
1724
+ - pattern: $R.Header.Get(...)
1725
+ - pattern: $C.Param(...)
1726
+ - pattern: $C.PostForm(...)
1727
+ - pattern: $C.QueryParam(...)
1728
+ - pattern: $C.DefaultQuery(...)
1729
+ - pattern: $C.GetHeader(...)
1730
+ - pattern: $C.Params(...)
1731
+ - pattern: mux.Vars($R)[...]
1732
+ - pattern: chi.URLParam(...)
1733
+ pattern-sinks:
1734
+ - patterns:
1735
+ - pattern-either:
1736
+ - pattern: template.HTML($X)
1737
+ - pattern: template.JS($X)
1738
+ - pattern: template.URL($X)
1739
+ - pattern: template.HTMLAttr($X)
1740
+ - pattern: template.CSS($X)
1741
+ - id: narvy-go-ssrf-http-get
1742
+ patterns:
1743
+ - pattern-either:
1744
+ - pattern: http.Get($URL)
1745
+ - pattern: http.Head($URL)
1746
+ - pattern: http.Post($URL, $CT, $BODY)
1747
+ - pattern: http.PostForm($URL, $DATA)
1748
+ - pattern-not: http.Get("...")
1749
+ - pattern-not: http.Head("...")
1750
+ - pattern-not: http.Post("...", $CT, $BODY)
1751
+ - pattern-not: http.PostForm("...", $DATA)
1752
+ - pattern-not: http.Get(os.Getenv($X))
1753
+ - pattern-not: http.Get($X + "/...")
1754
+ - pattern-either:
1755
+ - pattern-inside: "func $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1756
+ - pattern-inside: "func $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1757
+ - pattern-inside: "func ($RECV $T) $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1758
+ - pattern-inside: "func ($RECV $T) $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1759
+ - pattern-inside: "func $FN(..., $C echo.Context, ...) $RET {\n ...\n}\n"
1760
+ - pattern-inside: "func $FN(..., $C *gin.Context, ...) {\n ...\n}\n"
1761
+ - pattern-inside: "func $FN($SAFETY common.Safety, $PAYLOAD string, $X interface{}) ($A, $B, $C) {\n ...\n}\n"
1762
+ message: 'SSRF risk: `net/http` request to a non-literal URL inside an HTTP handler. If `$URL` comes from a request parameter
1763
+ the attacker can scan internal services, hit cloud-metadata endpoints (169.254.169.254), or trigger arbitrary outbound
1764
+ traffic. Validate the URL against an allow-list of hosts before requesting.'
1765
+ metadata:
1766
+ cwe:
1767
+ - 'CWE-918: Server-Side Request Forgery (SSRF)'
1768
+ owasp:
1769
+ - A10:2021 - Server-Side Request Forgery (SSRF)
1770
+ category: security
1771
+ technology:
1772
+ - go
1773
+ confidence: HIGH
1774
+ subcategory:
1775
+ - vuln
1776
+ likelihood: HIGH
1777
+ impact: HIGH
1778
+ severity: ERROR
1779
+ languages:
1780
+ - go
1781
+ - id: narvy-go-ssrf-newrequest-tainted
1782
+ patterns:
1783
+ - pattern-either:
1784
+ - pattern: http.NewRequest($METHOD, $URL, $BODY)
1785
+ - pattern: http.NewRequestWithContext($CTX, $METHOD, $URL, $BODY)
1786
+ - pattern-not: http.NewRequest($METHOD, "...", $BODY)
1787
+ - pattern-not: http.NewRequestWithContext($CTX, $METHOD, "...", $BODY)
1788
+ - pattern-not: http.NewRequest($METHOD, os.Getenv($X), $BODY)
1789
+ - pattern-not: http.NewRequestWithContext($CTX, $METHOD, os.Getenv($X), $BODY)
1790
+ - pattern-either:
1791
+ - pattern-inside: "func $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1792
+ - pattern-inside: "func $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1793
+ - pattern-inside: "func ($RECV $T) $FN(..., $R *http.Request, ...) {\n ...\n}\n"
1794
+ - pattern-inside: "func ($RECV $T) $FN(..., $W http.ResponseWriter, ...) {\n ...\n}\n"
1795
+ - pattern-inside: "func $FN(..., $C echo.Context, ...) $RET {\n ...\n}\n"
1796
+ - pattern-inside: "func $FN(..., $C *gin.Context, ...) {\n ...\n}\n"
1797
+ message: 'SSRF risk: `http.NewRequest` URL is a non-literal expression inside an HTTP handler. Confirm the URL is validated
1798
+ against an allow-list before this request is sent via `client.Do(req)`.'
1799
+ metadata:
1800
+ cwe:
1801
+ - 'CWE-918: Server-Side Request Forgery (SSRF)'
1802
+ owasp:
1803
+ - A10:2021 - Server-Side Request Forgery (SSRF)
1804
+ category: security
1805
+ technology:
1806
+ - go
1807
+ confidence: MEDIUM
1808
+ subcategory:
1809
+ - audit
1810
+ likelihood: MEDIUM
1811
+ impact: HIGH
1812
+ severity: WARNING
1813
+ languages:
1814
+ - go
1815
+ - id: narvy-go-ssrf-client-get
1816
+ patterns:
1817
+ - pattern-either:
1818
+ - pattern: $CLIENT.Get($URL)
1819
+ - pattern: $CLIENT.Post($URL, $CT, $BODY)
1820
+ - pattern: $CLIENT.PostForm($URL, $DATA)
1821
+ - pattern-inside: '$CLIENT := &http.Client{...}
1822
+
1823
+ ...
1824
+
1825
+ '
1826
+ - pattern-not: $CLIENT.Get("...")
1827
+ - pattern-not: $CLIENT.Post("...", $CT, $BODY)
1828
+ - pattern-not: $CLIENT.PostForm("...", $DATA)
1829
+ message: 'SSRF risk: custom `http.Client` is invoked with a non-literal URL. Same risk as `http.Get` - validate against
1830
+ an allow-list of hosts.'
1831
+ metadata:
1832
+ cwe:
1833
+ - 'CWE-918: Server-Side Request Forgery (SSRF)'
1834
+ owasp:
1835
+ - A10:2021 - Server-Side Request Forgery (SSRF)
1836
+ category: security
1837
+ technology:
1838
+ - go
1839
+ confidence: MEDIUM
1840
+ subcategory:
1841
+ - audit
1842
+ likelihood: MEDIUM
1843
+ impact: HIGH
1844
+ severity: WARNING
1845
+ languages:
1846
+ - go
1847
+ - id: narvy.go.ssrf.net-dial-tainted-host.net-dial-tainted-host
1848
+ message: '`net.Dial()` / `net.DialTimeout()` called with a host/address built from request data. This lets an attacker make
1849
+ the server open a raw TCP connection to any internal host/port it can reach (cloud metadata endpoints, internal admin
1850
+ ports, redis/memcache with no auth) - SSRF at the socket level (CWE-918). Validate the target against a strict allow-list,
1851
+ and resolve+re-check the IP is not in a private/link-local range before connecting.'
1852
+ severity: ERROR
1853
+ languages:
1854
+ - go
1855
+ metadata:
1856
+ category: security
1857
+ cwe:
1858
+ - 'CWE-918: Server-Side Request Forgery (SSRF)'
1859
+ owasp:
1860
+ - A10:2021 - Server-Side Request Forgery (SSRF)
1861
+ subcategory:
1862
+ - vuln
1863
+ likelihood: MEDIUM
1864
+ impact: HIGH
1865
+ confidence: MEDIUM
1866
+ technology:
1867
+ - go
1868
+ patterns:
1869
+ - pattern-either:
1870
+ - pattern: net.Dial("tcp", $R.URL.Query().Get(...))
1871
+ - pattern: net.Dial("tcp", $R.FormValue(...))
1872
+ - pattern: net.DialTimeout("tcp", $R.URL.Query().Get(...), ...)
1873
+ - id: narvy.go.ssrf.outbound-url-host-from-request-concat.outbound-url-host-from-request-concat
1874
+ message: An outbound URL is assembled by concatenating a fixed scheme onto a host taken directly from request data, then
1875
+ handed to an HTTP client. Same SSRF risk as passing the tainted value straight to `http.Get` (CWE-918) - the concatenation
1876
+ doesn't add any validation. Resolve and check the host against an allow-list before building the request.
1877
+ severity: ERROR
1878
+ languages:
1879
+ - go
1880
+ metadata:
1881
+ category: security
1882
+ cwe:
1883
+ - 'CWE-918: Server-Side Request Forgery (SSRF)'
1884
+ owasp:
1885
+ - A10:2021 - Server-Side Request Forgery (SSRF)
1886
+ subcategory:
1887
+ - vuln
1888
+ likelihood: MEDIUM
1889
+ impact: HIGH
1890
+ confidence: MEDIUM
1891
+ technology:
1892
+ - go
1893
+ patterns:
1894
+ - pattern-either:
1895
+ - pattern: '$URL := "http://" + $R.URL.Query().Get(...)
1896
+
1897
+ ...
1898
+
1899
+ http.Get($URL)
1900
+
1901
+ '
1902
+ - pattern: '$URL := "https://" + $R.FormValue(...)
1903
+
1904
+ ...
1905
+
1906
+ http.Get($URL)
1907
+
1908
+ '
1909
+ - id: narvy.go.transport.insecure-skip-verify.insecure-skip-verify
1910
+ message: '`InsecureSkipVerify: true` in a TLS (or TLS-forwarding) config struct literal disables TLS certificate validation
1911
+ entirely. Any certificate - including one presented by an attacker-in-the-middle - will be accepted, defeating the point
1912
+ of TLS (CWE-295). Remove the field (defaults to verifying) or, for pinning use cases, implement `VerifyConnection`/`VerifyPeerCertificate`
1913
+ instead.'
1914
+ severity: ERROR
1915
+ languages:
1916
+ - go
1917
+ metadata:
1918
+ category: security
1919
+ cwe:
1920
+ - 'CWE-295: Improper Certificate Validation'
1921
+ owasp:
1922
+ - A02:2021 - Cryptographic Failures
1923
+ subcategory:
1924
+ - vuln
1925
+ likelihood: MEDIUM
1926
+ impact: HIGH
1927
+ confidence: HIGH
1928
+ technology:
1929
+ - go
1930
+ paths:
1931
+ exclude:
1932
+ - '**/*_test.go'
1933
+ - '**/testdata/**'
1934
+ - '**/protocoltest/**'
1935
+ patterns:
1936
+ - pattern: '$T{..., InsecureSkipVerify: true, ...}
1937
+
1938
+ '
1939
+ - id: narvy.go.transport.tls-insecure-skip-verify.tls-insecure-skip-verify
1940
+ message: '`InsecureSkipVerify` field on a TLS config is set to `true` via field assignment (not the struct-literal form).
1941
+ This disables certificate validation on that connection - any certificate is accepted, defeating TLS (CWE-295). Remove
1942
+ the assignment (defaults to `false`).'
1943
+ severity: ERROR
1944
+ languages:
1945
+ - go
1946
+ metadata:
1947
+ category: security
1948
+ cwe:
1949
+ - 'CWE-295: Improper Certificate Validation'
1950
+ owasp:
1951
+ - A02:2021 - Cryptographic Failures
1952
+ subcategory:
1953
+ - vuln
1954
+ likelihood: MEDIUM
1955
+ impact: HIGH
1956
+ confidence: HIGH
1957
+ technology:
1958
+ - go
1959
+ paths:
1960
+ exclude:
1961
+ - '**/*_test.go'
1962
+ - '**/testdata/**'
1963
+ - '**/protocoltest/**'
1964
+ pattern: $CFG.InsecureSkipVerify = true
1965
+ - id: narvy.go.tls.missing-ssl-minversion.missing-ssl-minversion
1966
+ message: '`tls.Config{}` constructed with no `MinVersion` field. Go 1.18+ defaults to TLS 1.2 in the absence of this field,
1967
+ so this is a hardening advisory rather than a live vuln by itself - set `MinVersion: tls.VersionTLS12` (or 1.3) explicitly
1968
+ so the floor cannot silently drop on an older Go toolchain or be overridden elsewhere in the struct literal.'
1969
+ severity: INFO
1970
+ languages:
1971
+ - go
1972
+ metadata:
1973
+ category: security
1974
+ cwe:
1975
+ - 'CWE-326: Inadequate Encryption Strength'
1976
+ owasp:
1977
+ - A02:2021 - Cryptographic Failures
1978
+ subcategory:
1979
+ - audit
1980
+ likelihood: LOW
1981
+ impact: MEDIUM
1982
+ confidence: LOW
1983
+ technology:
1984
+ - go
1985
+ patterns:
1986
+ - pattern: 'tls.Config{...}
1987
+
1988
+ '
1989
+ - pattern-not: 'tls.Config{..., MinVersion: $V, ...}
1990
+
1991
+ '
1992
+ - id: narvy.go.tls.explicit-weak-min-version.explicit-weak-min-version
1993
+ message: '`tls.Config.MinVersion` explicitly set to SSLv3/TLS1.0/TLS1.1. All three have known practical attacks (POODLE,
1994
+ BEAST) and are disallowed by PCI-DSS; explicitly pinning the floor there (rather than merely omitting it) is a real, exploitable
1995
+ defect (CWE-326). Set `MinVersion: tls.VersionTLS12` or higher.'
1996
+ severity: ERROR
1997
+ languages:
1998
+ - go
1999
+ metadata:
2000
+ category: security
2001
+ cwe:
2002
+ - 'CWE-326: Inadequate Encryption Strength'
2003
+ owasp:
2004
+ - A02:2021 - Cryptographic Failures
2005
+ subcategory:
2006
+ - vuln
2007
+ likelihood: MEDIUM
2008
+ impact: HIGH
2009
+ confidence: HIGH
2010
+ technology:
2011
+ - go
2012
+ patterns:
2013
+ - pattern-either:
2014
+ - pattern: 'tls.Config{..., MinVersion: tls.VersionSSL30, ...}
2015
+
2016
+ '
2017
+ - pattern: 'tls.Config{..., MinVersion: tls.VersionTLS10, ...}
2018
+
2019
+ '
2020
+ - pattern: 'tls.Config{..., MinVersion: tls.VersionTLS11, ...}
2021
+
2022
+ '
2023
+ - id: narvy.go.tls.weak-cipher-suite-allowed.weak-cipher-suite-allowed
2024
+ message: '`tls.Config.CipherSuites` explicitly includes a CBC-mode or RC4 cipher suite. Including these re-enables ciphers
2025
+ modern TLS stacks disable by default because of padding-oracle (Lucky13) and keystream-bias attacks (CWE-327). Stick to
2026
+ the Go 1.18+ default cipher suite list (leave `CipherSuites` unset) or select only AEAD suites (`TLS_*_WITH_AES_*_GCM_*`,
2027
+ `TLS_*_CHACHA20_POLY1305_*`).'
2028
+ severity: WARNING
2029
+ languages:
2030
+ - go
2031
+ metadata:
2032
+ category: security
2033
+ cwe:
2034
+ - 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
2035
+ owasp:
2036
+ - A02:2021 - Cryptographic Failures
2037
+ subcategory:
2038
+ - vuln
2039
+ likelihood: LOW
2040
+ impact: MEDIUM
2041
+ confidence: MEDIUM
2042
+ technology:
2043
+ - go
2044
+ patterns:
2045
+ - pattern-either:
2046
+ - pattern: tls.TLS_RSA_WITH_RC4_128_SHA
2047
+ - pattern: tls.TLS_RSA_WITH_AES_128_CBC_SHA
2048
+ - pattern: tls.TLS_RSA_WITH_AES_256_CBC_SHA
2049
+ - id: narvy.go.transport.grpc-dial-insecure-credentials.grpc-dial-insecure-credentials
2050
+ message: '`grpc.Dial()` (or `grpc.NewClient()`) called with `grpc.WithInsecure()`/`insecure.NewCredentials()`, disabling
2051
+ transport encryption on the gRPC connection entirely - every request/response (including any auth token in metadata) crosses
2052
+ the wire in plaintext (CWE-319). Use `grpc.WithTransportCredentials(credentials.NewTLS(...))` for any connection that
2053
+ leaves the local host.'
2054
+ severity: WARNING
2055
+ languages:
2056
+ - go
2057
+ metadata:
2058
+ category: security
2059
+ cwe:
2060
+ - 'CWE-319: Cleartext Transmission of Sensitive Information'
2061
+ owasp:
2062
+ - A02:2021 - Cryptographic Failures
2063
+ subcategory:
2064
+ - vuln
2065
+ likelihood: LOW
2066
+ impact: HIGH
2067
+ confidence: MEDIUM
2068
+ technology:
2069
+ - go
2070
+ patterns:
2071
+ - pattern-either:
2072
+ - pattern: grpc.Dial($ADDR, ..., grpc.WithInsecure(), ...)
2073
+ - pattern: grpc.Dial($ADDR, ..., grpc.WithTransportCredentials(insecure.NewCredentials()), ...)
2074
+ - pattern: grpc.NewClient($ADDR, ..., grpc.WithTransportCredentials(insecure.NewCredentials()), ...)
2075
+ - id: narvy.go.transport.grpc-serve-no-tls-credentials.grpc-serve-no-tls-credentials
2076
+ message: '`grpc.NewServer()` constructed with no transport credentials, so the server accepts plaintext connections. Any
2077
+ auth metadata or payload sent to it crosses the wire unencrypted (CWE-319). Pass `grpc.Creds(credentials.NewTLS(...))`
2078
+ unless the server is deliberately scoped to a trusted local/mTLS-terminated network.'
2079
+ severity: INFO
2080
+ languages:
2081
+ - go
2082
+ metadata:
2083
+ category: security
2084
+ cwe:
2085
+ - 'CWE-319: Cleartext Transmission of Sensitive Information'
2086
+ owasp:
2087
+ - A02:2021 - Cryptographic Failures
2088
+ subcategory:
2089
+ - audit
2090
+ likelihood: LOW
2091
+ impact: MEDIUM
2092
+ confidence: LOW
2093
+ technology:
2094
+ - go
2095
+ patterns:
2096
+ - pattern: grpc.NewServer()
2097
+ - id: narvy.go.open-redirect.shared-url-struct-mutation.shared-url-struct-mutation
2098
+ message: A `url.URL` struct's `Host` or `Scheme` field is mutated using a value that traces back to request data (query
2099
+ param, header, form value). Building a proxy/redirect target this way lets an attacker redirect the request to an arbitrary
2100
+ external host by controlling that value (CWE-601 / SSRF-adjacent when the mutated URL is then fetched server-side rather
2101
+ than redirected to). Validate the host against a fixed allow-list before assigning it.
2102
+ severity: WARNING
2103
+ languages:
2104
+ - go
2105
+ metadata:
2106
+ category: security
2107
+ cwe:
2108
+ - 'CWE-601: URL Redirection to Untrusted Site (''Open Redirect'')'
2109
+ owasp:
2110
+ - A01:2021 - Broken Access Control
2111
+ subcategory:
2112
+ - vuln
2113
+ likelihood: LOW
2114
+ impact: MEDIUM
2115
+ confidence: LOW
2116
+ technology:
2117
+ - go
2118
+ patterns:
2119
+ - pattern-either:
2120
+ - pattern: $U.Host = $R.URL.Query().Get(...)
2121
+ - pattern: $U.Host = $R.FormValue(...)
2122
+ - pattern: $U.Host = $R.Header.Get(...)
2123
+ - pattern: $U.Scheme = $R.URL.Query().Get(...)
2124
+ - pattern: $U.Scheme = $R.FormValue(...)
2125
+ - id: narvy.go.open-redirect.location-header-tainted.location-header-tainted
2126
+ message: '`Location` response header set directly from request data via `Header().Set()`, bypassing `http.Redirect()`''s
2127
+ handling entirely. Same open-redirect risk as `http.Redirect()` with a tainted target (CWE-601): validate against a fixed
2128
+ allow-list before setting the header, or only allow relative paths.'
2129
+ severity: WARNING
2130
+ languages:
2131
+ - go
2132
+ metadata:
2133
+ category: security
2134
+ cwe:
2135
+ - 'CWE-601: URL Redirection to Untrusted Site (''Open Redirect'')'
2136
+ owasp:
2137
+ - A01:2021 - Broken Access Control
2138
+ subcategory:
2139
+ - vuln
2140
+ likelihood: MEDIUM
2141
+ impact: MEDIUM
2142
+ confidence: MEDIUM
2143
+ technology:
2144
+ - go
2145
+ patterns:
2146
+ - pattern-either:
2147
+ - pattern: $W.Header().Set("Location", $R.URL.Query().Get(...))
2148
+ - pattern: $W.Header().Set("Location", $R.FormValue(...))
2149
+ - id: narvy.go.crypto.weak-hash-password.weak-hash-password
2150
+ message: MD5/SHA1 applied to a value whose name indicates a password or credential. Both are fast, unsalted digests, brute-forceable
2151
+ with commodity GPUs (MD5 is also collision-broken). Use `golang.org/x/crypto/bcrypt` or `golang.org/x/crypto/argon2` for
2152
+ credential storage. CWE-916.
2153
+ severity: ERROR
2154
+ languages:
2155
+ - go
2156
+ metadata:
2157
+ cwe:
2158
+ - 'CWE-916: Use of Password Hash With Insufficient Computational Effort'
2159
+ owasp:
2160
+ - A02:2021 - Cryptographic Failures
2161
+ category: security
2162
+ subcategory:
2163
+ - vuln
2164
+ technology:
2165
+ - go
2166
+ likelihood: MEDIUM
2167
+ impact: HIGH
2168
+ confidence: MEDIUM
2169
+ vulnerability_class:
2170
+ - Cryptographic Issues
2171
+ patterns:
2172
+ - pattern-either:
2173
+ - pattern: md5.Sum([]byte($PW))
2174
+ - pattern: sha1.Sum([]byte($PW))
2175
+ - metavariable-regex:
2176
+ metavariable: $PW
2177
+ regex: (?i).*(pass|pwd|secret|credential).*
2178
+ - id: narvy.go.crypto.weak-des-cipher.weak-des-cipher
2179
+ message: '`crypto/des` used to construct a cipher. DES has a brute-forceable 56-bit effective key, and TripleDES (168-bit
2180
+ nominal, ~112-bit effective) is deprecated by NIST for new use and vulnerable to SWEET32 birthday attacks on its 64-bit
2181
+ block size. Use `crypto/aes` with GCM instead. CWE-327.'
2182
+ severity: ERROR
2183
+ languages:
2184
+ - go
2185
+ metadata:
2186
+ cwe:
2187
+ - 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
2188
+ owasp:
2189
+ - A02:2021 - Cryptographic Failures
2190
+ category: security
2191
+ subcategory:
2192
+ - vuln
2193
+ technology:
2194
+ - go
2195
+ likelihood: LOW
2196
+ impact: HIGH
2197
+ confidence: HIGH
2198
+ vulnerability_class:
2199
+ - Cryptographic Issues
2200
+ patterns:
2201
+ - pattern-either:
2202
+ - pattern: des.NewCipher(...)
2203
+ - pattern: des.NewTripleDESCipher(...)
2204
+ - id: narvy.go.crypto.math-rand-security-token.math-rand-security-token
2205
+ message: '`math/rand` (a deterministic PRNG, not cryptographically secure) is used to produce a value assigned to a variable
2206
+ whose name indicates security-sensitive use (token, secret, password, session, nonce, apikey, csrf). Its internal state
2207
+ can be recovered from observed outputs, making future values predictable. Use `crypto/rand` (`rand.Read` / a helper that
2208
+ base64/hex-encodes its bytes) for anything security-relevant. CWE-338.'
2209
+ severity: WARNING
2210
+ languages:
2211
+ - go
2212
+ metadata:
2213
+ cwe:
2214
+ - 'CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)'
2215
+ - 'CWE-330: Use of Insufficiently Random Values'
2216
+ owasp:
2217
+ - A02:2021 - Cryptographic Failures
2218
+ category: security
2219
+ subcategory:
2220
+ - audit
2221
+ technology:
2222
+ - go
2223
+ likelihood: MEDIUM
2224
+ impact: HIGH
2225
+ confidence: MEDIUM
2226
+ vulnerability_class:
2227
+ - Insufficient Entropy
2228
+ patterns:
2229
+ - pattern-either:
2230
+ - pattern: $TOKEN := rand.Int(...)
2231
+ - pattern: $TOKEN := rand.Intn(...)
2232
+ - pattern: $TOKEN := rand.Int63(...)
2233
+ - pattern: $TOKEN = rand.Intn(...)
2234
+ - metavariable-regex:
2235
+ metavariable: $TOKEN
2236
+ regex: (?i).*(token|secret|password|passwd|pwd|session|nonce|apikey|api_key|csrf|auth).*
2237
+ - pattern-not-inside: 'import "crypto/rand"
2238
+
2239
+ ...
2240
+
2241
+ '
2242
+ - id: narvy.go.crypto.use-of-md5.use-of-md5
2243
+ message: '`md5.Sum()` / `md5.New()` used to hash a value. MD5 is cryptographically broken (collision-forgeable) and must
2244
+ not be used for integrity checks, signatures, or password hashing (CWE-327 / CWE-328). Use SHA-256+ for integrity/signing,
2245
+ or `bcrypt`/`argon2`/`scrypt` for passwords.'
2246
+ severity: WARNING
2247
+ languages:
2248
+ - go
2249
+ metadata:
2250
+ category: security
2251
+ cwe:
2252
+ - 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
2253
+ - 'CWE-328: Use of Weak Hash'
2254
+ owasp:
2255
+ - A02:2021 - Cryptographic Failures
2256
+ subcategory:
2257
+ - vuln
2258
+ likelihood: LOW
2259
+ impact: MEDIUM
2260
+ confidence: MEDIUM
2261
+ technology:
2262
+ - go
2263
+ patterns:
2264
+ - pattern-either:
2265
+ - pattern: md5.Sum(...)
2266
+ - pattern: md5.New()
2267
+ - id: narvy.go.crypto.use-of-sha1.use-of-sha1
2268
+ message: '`sha1.Sum()` / `sha1.New()` used to hash a value. SHA-1 has practical collision attacks and should not be used
2269
+ for integrity checks or signatures (CWE-327 / CWE-328). Use SHA-256 or better.'
2270
+ severity: WARNING
2271
+ languages:
2272
+ - go
2273
+ metadata:
2274
+ category: security
2275
+ cwe:
2276
+ - 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
2277
+ - 'CWE-328: Use of Weak Hash'
2278
+ owasp:
2279
+ - A02:2021 - Cryptographic Failures
2280
+ subcategory:
2281
+ - vuln
2282
+ likelihood: LOW
2283
+ impact: LOW
2284
+ confidence: MEDIUM
2285
+ technology:
2286
+ - go
2287
+ patterns:
2288
+ - pattern-either:
2289
+ - pattern: sha1.Sum(...)
2290
+ - pattern: sha1.New()
2291
+ - id: narvy.go.crypto.math-random-used.math-random-used
2292
+ message: '`math/rand` used to generate a value. `math/rand`''s default source is not cryptographically secure and is predictable
2293
+ once seeded - using it for anything security-sensitive (tokens, session IDs, password reset codes, nonces, keys) lets
2294
+ an attacker predict or brute-force the value (CWE-338). Use `crypto/rand` for anything security-sensitive; this broad
2295
+ detector deliberately also fires on non-sensitive uses (jitter, load-balancing, retry backoff), which you can treat
2296
+ as advisory. Covers both the classic `math/rand` API
2297
+ (`rand.Intn`/`.Int63`/`.Int31`/`.Float64`/`.Int`) and `math/rand/v2`''s generic form (`rand.N`/`.IntN`/`.Int32N`/ `.Int64N`/`.Uint32N`/`.Uint64N`/...)
2298
+ via a `$FUNC` wildcard instead of a fixed identifier list, and matches through an import alias (`mathrand "math/rand"`
2299
+ then `mathrand.Int63()`) by binding the alias from the import statement itself rather than assuming the package is always
2300
+ referenced as literal `rand`.'
2301
+ severity: INFO
2302
+ languages:
2303
+ - go
2304
+ metadata:
2305
+ category: security
2306
+ cwe:
2307
+ - 'CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)'
2308
+ owasp:
2309
+ - A02:2021 - Cryptographic Failures
2310
+ subcategory:
2311
+ - audit
2312
+ likelihood: LOW
2313
+ impact: MEDIUM
2314
+ confidence: LOW
2315
+ technology:
2316
+ - go
2317
+ patterns:
2318
+ - pattern-either:
2319
+ - patterns:
2320
+ - pattern: rand.$FUNC(...)
2321
+ - pattern-not-inside: 'import "crypto/rand"
2322
+
2323
+ ...
2324
+
2325
+ '
2326
+ - patterns:
2327
+ - pattern: $ALIAS.$FUNC(...)
2328
+ - pattern-inside: 'import $ALIAS "$MODPATH"
2329
+
2330
+ ...
2331
+
2332
+ '
2333
+ - metavariable-regex:
2334
+ metavariable: $MODPATH
2335
+ regex: ^math/rand(/v[0-9]+)?$
2336
+ - id: narvy.go.crypto.rc4-cipher-constructed.rc4-cipher-constructed
2337
+ message: '`rc4.NewCipher()` used. RC4 has known keystream biases that break confidentiality in practice and is disabled
2338
+ by default in modern TLS stacks for that reason (CWE-327). Use AES-GCM or ChaCha20-Poly1305.'
2339
+ severity: ERROR
2340
+ languages:
2341
+ - go
2342
+ metadata:
2343
+ category: security
2344
+ cwe:
2345
+ - 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
2346
+ owasp:
2347
+ - A02:2021 - Cryptographic Failures
2348
+ subcategory:
2349
+ - vuln
2350
+ likelihood: LOW
2351
+ impact: HIGH
2352
+ confidence: HIGH
2353
+ technology:
2354
+ - go
2355
+ pattern: rc4.NewCipher(...)
2356
+ - id: narvy.go.crypto.rsa-key-undersized-modulus.rsa-key-undersized-modulus
2357
+ message: '`rsa.GenerateKey()` called with a key size below 2048 bits. RSA keys under 2048 bits are within reach of modern
2358
+ factoring attacks (CWE-326). Generate at least a 2048-bit key (3072+ recommended for new systems).'
2359
+ severity: WARNING
2360
+ languages:
2361
+ - go
2362
+ metadata:
2363
+ category: security
2364
+ cwe:
2365
+ - 'CWE-326: Inadequate Encryption Strength'
2366
+ owasp:
2367
+ - A02:2021 - Cryptographic Failures
2368
+ subcategory:
2369
+ - vuln
2370
+ likelihood: LOW
2371
+ impact: HIGH
2372
+ confidence: HIGH
2373
+ technology:
2374
+ - go
2375
+ patterns:
2376
+ - pattern: rsa.GenerateKey($RAND, $BITS)
2377
+ - metavariable-comparison:
2378
+ metavariable: $BITS
2379
+ comparison: $BITS < 2048
2380
+ - id: narvy.go.crypto.aes-cbc-static-iv.aes-cbc-static-iv
2381
+ message: '`cipher.NewCBCEncrypter()` called with an IV that is a hardcoded literal byte-array instead of a freshly generated
2382
+ random value. A reused/predictable IV breaks CBC''s semantic-security guarantee and leaks information about the plaintext
2383
+ across messages (CWE-329). Draw the IV from `crypto/rand` for every encryption.'
2384
+ severity: ERROR
2385
+ languages:
2386
+ - go
2387
+ metadata:
2388
+ category: security
2389
+ cwe:
2390
+ - 'CWE-329: Generation of Predictable IV with CBC Mode'
2391
+ owasp:
2392
+ - A02:2021 - Cryptographic Failures
2393
+ subcategory:
2394
+ - vuln
2395
+ likelihood: MEDIUM
2396
+ impact: HIGH
2397
+ confidence: MEDIUM
2398
+ technology:
2399
+ - go
2400
+ patterns:
2401
+ - pattern: cipher.NewCBCEncrypter($BLOCK, $IV)
2402
+ - pattern-inside: 'var $IV = [...]byte{...}
2403
+
2404
+ ...
2405
+
2406
+ '
2407
+ - id: narvy.go.crypto.sha224-checksum-used.sha224-checksum-used
2408
+ message: '`sha256.Sum224()` / `sha256.New224()` used. SHA-224 is not broken, but it is a truncated-output variant with no
2409
+ benefit over SHA-256 on 64-bit hardware and is easy to confuse with the weaker SHA-1/MD5 family in review (CWE-327 advisory,
2410
+ not exploitable by itself). Use SHA-256 unless you have a specific length-constrained wire-format requirement.'
2411
+ severity: INFO
2412
+ languages:
2413
+ - go
2414
+ metadata:
2415
+ category: security
2416
+ cwe:
2417
+ - 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
2418
+ owasp:
2419
+ - A02:2021 - Cryptographic Failures
2420
+ subcategory:
2421
+ - audit
2422
+ likelihood: LOW
2423
+ impact: LOW
2424
+ confidence: LOW
2425
+ technology:
2426
+ - go
2427
+ patterns:
2428
+ - pattern-either:
2429
+ - pattern: sha256.Sum224(...)
2430
+ - pattern: sha256.New224()
2431
+ - id: narvy-go-xss-template-html-cast
2432
+ patterns:
2433
+ - pattern-either:
2434
+ - pattern: template.HTML($X)
2435
+ - pattern: template.JS($X)
2436
+ - pattern: template.URL($X)
2437
+ - pattern: template.HTMLAttr($X)
2438
+ - pattern: template.CSS($X)
2439
+ - pattern-not: template.HTML("...")
2440
+ - pattern-not: template.JS("...")
2441
+ - pattern-not: template.URL("...")
2442
+ - pattern-not: template.HTMLAttr("...")
2443
+ - pattern-not: template.CSS("...")
2444
+ # Excludes provably-safe sources: just-built strings.Builder/bytes.Buffer `.String()` and escaper output (html.EscapeString / template.HTMLEscapeString / url.QueryEscape).
2445
+ - pattern-not: template.HTML($SB.String())
2446
+ - pattern-not: template.JS($SB.String())
2447
+ - pattern-not: template.URL($SB.String())
2448
+ - pattern-not: template.HTMLAttr($SB.String())
2449
+ - pattern-not: template.CSS($SB.String())
2450
+ - pattern-not: template.HTML(html.EscapeString(...))
2451
+ - pattern-not: template.HTML(template.HTMLEscapeString(...))
2452
+ - pattern-not: template.HTMLAttr(html.EscapeString(...))
2453
+ - pattern-not: template.HTMLAttr(template.HTMLEscapeString(...))
2454
+ - pattern-not: template.JS(template.JSEscapeString(...))
2455
+ - pattern-not: template.URL(url.QueryEscape(...))
2456
+ - pattern-not: template.URL(html.EscapeString(...))
2457
+ message: 'Non-literal value cast to `template.HTML` (or `JS` / `URL` / `HTMLAttr` / `CSS`), which suppresses Go''s
2458
+ auto-escaping in `html/template`. This is a HEURISTIC audit signal, not a proven exploit: without data-flow proof
2459
+ we cannot tell a safe internal string from attacker input. IF the value is user-controlled it is stored/reflected
2460
+ XSS (CWE-79) - sanitise with `bluemonday` (or equivalent) before casting, or drop the cast and let `html/template`
2461
+ escape. The taint-proven case is reported separately at CRITICAL by narvy-go-xss-template-html-request-taint.'
2462
+ metadata:
2463
+ cwe:
2464
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2465
+ owasp:
2466
+ - A03:2021 - Injection
2467
+ category: security
2468
+ technology:
2469
+ - go
2470
+ - html/template
2471
+ confidence: LOW
2472
+ subcategory:
2473
+ - audit
2474
+ likelihood: LOW
2475
+ impact: HIGH
2476
+ severity: INFO
2477
+ languages:
2478
+ - go
2479
+ - id: narvy-go-xss-responsewriter-bytes
2480
+ patterns:
2481
+ - pattern-either:
2482
+ - pattern: $W.Write([]byte($X))
2483
+ - pattern-not: $W.Write([]byte("..."))
2484
+ - pattern-either:
2485
+ - pattern-inside: "func $FN($W http.ResponseWriter, ...) {\n ...\n}\n"
2486
+ - pattern-inside: "func ($RECV $T) $FN($W http.ResponseWriter, ...) {\n ...\n}\n"
2487
+ message: 'XSS risk: `ResponseWriter.Write([]byte(x))` with a non-literal value `x`. The response body is written verbatim
2488
+ to the browser without escaping. If `x` originates from a request parameter this is reflected XSS. Use `html/template`
2489
+ to render dynamic content.'
2490
+ metadata:
2491
+ cwe:
2492
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2493
+ owasp:
2494
+ - A03:2021 - Injection
2495
+ category: security
2496
+ technology:
2497
+ - go
2498
+ confidence: MEDIUM
2499
+ subcategory:
2500
+ - audit
2501
+ likelihood: MEDIUM
2502
+ impact: MEDIUM
2503
+ severity: WARNING
2504
+ languages:
2505
+ - go
2506
+ - id: narvy.go.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter
2507
+ message: '`w.Write(...)` writes raw bytes to the `http.ResponseWriter`. This is safe for JSON/binary responses and is
2508
+ idiomatic Go; it is only XSS when the bytes carry attacker-controlled data into an HTML response without escaping (CWE-79).
2509
+ We cannot prove taint here, so this is a review pointer, not a confirmed finding: if the value is request-derived and the
2510
+ response is HTML, render through `html/template` (auto-escapes) instead. Ignore for JSON/`application/json` handlers.'
2511
+ severity: INFO
2512
+ languages:
2513
+ - go
2514
+ metadata:
2515
+ category: security
2516
+ cwe:
2517
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2518
+ owasp:
2519
+ - A03:2021 - Injection
2520
+ subcategory:
2521
+ - vuln
2522
+ likelihood: MEDIUM
2523
+ impact: MEDIUM
2524
+ confidence: MEDIUM
2525
+ technology:
2526
+ - go
2527
+ patterns:
2528
+ - pattern-either:
2529
+ - pattern: $W.Write([]byte($X))
2530
+ - pattern: $W.Write($X)
2531
+ - pattern-not: $W.Write([]byte("..."))
2532
+ - pattern-either:
2533
+ - pattern-inside: 'func $FUNC(..., $W http.ResponseWriter, ...) { ... }
2534
+
2535
+ '
2536
+ - pattern-inside: 'func ($RECV) $FUNC(..., $W http.ResponseWriter, ...) { ... }
2537
+
2538
+ '
2539
+ - id: narvy.go.xss.no-fprintf-to-responsewriter.no-fprintf-to-responsewriter
2540
+ message: '`fmt.Fprintf(w, ...)` formats a tainted value directly into the HTTP response body. `Fprintf` does not HTML-escape
2541
+ its arguments, so a request-derived value reaching the format arguments is reflected XSS (CWE-79). Use `html/template`
2542
+ for anything written to an HTML response.'
2543
+ severity: WARNING
2544
+ languages:
2545
+ - go
2546
+ metadata:
2547
+ category: security
2548
+ cwe:
2549
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2550
+ owasp:
2551
+ - A03:2021 - Injection
2552
+ subcategory:
2553
+ - vuln
2554
+ likelihood: MEDIUM
2555
+ impact: MEDIUM
2556
+ confidence: MEDIUM
2557
+ technology:
2558
+ - go
2559
+ patterns:
2560
+ - pattern: fmt.Fprintf($W, "...", ...)
2561
+ - pattern-either:
2562
+ - pattern-inside: 'func $FUNC(..., $W http.ResponseWriter, ...) { ... }
2563
+
2564
+ '
2565
+ - pattern-inside: 'func ($RECV) $FUNC(..., $W http.ResponseWriter, ...) { ... }
2566
+
2567
+ '
2568
+ - id: narvy.go.xss.no-io-writestring-to-responsewriter.no-io-writestring-to-responsewriter
2569
+ message: '`io.WriteString(w, ...)` writes a tainted string directly to the HTTP response body with no HTML escaping - reflected
2570
+ XSS if the string contains request-derived data (CWE-79). Use `html/template` instead.'
2571
+ severity: WARNING
2572
+ languages:
2573
+ - go
2574
+ metadata:
2575
+ category: security
2576
+ cwe:
2577
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2578
+ owasp:
2579
+ - A03:2021 - Injection
2580
+ subcategory:
2581
+ - vuln
2582
+ likelihood: MEDIUM
2583
+ impact: MEDIUM
2584
+ confidence: MEDIUM
2585
+ technology:
2586
+ - go
2587
+ patterns:
2588
+ - pattern: io.WriteString($W, $X)
2589
+ - pattern-either:
2590
+ - pattern-inside: 'func $FUNC(..., $W http.ResponseWriter, ...) { ... }
2591
+
2592
+ '
2593
+ - pattern-inside: 'func ($RECV) $FUNC(..., $W http.ResponseWriter, ...) { ... }
2594
+
2595
+ '
2596
+ - id: narvy.go.xss.no-printf-in-responsewriter.no-printf-in-responsewriter
2597
+ message: A formatted/concatenated string is passed to a `ResponseWriter` write call. If any operand is request-derived,
2598
+ this is a reflected-XSS sink (CWE-79) - the response body is not passed through an HTML escaper. Render through `html/template`.
2599
+ severity: WARNING
2600
+ languages:
2601
+ - go
2602
+ metadata:
2603
+ category: security
2604
+ cwe:
2605
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2606
+ owasp:
2607
+ - A03:2021 - Injection
2608
+ subcategory:
2609
+ - vuln
2610
+ likelihood: LOW
2611
+ impact: MEDIUM
2612
+ confidence: MEDIUM
2613
+ technology:
2614
+ - go
2615
+ patterns:
2616
+ - pattern: $W.Write([]byte(fmt.Sprintf("...", ...)))
2617
+ - pattern-either:
2618
+ - pattern-inside: 'func $FUNC(..., $W http.ResponseWriter, ...) { ... }
2619
+
2620
+ '
2621
+ - pattern-inside: 'func ($RECV) $FUNC(..., $W http.ResponseWriter, ...) { ... }
2622
+
2623
+ '
2624
+ - id: narvy.go.xss.wip-xss-using-responsewriter-and-printf.wip-xss-using-responsewriter-and-printf
2625
+ message: '`fmt.Fprint(w, ...)` / `fmt.Fprintln(w, ...)` writes concatenated, request-derived data straight to the response
2626
+ body with no escaping - reflected XSS (CWE-79). Render through `html/template` instead of hand-formatting the body.'
2627
+ severity: WARNING
2628
+ languages:
2629
+ - go
2630
+ metadata:
2631
+ category: security
2632
+ cwe:
2633
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2634
+ owasp:
2635
+ - A03:2021 - Injection
2636
+ subcategory:
2637
+ - vuln
2638
+ likelihood: LOW
2639
+ impact: MEDIUM
2640
+ confidence: MEDIUM
2641
+ technology:
2642
+ - go
2643
+ patterns:
2644
+ - pattern-either:
2645
+ - pattern: fmt.Fprint($W, ...)
2646
+ - pattern: fmt.Fprintln($W, ...)
2647
+ - pattern-not: fmt.Fprint($W, "...")
2648
+ - pattern-not: fmt.Fprintln($W, "...")
2649
+ - pattern-either:
2650
+ - pattern-inside: 'func $FUNC(..., $W http.ResponseWriter, ...) { ... }
2651
+
2652
+ '
2653
+ - pattern-inside: 'func ($RECV) $FUNC(..., $W http.ResponseWriter, ...) { ... }
2654
+
2655
+ '
2656
+ - id: narvy.go.xss.text-template-attr-context-unescaped.text-template-attr-context-unescaped
2657
+ message: A `text/template` (not `html/template`) template renders a value inside an HTML attribute. `text/template` performs
2658
+ no contextual escaping, so a request-derived value here can break out of the attribute and inject markup/script - XSS
2659
+ (CWE-79). Switch the template engine to `html/template`.
2660
+ severity: WARNING
2661
+ languages:
2662
+ - go
2663
+ metadata:
2664
+ category: security
2665
+ cwe:
2666
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2667
+ owasp:
2668
+ - A03:2021 - Injection
2669
+ subcategory:
2670
+ - vuln
2671
+ likelihood: LOW
2672
+ impact: MEDIUM
2673
+ confidence: LOW
2674
+ technology:
2675
+ - go
2676
+ patterns:
2677
+ - pattern: text/template.Must(text/template.New($NAME).Parse($SRC))
2678
+ - pattern-not: text/template.Must(text/template.New($NAME).Parse("..."))
2679
+ - id: narvy.go.xss.href-src-concat-unescaped.href-src-concat-unescaped
2680
+ message: A request-derived value is concatenated directly into an `href`/`src` attribute string that is then written to
2681
+ the response without going through `html/template`'s URL-context escaping. A `javascript:` URL or quote-breakout here
2682
+ is XSS (CWE-79). Use `html/template`, which escapes URL attribute context correctly.
2683
+ severity: WARNING
2684
+ languages:
2685
+ - go
2686
+ metadata:
2687
+ category: security
2688
+ cwe:
2689
+ - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
2690
+ owasp:
2691
+ - A03:2021 - Injection
2692
+ subcategory:
2693
+ - vuln
2694
+ likelihood: LOW
2695
+ impact: MEDIUM
2696
+ confidence: LOW
2697
+ technology:
2698
+ - go
2699
+ patterns:
2700
+ - pattern-either:
2701
+ - pattern: fmt.Sprintf("...href=\"%s\"...", $X)
2702
+ - pattern: fmt.Sprintf("...src=\"%s\"...", $X)
2703
+ - pattern: '"<a href=\"" + $X + "\">"'
2704
+ - id: narvy.go.deserialization.tar-entry-unbounded-copy.tar-entry-unbounded-copy
2705
+ message: A `tar.Reader` entry is copied out with `io.Copy()` and no size limit anywhere in the copy path. `header.Size`
2706
+ from the tar header is attacker-controlled and is not enforced by `tar.Reader.Read()` in any way - a crafted tar stream
2707
+ can present a small on-disk archive that unpacks to gigabytes on this entry alone, exhausting disk/memory (decompression
2708
+ bomb, CWE-409). Cap the copy with `io.CopyN($DST, tarReader, maxEntrySize)` or wrap the destination/source in `io.LimitReader`
2709
+ sized off a fixed maximum before copying.
2710
+ severity: WARNING
2711
+ languages:
2712
+ - go
2713
+ metadata:
2714
+ category: security
2715
+ cwe:
2716
+ - 'CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)'
2717
+ owasp:
2718
+ - A05:2021 - Security Misconfiguration
2719
+ subcategory:
2720
+ - vuln
2721
+ likelihood: LOW
2722
+ impact: MEDIUM
2723
+ confidence: LOW
2724
+ technology:
2725
+ - go
2726
+ patterns:
2727
+ - pattern: '$TR := tar.NewReader(...)
2728
+
2729
+ ...
2730
+
2731
+ io.Copy($DST, $TR)
2732
+
2733
+ '
2734
+ - pattern-not: '$TR := tar.NewReader(...)
2735
+
2736
+ ...
2737
+
2738
+ io.Copy($DST, io.LimitReader($TR, ...))
2739
+
2740
+ '
2741
+ - pattern-not: '$TR := tar.NewReader(...)
2742
+
2743
+ ...
2744
+
2745
+ io.CopyN($DST, $TR, ...)
2746
+
2747
+ '
2748
+ - id: narvy.go.deserialization.zip-entry-unbounded-copy.zip-entry-unbounded-copy
2749
+ message: A `*zip.File` entry is opened and its content copied out with `io.Copy()` with no size limit enforced during the
2750
+ copy itself. The entry's declared `UncompressedSize64` in the zip central directory is just a header field an attacker
2751
+ fully controls - it is not checked by `zip.File.Open()`/`Read()`, so even code that pre-checks the declared size against
2752
+ free disk space is still exposed to a stream that decompresses to far more bytes than it claims (decompression bomb, CWE-409).
2753
+ Cap the copy with `io.CopyN()` or wrap the entry reader in `io.LimitReader(entryReader, maxEntrySize)` before copying,
2754
+ and treat a truncated/limited read as an error, not a success.
2755
+ severity: WARNING
2756
+ languages:
2757
+ - go
2758
+ metadata:
2759
+ category: security
2760
+ cwe:
2761
+ - 'CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)'
2762
+ owasp:
2763
+ - A05:2021 - Security Misconfiguration
2764
+ subcategory:
2765
+ - vuln
2766
+ likelihood: LOW
2767
+ impact: MEDIUM
2768
+ confidence: LOW
2769
+ technology:
2770
+ - go
2771
+ patterns:
2772
+ - pattern-either:
2773
+ - patterns:
2774
+ - pattern-inside: "for $_, $ZF := range $ZR.File {\n ...\n}\n"
2775
+ - pattern: '$RC, $ERR := $ZF.Open()
2776
+
2777
+ ...
2778
+
2779
+ io.Copy($DST, $RC)
2780
+
2781
+ '
2782
+ - pattern-not: '$RC, $ERR := $ZF.Open()
2783
+
2784
+ ...
2785
+
2786
+ io.Copy($DST, io.LimitReader($RC, ...))
2787
+
2788
+ '
2789
+ - pattern-not: '$RC, $ERR := $ZF.Open()
2790
+
2791
+ ...
2792
+
2793
+ io.CopyN($DST, $RC, ...)
2794
+
2795
+ '
2796
+ - patterns:
2797
+ - pattern: "func($P *zip.File) $RET {\n ...\n $RC, $ERR := $P.Open()\n ...\n io.Copy($DST, $RC)\n ...\n}\n"
2798
+ - pattern-not: "func($P *zip.File) $RET {\n ...\n $RC, $ERR := $P.Open()\n ...\n io.Copy($DST, io.LimitReader($RC,\
2799
+ \ ...))\n ...\n}\n"
2800
+ - pattern-not: "func($P *zip.File) $RET {\n ...\n $RC, $ERR := $P.Open()\n ...\n io.CopyN($DST, $RC, ...)\n ...\n\
2801
+ }\n"
2802
+ - id: narvy.go.deserialization.decompression-reader-unbounded-read.decompression-reader-unbounded-read
2803
+ message: '`gzip.NewReader()` / `zlib.NewReader()` output is copied with `io.Copy()` and no size limit. A malicious small
2804
+ compressed payload can expand to gigabytes in memory/disk, exhausting resources - a decompression-bomb DoS (CWE-409).
2805
+ Wrap the reader with `io.LimitReader(r, maxDecompressedSize)` before copying.'
2806
+ severity: WARNING
2807
+ languages:
2808
+ - go
2809
+ metadata:
2810
+ category: security
2811
+ cwe:
2812
+ - 'CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)'
2813
+ owasp:
2814
+ - A05:2021 - Security Misconfiguration
2815
+ subcategory:
2816
+ - vuln
2817
+ likelihood: LOW
2818
+ impact: MEDIUM
2819
+ confidence: LOW
2820
+ technology:
2821
+ - go
2822
+ patterns:
2823
+ - pattern-either:
2824
+ - pattern: '$GZ, $ERR := gzip.NewReader($R)
2825
+
2826
+ ...
2827
+
2828
+ io.Copy($DST, $GZ)
2829
+
2830
+ '
2831
+ - pattern: '$ZR, $ERR := zlib.NewReader($R)
2832
+
2833
+ ...
2834
+
2835
+ io.Copy($DST, $ZR)
2836
+
2837
+ '