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.
- narvy/__init__.py +3 -0
- narvy/android/__init__.py +0 -0
- narvy/android/source_analyzer.py +268 -0
- narvy/android/split_bundle.py +475 -0
- narvy/android_rule_context.py +196 -0
- narvy/apk_memory_preflight.py +248 -0
- narvy/auth.py +40 -0
- narvy/ci_templates/bitbucket.yml +25 -0
- narvy/ci_templates/github.yml +60 -0
- narvy/ci_templates/gitlab.yml +32 -0
- narvy/cloud/__init__.py +0 -0
- narvy/cloud/aws_scan.py +1188 -0
- narvy/comment_filter.py +134 -0
- narvy/crypto_taint_lite.py +82 -0
- narvy/decompiler.py +337 -0
- narvy/doctor.py +244 -0
- narvy/host/__init__.py +0 -0
- narvy/host/audit.py +157 -0
- narvy/host/host_knowledge.py +982 -0
- narvy/host/lynis_bootstrap.py +400 -0
- narvy/host/lynis_parser.py +358 -0
- narvy/host/narvy_checks.py +789 -0
- narvy/host/report.py +69 -0
- narvy/host/ssh_exec.py +219 -0
- narvy/ios/__init__.py +0 -0
- narvy/ios/binary_analyzer.py +1201 -0
- narvy/ios/plist_checks.py +249 -0
- narvy/ios/source_analyzer.py +301 -0
- narvy/ios/third_party_filter.py +242 -0
- narvy/ios/trust_all_context.py +66 -0
- narvy/main.py +1983 -0
- narvy/native_hardening.py +503 -0
- narvy/reporter.py +151 -0
- narvy/rule_engine.py +57 -0
- narvy/rules/android/config.yml +77 -0
- narvy/rules/android/crypto.yml +34 -0
- narvy/rules/android/secrets.yml +161 -0
- narvy/rules/android/storage.yml +24 -0
- narvy/rules/android/webview.yml +23 -0
- narvy/rules/android.yml +219 -0
- narvy/rules/ios/objc/crypto.yml +56 -0
- narvy/rules/ios/objc/network.yml +67 -0
- narvy/rules/ios/objc/secrets.yml +79 -0
- narvy/rules/ios/objc/storage.yml +45 -0
- narvy/rules/ios/objc/webview.yml +45 -0
- narvy/rules/ios_swift.yml +327 -0
- narvy/rules/web/go.yml +2837 -0
- narvy/rules/web/java.yml +1576 -0
- narvy/rules/web/javascript.yml +3683 -0
- narvy/rules/web/kotlin.yml +413 -0
- narvy/rules/web/local/csharp_narvy/config.yml +61 -0
- narvy/rules/web/local/csharp_narvy/crypto.yml +64 -0
- narvy/rules/web/local/csharp_narvy/deserialization.yml +59 -0
- narvy/rules/web/local/csharp_narvy/injection.yml +122 -0
- narvy/rules/web/local/csharp_narvy/xxe.yml +48 -0
- narvy/rules/web/local/java_narvy/auth_jwt.yml +134 -0
- narvy/rules/web/local/java_narvy/deserialization.yml +108 -0
- narvy/rules/web/local/java_narvy/mybatis.yml +39 -0
- narvy/rules/web/local/java_narvy/snakeyaml.yml +34 -0
- narvy/rules/web/local/java_narvy/spring_authz.yml +32 -0
- narvy/rules/web/local/java_narvy/spring_config.yml +67 -0
- narvy/rules/web/local/java_narvy/spring_hardening.yml +291 -0
- narvy/rules/web/local/java_narvy/sqli.yml +235 -0
- narvy/rules/web/local/java_narvy/xxe.yml +212 -0
- narvy/rules/web/php.yml +1644 -0
- narvy/rules/web/python.yml +3967 -0
- narvy/rules/web/ruby.yml +703 -0
- narvy/rules/web/rust.yml +258 -0
- narvy/rules/web/secrets.yml +1420 -0
- narvy/rules/web/secrets_supplement.yml +383 -0
- narvy/sca/__init__.py +1 -0
- narvy/sca/android_deps.py +349 -0
- narvy/sca/ios_deps.py +578 -0
- narvy/sca/osv_client.py +617 -0
- narvy/sca/web_deps.py +955 -0
- narvy/scope_config.py +195 -0
- narvy/semgrep_engine.py +219 -0
- narvy/stack_protector_evidence.py +96 -0
- narvy/third_party_filter.py +211 -0
- narvy/uploader.py +92 -0
- narvy/weak_prng_context.py +270 -0
- narvy/web/__init__.py +0 -0
- narvy/web/nuclei_binary.py +105 -0
- narvy/web/scan_blocklist.py +96 -0
- narvy/web/scanner.py +842 -0
- narvy/web/source_analyzer.py +714 -0
- narvy/web/ssrf_guard.py +374 -0
- narvy_cli-1.0.0.dist-info/METADATA +165 -0
- narvy_cli-1.0.0.dist-info/RECORD +93 -0
- narvy_cli-1.0.0.dist-info/WHEEL +5 -0
- narvy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- narvy_cli-1.0.0.dist-info/licenses/LICENSE +202 -0
- narvy_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,3967 @@
|
|
|
1
|
+
# Python security rules, authored by Narvy.
|
|
2
|
+
# Injection, deserialization, SSRF, XSS, path traversal, weak crypto, secrets.
|
|
3
|
+
|
|
4
|
+
rules:
|
|
5
|
+
- id: narvy.python.injection.os-popen-concat.os-popen-concat
|
|
6
|
+
message: 'Detected `os.popen()` called with a dynamically constructed command string. This is equivalent
|
|
7
|
+
to os.system() for command injection purposes (CWE-78). Use subprocess with a list and `shell=False`.
|
|
8
|
+
|
|
9
|
+
'
|
|
10
|
+
severity: ERROR
|
|
11
|
+
languages:
|
|
12
|
+
- python
|
|
13
|
+
metadata:
|
|
14
|
+
category: security
|
|
15
|
+
cwe:
|
|
16
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
17
|
+
owasp:
|
|
18
|
+
- A03:2021 - Injection
|
|
19
|
+
subcategory:
|
|
20
|
+
- vuln
|
|
21
|
+
likelihood: HIGH
|
|
22
|
+
impact: HIGH
|
|
23
|
+
confidence: MEDIUM
|
|
24
|
+
patterns:
|
|
25
|
+
- pattern-either:
|
|
26
|
+
- pattern: os.popen("..." + $VAR)
|
|
27
|
+
- pattern: os.popen($VAR + "...")
|
|
28
|
+
- pattern: os.popen($VAR)
|
|
29
|
+
- pattern: os.popen(f"...{$VAR}...")
|
|
30
|
+
- pattern-not: os.popen("...")
|
|
31
|
+
- id: narvy.python.injection.os-system-concat.os-system-concat
|
|
32
|
+
message: 'Detected `os.system()` called with a string that includes a non-literal variable via concatenation
|
|
33
|
+
or f-string. If the variable originates from user input this enables OS command injection (CWE-78).
|
|
34
|
+
Use the `subprocess` module with a list of arguments and `shell=False` instead.
|
|
35
|
+
|
|
36
|
+
'
|
|
37
|
+
severity: ERROR
|
|
38
|
+
languages:
|
|
39
|
+
- python
|
|
40
|
+
metadata:
|
|
41
|
+
category: security
|
|
42
|
+
cwe:
|
|
43
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
44
|
+
owasp:
|
|
45
|
+
- A03:2021 - Injection
|
|
46
|
+
subcategory:
|
|
47
|
+
- vuln
|
|
48
|
+
likelihood: HIGH
|
|
49
|
+
impact: HIGH
|
|
50
|
+
confidence: MEDIUM
|
|
51
|
+
patterns:
|
|
52
|
+
- pattern-either:
|
|
53
|
+
- pattern: os.system("..." + $VAR)
|
|
54
|
+
- pattern: os.system($VAR + "...")
|
|
55
|
+
- pattern: os.system($VAR)
|
|
56
|
+
- pattern: os.system(f"...{$VAR}...")
|
|
57
|
+
- pattern: os.system("...".format(..., $VAR, ...))
|
|
58
|
+
- pattern: os.system("..." % $VAR)
|
|
59
|
+
- pattern-not: os.system("...")
|
|
60
|
+
- id: narvy.python.injection.subprocess-shell-true-with-var.subprocess-shell-true-with-var
|
|
61
|
+
message: 'Detected subprocess call with `shell=True` and a dynamically constructed command. Using `shell=True`
|
|
62
|
+
with user input enables OS command injection (CWE-78). Pass a list of arguments with `shell=False`
|
|
63
|
+
instead.
|
|
64
|
+
|
|
65
|
+
'
|
|
66
|
+
severity: ERROR
|
|
67
|
+
languages:
|
|
68
|
+
- python
|
|
69
|
+
metadata:
|
|
70
|
+
category: security
|
|
71
|
+
cwe:
|
|
72
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
73
|
+
owasp:
|
|
74
|
+
- A03:2021 - Injection
|
|
75
|
+
subcategory:
|
|
76
|
+
- vuln
|
|
77
|
+
likelihood: HIGH
|
|
78
|
+
impact: HIGH
|
|
79
|
+
confidence: MEDIUM
|
|
80
|
+
patterns:
|
|
81
|
+
- pattern-either:
|
|
82
|
+
- pattern: subprocess.run("..." + $VAR, ..., shell=True, ...)
|
|
83
|
+
- pattern: subprocess.run($VAR, ..., shell=True, ...)
|
|
84
|
+
- pattern: subprocess.run(f"...{$VAR}...", ..., shell=True, ...)
|
|
85
|
+
- pattern: subprocess.Popen("..." + $VAR, ..., shell=True, ...)
|
|
86
|
+
- pattern: subprocess.Popen($VAR, ..., shell=True, ...)
|
|
87
|
+
- pattern: subprocess.Popen(f"...{$VAR}...", ..., shell=True, ...)
|
|
88
|
+
- pattern: subprocess.call("..." + $VAR, ..., shell=True, ...)
|
|
89
|
+
- pattern: subprocess.call($VAR, ..., shell=True, ...)
|
|
90
|
+
- pattern: subprocess.check_output("..." + $VAR, ..., shell=True, ...)
|
|
91
|
+
- pattern: subprocess.check_output($VAR, ..., shell=True, ...)
|
|
92
|
+
- pattern-not: subprocess.run("...", ..., shell=True, ...)
|
|
93
|
+
- pattern-not: subprocess.Popen("...", ..., shell=True, ...)
|
|
94
|
+
- id: narvy.python.deserialization.yaml-load-unsafe.yaml-load-unsafe
|
|
95
|
+
message: 'Detected `yaml.load()` without an explicit Loader. Without a Loader, PyYAML defaults to FullLoader
|
|
96
|
+
(PyYAML >= 5.1) or an even more dangerous loader in older versions, both of which can deserialise
|
|
97
|
+
arbitrary Python objects when the input is untrusted (CWE-502). Use `yaml.safe_load()` for untrusted
|
|
98
|
+
input.
|
|
99
|
+
|
|
100
|
+
'
|
|
101
|
+
severity: ERROR
|
|
102
|
+
languages:
|
|
103
|
+
- python
|
|
104
|
+
metadata:
|
|
105
|
+
category: security
|
|
106
|
+
cwe:
|
|
107
|
+
- 'CWE-502: Deserialization of Untrusted Data'
|
|
108
|
+
owasp:
|
|
109
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
110
|
+
subcategory:
|
|
111
|
+
- vuln
|
|
112
|
+
likelihood: HIGH
|
|
113
|
+
impact: HIGH
|
|
114
|
+
confidence: HIGH
|
|
115
|
+
patterns:
|
|
116
|
+
- pattern: yaml.load($DATA)
|
|
117
|
+
- pattern-not: yaml.load($DATA, Loader=yaml.SafeLoader)
|
|
118
|
+
- pattern-not: yaml.load($DATA, Loader=yaml.BaseLoader)
|
|
119
|
+
- pattern-not: yaml.safe_load(...)
|
|
120
|
+
- id: narvy.python.auth.django-set-password-without-validation.django-set-password-without-validation
|
|
121
|
+
message: '`set_password()` is called without a call to `django.contrib.auth.password_validation.validate_password()`
|
|
122
|
+
anywhere else in the same function, or in any other method of the same class. Django''s password strength
|
|
123
|
+
rules (`AUTH_PASSWORD_VALIDATORS` - minimum length, common-password check, similarity-to-user-attributes
|
|
124
|
+
check) are opt-in per call site, not enforced automatically by `set_password()` itself (CWE-521).
|
|
125
|
+
Call `validate_password(raw_password, user=$INSTANCE)` before `set_password()` and handle `ValidationError`.'
|
|
126
|
+
severity: WARNING
|
|
127
|
+
languages:
|
|
128
|
+
- python
|
|
129
|
+
metadata:
|
|
130
|
+
cwe:
|
|
131
|
+
- 'CWE-521: Weak Password Requirements'
|
|
132
|
+
owasp:
|
|
133
|
+
- A07:2021 - Identification and Authentication Failures
|
|
134
|
+
category: security
|
|
135
|
+
subcategory:
|
|
136
|
+
- audit
|
|
137
|
+
technology:
|
|
138
|
+
- python
|
|
139
|
+
- django
|
|
140
|
+
likelihood: LOW
|
|
141
|
+
impact: MEDIUM
|
|
142
|
+
confidence: LOW
|
|
143
|
+
vulnerability_class:
|
|
144
|
+
- Improper Authentication
|
|
145
|
+
patterns:
|
|
146
|
+
- pattern: $INSTANCE.set_password($RAW)
|
|
147
|
+
- pattern-not: $INSTANCE.set_password("...")
|
|
148
|
+
- pattern-not-inside: "def $FUNC(...):\n ...\n password_validation.validate_password(...)\n \
|
|
149
|
+
\ ...\n"
|
|
150
|
+
- pattern-not-inside: "def $FUNC(...):\n ...\n django.contrib.auth.password_validation.validate_password(...)\n\
|
|
151
|
+
\ ...\n"
|
|
152
|
+
- pattern-not-inside: "class $CLS(...):\n ...\n def $ANYMETHOD(...):\n ...\n password_validation.validate_password(...)\n\
|
|
153
|
+
\ ...\n ...\n"
|
|
154
|
+
- pattern-not-inside: "class $CLS(...):\n ...\n def $ANYMETHOD(...):\n ...\n django.contrib.auth.password_validation.validate_password(...)\n\
|
|
155
|
+
\ ...\n ...\n"
|
|
156
|
+
- id: narvy.python.code-injection.compile-exec-tainted.compile-exec-tainted
|
|
157
|
+
message: '`compile()` output is passed to `exec()`/`eval()` where the source text passed to `compile()`
|
|
158
|
+
is not a fixed literal. This is the same RCE risk as calling `eval()`/`exec()` directly on tainted
|
|
159
|
+
text (CWE-94), just split across two calls, which sometimes slips past naive eval/exec-only detectors.'
|
|
160
|
+
severity: ERROR
|
|
161
|
+
languages:
|
|
162
|
+
- python
|
|
163
|
+
metadata:
|
|
164
|
+
cwe:
|
|
165
|
+
- 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
|
|
166
|
+
owasp:
|
|
167
|
+
- A03:2021 - Injection
|
|
168
|
+
category: security
|
|
169
|
+
subcategory:
|
|
170
|
+
- vuln
|
|
171
|
+
technology:
|
|
172
|
+
- python
|
|
173
|
+
likelihood: MEDIUM
|
|
174
|
+
impact: HIGH
|
|
175
|
+
confidence: MEDIUM
|
|
176
|
+
vulnerability_class:
|
|
177
|
+
- Code Injection
|
|
178
|
+
patterns:
|
|
179
|
+
- pattern-either:
|
|
180
|
+
- pattern: '$CODE = compile($SRC, ...)
|
|
181
|
+
|
|
182
|
+
...
|
|
183
|
+
|
|
184
|
+
exec($CODE)
|
|
185
|
+
|
|
186
|
+
'
|
|
187
|
+
- pattern: '$CODE = compile($SRC, ...)
|
|
188
|
+
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
eval($CODE)
|
|
192
|
+
|
|
193
|
+
'
|
|
194
|
+
- pattern-not: '$CODE = compile("...", ...)
|
|
195
|
+
|
|
196
|
+
...
|
|
197
|
+
|
|
198
|
+
exec($CODE)
|
|
199
|
+
|
|
200
|
+
'
|
|
201
|
+
- pattern-not: '$CODE = compile("...", ...)
|
|
202
|
+
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
eval($CODE)
|
|
206
|
+
|
|
207
|
+
'
|
|
208
|
+
- id: narvy.python.code-injection.eval-format-string.eval-format-string
|
|
209
|
+
message: '`eval()` called on a dynamically built string (f-string, `%` formatting, or `.format()`) rather
|
|
210
|
+
than a fixed literal. Even if the immediate source is not obviously a request object, building code
|
|
211
|
+
text by interpolation is a strong code-injection smell (CWE-94) whenever any interpolated part is
|
|
212
|
+
not fully trusted. Prefer `ast.literal_eval` for literals, or refactor to avoid building code as text.'
|
|
213
|
+
severity: WARNING
|
|
214
|
+
languages:
|
|
215
|
+
- python
|
|
216
|
+
metadata:
|
|
217
|
+
cwe:
|
|
218
|
+
- 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
|
|
219
|
+
owasp:
|
|
220
|
+
- A03:2021 - Injection
|
|
221
|
+
category: security
|
|
222
|
+
subcategory:
|
|
223
|
+
- vuln
|
|
224
|
+
technology:
|
|
225
|
+
- python
|
|
226
|
+
likelihood: MEDIUM
|
|
227
|
+
impact: HIGH
|
|
228
|
+
confidence: LOW
|
|
229
|
+
vulnerability_class:
|
|
230
|
+
- Code Injection
|
|
231
|
+
patterns:
|
|
232
|
+
- pattern-either:
|
|
233
|
+
- pattern: eval(f"...{$VAR}...")
|
|
234
|
+
- pattern: eval("...".format($VAR))
|
|
235
|
+
- pattern: eval("..." % $VAR)
|
|
236
|
+
- pattern: exec(f"...{$VAR}...")
|
|
237
|
+
- pattern: exec("...".format($VAR))
|
|
238
|
+
- pattern: exec("..." % $VAR)
|
|
239
|
+
- id: narvy.python.code-injection.eval-on-request-data.eval-on-request-data
|
|
240
|
+
message: '`eval()` is called on data that originates from an HTTP request (Flask `request.*`, Django
|
|
241
|
+
`request.*`, or a function argument that is clearly user-controlled). `eval()` executes arbitrary
|
|
242
|
+
Python - this is full remote code execution (CWE-94). There is no safe way to sandbox `eval()` against
|
|
243
|
+
a malicious string; replace with `ast.literal_eval()` if only literals are expected, or a proper parser/allowlisted
|
|
244
|
+
DSL otherwise.'
|
|
245
|
+
severity: ERROR
|
|
246
|
+
languages:
|
|
247
|
+
- python
|
|
248
|
+
metadata:
|
|
249
|
+
cwe:
|
|
250
|
+
- 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
|
|
251
|
+
owasp:
|
|
252
|
+
- A03:2021 - Injection
|
|
253
|
+
category: security
|
|
254
|
+
subcategory:
|
|
255
|
+
- vuln
|
|
256
|
+
technology:
|
|
257
|
+
- python
|
|
258
|
+
- flask
|
|
259
|
+
- django
|
|
260
|
+
likelihood: HIGH
|
|
261
|
+
impact: HIGH
|
|
262
|
+
confidence: HIGH
|
|
263
|
+
vulnerability_class:
|
|
264
|
+
- Code Injection
|
|
265
|
+
patterns:
|
|
266
|
+
- pattern-either:
|
|
267
|
+
- pattern: eval(request.$KIND.$ATTR(...))
|
|
268
|
+
- pattern: eval(request.$KIND.$ATTR)
|
|
269
|
+
- pattern: eval(request.$KIND[$X])
|
|
270
|
+
- pattern: eval(request.$KIND.get(...))
|
|
271
|
+
- pattern: '$V = request.$KIND.$ATTR(...)
|
|
272
|
+
|
|
273
|
+
...
|
|
274
|
+
|
|
275
|
+
eval($V)
|
|
276
|
+
|
|
277
|
+
'
|
|
278
|
+
- pattern: '$V = request.$KIND[$X]
|
|
279
|
+
|
|
280
|
+
...
|
|
281
|
+
|
|
282
|
+
eval($V)
|
|
283
|
+
|
|
284
|
+
'
|
|
285
|
+
- metavariable-regex:
|
|
286
|
+
metavariable: $KIND
|
|
287
|
+
regex: ^(GET|POST|args|form|json|values|data|cookies|headers|params)$
|
|
288
|
+
- id: narvy.python.code-injection.exec-on-request-data.exec-on-request-data
|
|
289
|
+
message: '`exec()` is called on data that originates from an HTTP request. `exec()` runs arbitrary Python
|
|
290
|
+
statements - full remote code execution (CWE-94). Never execute request-derived strings as code.'
|
|
291
|
+
severity: ERROR
|
|
292
|
+
languages:
|
|
293
|
+
- python
|
|
294
|
+
metadata:
|
|
295
|
+
cwe:
|
|
296
|
+
- 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
|
|
297
|
+
owasp:
|
|
298
|
+
- A03:2021 - Injection
|
|
299
|
+
category: security
|
|
300
|
+
subcategory:
|
|
301
|
+
- vuln
|
|
302
|
+
technology:
|
|
303
|
+
- python
|
|
304
|
+
- flask
|
|
305
|
+
- django
|
|
306
|
+
likelihood: HIGH
|
|
307
|
+
impact: HIGH
|
|
308
|
+
confidence: HIGH
|
|
309
|
+
vulnerability_class:
|
|
310
|
+
- Code Injection
|
|
311
|
+
patterns:
|
|
312
|
+
- pattern-either:
|
|
313
|
+
- pattern: exec(request.$KIND.$ATTR(...))
|
|
314
|
+
- pattern: exec(request.$KIND.$ATTR)
|
|
315
|
+
- pattern: exec(request.$KIND[$X])
|
|
316
|
+
- pattern: exec(request.$KIND.get(...))
|
|
317
|
+
- pattern: '$V = request.$KIND.$ATTR(...)
|
|
318
|
+
|
|
319
|
+
...
|
|
320
|
+
|
|
321
|
+
exec($V)
|
|
322
|
+
|
|
323
|
+
'
|
|
324
|
+
- pattern: '$V = request.$KIND[$X]
|
|
325
|
+
|
|
326
|
+
...
|
|
327
|
+
|
|
328
|
+
exec($V)
|
|
329
|
+
|
|
330
|
+
'
|
|
331
|
+
- metavariable-regex:
|
|
332
|
+
metavariable: $KIND
|
|
333
|
+
regex: ^(GET|POST|args|form|json|values|data|cookies|headers|params)$
|
|
334
|
+
- id: narvy.python.command-injection.asyncio-subprocess-shell.asyncio-subprocess-shell
|
|
335
|
+
message: '`asyncio.create_subprocess_shell()` runs its argument through a real shell (like os.system).
|
|
336
|
+
If `$CMD` is built from a variable instead of a pure literal, this is OS command injection (CWE-78).
|
|
337
|
+
Use `asyncio.create_subprocess_exec()` with a list of arguments instead.'
|
|
338
|
+
severity: ERROR
|
|
339
|
+
languages:
|
|
340
|
+
- python
|
|
341
|
+
metadata:
|
|
342
|
+
cwe:
|
|
343
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
344
|
+
owasp:
|
|
345
|
+
- A03:2021 - Injection
|
|
346
|
+
category: security
|
|
347
|
+
subcategory:
|
|
348
|
+
- vuln
|
|
349
|
+
technology:
|
|
350
|
+
- python
|
|
351
|
+
- asyncio
|
|
352
|
+
likelihood: HIGH
|
|
353
|
+
impact: HIGH
|
|
354
|
+
confidence: MEDIUM
|
|
355
|
+
vulnerability_class:
|
|
356
|
+
- Command Injection
|
|
357
|
+
patterns:
|
|
358
|
+
- pattern-either:
|
|
359
|
+
- pattern: asyncio.create_subprocess_shell($CMD)
|
|
360
|
+
- pattern: asyncio.create_subprocess_shell("..." + $VAR)
|
|
361
|
+
- pattern: asyncio.create_subprocess_shell(f"...{$VAR}...")
|
|
362
|
+
- pattern-not: asyncio.create_subprocess_shell("...")
|
|
363
|
+
- id: narvy.python.command-injection.os-exec-family-tainted.os-exec-family-tainted
|
|
364
|
+
message: '`os.$FN()` replaces the current process image with a command built from a non-literal argument.
|
|
365
|
+
If the argument (or one of its list elements) is influenced by external input, this is command injection
|
|
366
|
+
/ arbitrary process execution (CWE-78, CWE-88). Validate against an allowlist of known binaries before
|
|
367
|
+
calling.'
|
|
368
|
+
severity: ERROR
|
|
369
|
+
languages:
|
|
370
|
+
- python
|
|
371
|
+
metadata:
|
|
372
|
+
cwe:
|
|
373
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
374
|
+
- 'CWE-88: Improper Neutralization of Argument Delimiters in a Command (''Argument Injection'')'
|
|
375
|
+
owasp:
|
|
376
|
+
- A03:2021 - Injection
|
|
377
|
+
category: security
|
|
378
|
+
subcategory:
|
|
379
|
+
- vuln
|
|
380
|
+
technology:
|
|
381
|
+
- python
|
|
382
|
+
likelihood: MEDIUM
|
|
383
|
+
impact: HIGH
|
|
384
|
+
confidence: MEDIUM
|
|
385
|
+
vulnerability_class:
|
|
386
|
+
- Command Injection
|
|
387
|
+
patterns:
|
|
388
|
+
- pattern-either:
|
|
389
|
+
- pattern: os.execl($VAR, ...)
|
|
390
|
+
- pattern: os.execle($VAR, ...)
|
|
391
|
+
- pattern: os.execlp($VAR, ...)
|
|
392
|
+
- pattern: os.execv($VAR, ...)
|
|
393
|
+
- pattern: os.execve($VAR, ...)
|
|
394
|
+
- pattern: os.execvp($VAR, ...)
|
|
395
|
+
- pattern: os.spawnl(..., $VAR, ...)
|
|
396
|
+
- pattern: os.spawnv(..., $VAR, ...)
|
|
397
|
+
- pattern: os.posix_spawn($VAR, ...)
|
|
398
|
+
- pattern-not: os.$FN("...", ...)
|
|
399
|
+
- id: narvy.python.command-injection.paramiko-exec-command-tainted.paramiko-exec-command-tainted
|
|
400
|
+
message: '`paramiko` SSH client `exec_command()` invoked with a non-literal command string. The remote
|
|
401
|
+
shell will interpret this string, so any external input reaching `$CMD` is remote command injection
|
|
402
|
+
on the target host (CWE-78). Build the command from a fixed template and shell-quote any variable
|
|
403
|
+
parts with `shlex.quote()`.'
|
|
404
|
+
severity: ERROR
|
|
405
|
+
languages:
|
|
406
|
+
- python
|
|
407
|
+
metadata:
|
|
408
|
+
cwe:
|
|
409
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
410
|
+
owasp:
|
|
411
|
+
- A03:2021 - Injection
|
|
412
|
+
category: security
|
|
413
|
+
subcategory:
|
|
414
|
+
- vuln
|
|
415
|
+
technology:
|
|
416
|
+
- python
|
|
417
|
+
- paramiko
|
|
418
|
+
likelihood: MEDIUM
|
|
419
|
+
impact: HIGH
|
|
420
|
+
confidence: MEDIUM
|
|
421
|
+
vulnerability_class:
|
|
422
|
+
- Command Injection
|
|
423
|
+
patterns:
|
|
424
|
+
- pattern-either:
|
|
425
|
+
- pattern: $CLIENT.exec_command($CMD)
|
|
426
|
+
- pattern: $CLIENT.exec_command("..." + $VAR)
|
|
427
|
+
- pattern: $CLIENT.exec_command(f"...{$VAR}...")
|
|
428
|
+
- pattern-not: $CLIENT.exec_command("...")
|
|
429
|
+
- metavariable-regex:
|
|
430
|
+
metavariable: $CLIENT
|
|
431
|
+
regex: (?i)(ssh|client)
|
|
432
|
+
- id: narvy.python.command-injection.shell-metacharacter-in-fstring-subprocess.shell-metacharacter-fstring-subprocess
|
|
433
|
+
message: 'subprocess call built via an f-string / % / .format() that embeds a variable directly into
|
|
434
|
+
the command text (rather than passing it as a separate argv element). Even without `shell=True`, if
|
|
435
|
+
this string is later split by shlex or passed to a shell, or if the call site is actually `shell=True`
|
|
436
|
+
elsewhere in this branch, unescaped shell metacharacters in the variable let an attacker chain commands.
|
|
437
|
+
Pass arguments as a list: `subprocess.run(["cmd", user_arg])`. CWE-78.'
|
|
438
|
+
severity: WARNING
|
|
439
|
+
languages:
|
|
440
|
+
- python
|
|
441
|
+
metadata:
|
|
442
|
+
cwe:
|
|
443
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
444
|
+
owasp:
|
|
445
|
+
- A03:2021 - Injection
|
|
446
|
+
category: security
|
|
447
|
+
subcategory:
|
|
448
|
+
- vuln
|
|
449
|
+
technology:
|
|
450
|
+
- python
|
|
451
|
+
- subprocess
|
|
452
|
+
likelihood: MEDIUM
|
|
453
|
+
impact: HIGH
|
|
454
|
+
confidence: LOW
|
|
455
|
+
vulnerability_class:
|
|
456
|
+
- Command Injection
|
|
457
|
+
patterns:
|
|
458
|
+
- pattern-either:
|
|
459
|
+
- pattern: subprocess.run(f"...{$VAR}...", ...)
|
|
460
|
+
- pattern: subprocess.Popen(f"...{$VAR}...", ...)
|
|
461
|
+
- pattern: subprocess.call(f"...{$VAR}...", ...)
|
|
462
|
+
- pattern: subprocess.check_output(f"...{$VAR}...", ...)
|
|
463
|
+
- pattern: subprocess.check_call(f"...{$VAR}...", ...)
|
|
464
|
+
- id: narvy.python.command-injection.subprocess-list-arg-tainted.subprocess-list-arg-tainted
|
|
465
|
+
message: A value read from the HTTP request reaches a subprocess call as part of the argv list, with
|
|
466
|
+
no shell=True in sight and no allow-list check in between. List-form subprocess calls are often assumed
|
|
467
|
+
safe because there is no shell to interpret metacharacters, but that only protects against injecting
|
|
468
|
+
*additional* commands via `;`/`|`/backticks - it does nothing to stop the attacker from choosing which
|
|
469
|
+
flags or arguments reach the target binary, and if the tainted value is split into several argv elements
|
|
470
|
+
(e.g. via `.split(' ')`) the attacker effectively controls an arbitrary number of arguments to that
|
|
471
|
+
binary. CWE-78, CWE-88. Validate the value against a strict allow-list of expected tokens, or don't
|
|
472
|
+
let request data reach argv at all.
|
|
473
|
+
severity: ERROR
|
|
474
|
+
languages:
|
|
475
|
+
- python
|
|
476
|
+
metadata:
|
|
477
|
+
cwe:
|
|
478
|
+
- 'CWE-78: Improper Neutralization of Special Elements used in an OS Command (''OS Command Injection'')'
|
|
479
|
+
- 'CWE-88: Improper Neutralization of Argument Delimiters in a Command (''Argument Injection'')'
|
|
480
|
+
owasp:
|
|
481
|
+
- A03:2021 - Injection
|
|
482
|
+
category: security
|
|
483
|
+
subcategory:
|
|
484
|
+
- vuln
|
|
485
|
+
technology:
|
|
486
|
+
- python
|
|
487
|
+
- subprocess
|
|
488
|
+
- django
|
|
489
|
+
- flask
|
|
490
|
+
likelihood: HIGH
|
|
491
|
+
impact: HIGH
|
|
492
|
+
confidence: MEDIUM
|
|
493
|
+
vulnerability_class:
|
|
494
|
+
- Command Injection
|
|
495
|
+
references:
|
|
496
|
+
- https://cwe.mitre.org/data/definitions/78.html
|
|
497
|
+
- https://cwe.mitre.org/data/definitions/88.html
|
|
498
|
+
mode: taint
|
|
499
|
+
pattern-sources:
|
|
500
|
+
- patterns:
|
|
501
|
+
- pattern-either:
|
|
502
|
+
- pattern: request.POST[...]
|
|
503
|
+
- pattern: request.POST.get(...)
|
|
504
|
+
- pattern: request.GET[...]
|
|
505
|
+
- pattern: request.GET.get(...)
|
|
506
|
+
- pattern: request.args[...]
|
|
507
|
+
- pattern: request.args.get(...)
|
|
508
|
+
- pattern: request.form[...]
|
|
509
|
+
- pattern: request.form.get(...)
|
|
510
|
+
- pattern: request.values[...]
|
|
511
|
+
- pattern: request.values.get(...)
|
|
512
|
+
- pattern: request.json[...]
|
|
513
|
+
- pattern: request.json.get(...)
|
|
514
|
+
- pattern: request.data
|
|
515
|
+
pattern-sinks:
|
|
516
|
+
- patterns:
|
|
517
|
+
- pattern-either:
|
|
518
|
+
- pattern: subprocess.Popen($SINK, ...)
|
|
519
|
+
- pattern: subprocess.run($SINK, ...)
|
|
520
|
+
- pattern: subprocess.call($SINK, ...)
|
|
521
|
+
- pattern: subprocess.check_output($SINK, ...)
|
|
522
|
+
- pattern: subprocess.check_call($SINK, ...)
|
|
523
|
+
pattern-sanitizers:
|
|
524
|
+
- patterns:
|
|
525
|
+
- pattern: shlex.quote(...)
|
|
526
|
+
- patterns:
|
|
527
|
+
- pattern: int(...)
|
|
528
|
+
- id: narvy.python.cors.django-cors-allow-all-origins.django-cors-allow-all-origins
|
|
529
|
+
message: '`django-cors-headers` is configured with `CORS_ALLOW_ALL_ORIGINS = True` (or the deprecated
|
|
530
|
+
`CORS_ORIGIN_ALLOW_ALL = True`). Any website can now make cross-origin requests to this API from a
|
|
531
|
+
victim''s browser; if session cookies are also allowed (`CORS_ALLOW_CREDENTIALS = True`), this is
|
|
532
|
+
a full CSRF-equivalent hole on every endpoint. Set `CORS_ALLOWED_ORIGINS` to an explicit list instead.
|
|
533
|
+
CWE-942.'
|
|
534
|
+
severity: WARNING
|
|
535
|
+
languages:
|
|
536
|
+
- python
|
|
537
|
+
metadata:
|
|
538
|
+
cwe:
|
|
539
|
+
- 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
|
|
540
|
+
owasp:
|
|
541
|
+
- A05:2021 - Security Misconfiguration
|
|
542
|
+
category: security
|
|
543
|
+
subcategory:
|
|
544
|
+
- vuln
|
|
545
|
+
technology:
|
|
546
|
+
- django
|
|
547
|
+
likelihood: MEDIUM
|
|
548
|
+
impact: MEDIUM
|
|
549
|
+
confidence: HIGH
|
|
550
|
+
vulnerability_class:
|
|
551
|
+
- Cookie Security
|
|
552
|
+
patterns:
|
|
553
|
+
- pattern-either:
|
|
554
|
+
- pattern: CORS_ALLOW_ALL_ORIGINS = True
|
|
555
|
+
- pattern: CORS_ORIGIN_ALLOW_ALL = True
|
|
556
|
+
- id: narvy.python.cors.fastapi-wildcard-with-credentials.fastapi-wildcard-with-credentials
|
|
557
|
+
message: FastAPI/Starlette `CORSMiddleware` is added with `allow_origins=["*"]` and `allow_credentials=True`.
|
|
558
|
+
Starlette will raise at request time for exactly this combination when it can, but variants that pass
|
|
559
|
+
a wildcard through `allow_origin_regex` slip past that guard and effectively allow any origin to make
|
|
560
|
+
credentialed requests. Use an explicit origin allowlist whenever `allow_credentials=True`. CWE-942.
|
|
561
|
+
severity: ERROR
|
|
562
|
+
languages:
|
|
563
|
+
- python
|
|
564
|
+
metadata:
|
|
565
|
+
cwe:
|
|
566
|
+
- 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
|
|
567
|
+
owasp:
|
|
568
|
+
- A05:2021 - Security Misconfiguration
|
|
569
|
+
category: security
|
|
570
|
+
subcategory:
|
|
571
|
+
- vuln
|
|
572
|
+
technology:
|
|
573
|
+
- fastapi
|
|
574
|
+
- starlette
|
|
575
|
+
likelihood: MEDIUM
|
|
576
|
+
impact: HIGH
|
|
577
|
+
confidence: HIGH
|
|
578
|
+
vulnerability_class:
|
|
579
|
+
- Cookie Security
|
|
580
|
+
patterns:
|
|
581
|
+
- pattern-either:
|
|
582
|
+
- pattern: '$APP.add_middleware(CORSMiddleware, allow_origins=["*"], ..., allow_credentials=True,
|
|
583
|
+
...)
|
|
584
|
+
|
|
585
|
+
'
|
|
586
|
+
- pattern: '$APP.add_middleware(CORSMiddleware, allow_credentials=True, ..., allow_origins=["*"],
|
|
587
|
+
...)
|
|
588
|
+
|
|
589
|
+
'
|
|
590
|
+
- pattern: '$APP.add_middleware(CORSMiddleware, allow_origin_regex=".*", ..., allow_credentials=True,
|
|
591
|
+
...)
|
|
592
|
+
|
|
593
|
+
'
|
|
594
|
+
- id: narvy.python.cors.flask-cors-wildcard-with-credentials.flask-cors-wildcard-with-credentials
|
|
595
|
+
message: flask-cors is configured with `origins="*"` (or unset, which defaults to "*") together with
|
|
596
|
+
`supports_credentials=True`. Per the Fetch/CORS spec browsers should reject this combination, but
|
|
597
|
+
some older or misconfigured setups still leak it, and the wildcard alone already allows any site to
|
|
598
|
+
read non-credentialed responses. Pin `origins=` to an explicit allowlist when `supports_credentials=True`.
|
|
599
|
+
CWE-942.
|
|
600
|
+
severity: ERROR
|
|
601
|
+
languages:
|
|
602
|
+
- python
|
|
603
|
+
metadata:
|
|
604
|
+
cwe:
|
|
605
|
+
- 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
|
|
606
|
+
owasp:
|
|
607
|
+
- A05:2021 - Security Misconfiguration
|
|
608
|
+
category: security
|
|
609
|
+
subcategory:
|
|
610
|
+
- vuln
|
|
611
|
+
technology:
|
|
612
|
+
- flask
|
|
613
|
+
- flask-cors
|
|
614
|
+
likelihood: MEDIUM
|
|
615
|
+
impact: HIGH
|
|
616
|
+
confidence: HIGH
|
|
617
|
+
vulnerability_class:
|
|
618
|
+
- Cookie Security
|
|
619
|
+
patterns:
|
|
620
|
+
- pattern-either:
|
|
621
|
+
- pattern: CORS($APP, origins="*", ..., supports_credentials=True, ...)
|
|
622
|
+
- pattern: CORS($APP, supports_credentials=True, ..., origins="*", ...)
|
|
623
|
+
- pattern: CORS($APP, supports_credentials=True)
|
|
624
|
+
- id: narvy.python.cors.reflected-origin-header.reflected-origin-header
|
|
625
|
+
message: The response's `Access-Control-Allow-Origin` header is set to the value of the incoming `Origin`
|
|
626
|
+
request header verbatim, instead of to a fixed value or a value checked against an allowlist. This
|
|
627
|
+
effectively grants every origin CORS access - identical in effect to a wildcard, but easy to miss
|
|
628
|
+
in review because the header value looks "dynamic" rather than "*". CWE-942.
|
|
629
|
+
severity: ERROR
|
|
630
|
+
languages:
|
|
631
|
+
- python
|
|
632
|
+
metadata:
|
|
633
|
+
cwe:
|
|
634
|
+
- 'CWE-942: Permissive Cross-domain Policy with Untrusted Domains'
|
|
635
|
+
owasp:
|
|
636
|
+
- A05:2021 - Security Misconfiguration
|
|
637
|
+
category: security
|
|
638
|
+
subcategory:
|
|
639
|
+
- vuln
|
|
640
|
+
technology:
|
|
641
|
+
- python
|
|
642
|
+
- flask
|
|
643
|
+
- django
|
|
644
|
+
likelihood: MEDIUM
|
|
645
|
+
impact: HIGH
|
|
646
|
+
confidence: MEDIUM
|
|
647
|
+
vulnerability_class:
|
|
648
|
+
- Cookie Security
|
|
649
|
+
patterns:
|
|
650
|
+
- pattern-either:
|
|
651
|
+
- pattern: $RESP.headers["Access-Control-Allow-Origin"] = request.headers.get("Origin")
|
|
652
|
+
- pattern: $RESP.headers["Access-Control-Allow-Origin"] = request.headers["Origin"]
|
|
653
|
+
- pattern: $RESP["Access-Control-Allow-Origin"] = request.headers.get("Origin")
|
|
654
|
+
- pattern: $RESP["Access-Control-Allow-Origin"] = request.META.get("HTTP_ORIGIN")
|
|
655
|
+
- id: narvy.python.crypto.broken-cipher-algorithm.broken-cipher-algorithm
|
|
656
|
+
message: Broken or deprecated symmetric cipher '$ALGO'. DES (56-bit key) and RC2 are trivially brute-forceable
|
|
657
|
+
on modern hardware; RC4/ARC4 has known keystream biases; Blowfish's 64-bit block size is vulnerable
|
|
658
|
+
to birthday-bound attacks (SWEET32) on large ciphertexts. Use AES-256-GCM or ChaCha20-Poly1305. CWE-327.
|
|
659
|
+
severity: ERROR
|
|
660
|
+
languages:
|
|
661
|
+
- python
|
|
662
|
+
metadata:
|
|
663
|
+
cwe:
|
|
664
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
665
|
+
owasp:
|
|
666
|
+
- A02:2021 - Cryptographic Failures
|
|
667
|
+
category: security
|
|
668
|
+
subcategory:
|
|
669
|
+
- vuln
|
|
670
|
+
technology:
|
|
671
|
+
- pycryptodome
|
|
672
|
+
- cryptography
|
|
673
|
+
likelihood: MEDIUM
|
|
674
|
+
impact: HIGH
|
|
675
|
+
confidence: HIGH
|
|
676
|
+
vulnerability_class:
|
|
677
|
+
- Cryptographic Issues
|
|
678
|
+
patterns:
|
|
679
|
+
- pattern-either:
|
|
680
|
+
- pattern: $ALGO.new(...)
|
|
681
|
+
- pattern: algorithms.$ALGO(...)
|
|
682
|
+
- metavariable-regex:
|
|
683
|
+
metavariable: $ALGO
|
|
684
|
+
regex: ^(DES|DES3|ARC2|ARC4|RC2|RC4|Blowfish|IDEA|CAST)$
|
|
685
|
+
- id: narvy.python.crypto.django-secret-key-env-fallback.django-secret-key-env-fallback
|
|
686
|
+
message: Django `SECRET_KEY` is set with a hardcoded fallback '$LITERAL' when the environment variable
|
|
687
|
+
is unset. SECRET_KEY is used for session signing, CSRF token derivation, password reset tokens, and
|
|
688
|
+
cryptographic signing of cookies. If the env var is missing in any deployment (e.g. local dev sneaking
|
|
689
|
+
into prod, CI), the SECRET_KEY reduces to a public constant and ALL session-signed data is forgeable.
|
|
690
|
+
ALWAYS fail fast in settings.py (`SECRET_KEY = os.environ['DJANGO_SECRET_KEY']` - KeyError on missing
|
|
691
|
+
- or raise ImproperlyConfigured).
|
|
692
|
+
severity: ERROR
|
|
693
|
+
languages:
|
|
694
|
+
- python
|
|
695
|
+
metadata:
|
|
696
|
+
cwe:
|
|
697
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
698
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
699
|
+
owasp:
|
|
700
|
+
- A02:2021 - Cryptographic Failures
|
|
701
|
+
- A07:2021 - Identification and Authentication Failures
|
|
702
|
+
category: security
|
|
703
|
+
subcategory:
|
|
704
|
+
- vuln
|
|
705
|
+
technology:
|
|
706
|
+
- django
|
|
707
|
+
- python
|
|
708
|
+
likelihood: HIGH
|
|
709
|
+
impact: HIGH
|
|
710
|
+
confidence: HIGH
|
|
711
|
+
vulnerability_class:
|
|
712
|
+
- Cryptographic Issues
|
|
713
|
+
- Hard-coded Credentials
|
|
714
|
+
patterns:
|
|
715
|
+
- pattern-either:
|
|
716
|
+
- pattern: 'SECRET_KEY = os.getenv($ENV, "$LITERAL")
|
|
717
|
+
|
|
718
|
+
'
|
|
719
|
+
- pattern: 'SECRET_KEY = os.environ.get($ENV, "$LITERAL")
|
|
720
|
+
|
|
721
|
+
'
|
|
722
|
+
- pattern: 'SECRET_KEY = os.environ.get($ENV) or "$LITERAL"
|
|
723
|
+
|
|
724
|
+
'
|
|
725
|
+
- pattern: 'SECRET_KEY = os.getenv($ENV) or "$LITERAL"
|
|
726
|
+
|
|
727
|
+
'
|
|
728
|
+
- metavariable-regex:
|
|
729
|
+
metavariable: $LITERAL
|
|
730
|
+
regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
|
|
731
|
+
- id: narvy.python.crypto.ecb-cipher-mode.ecb-cipher-mode
|
|
732
|
+
message: ECB cipher mode encrypts identical plaintext blocks to identical ciphertext blocks, leaking
|
|
733
|
+
structural patterns in the data (the classic "ECB penguin"). Use GCM (authenticated) or, at minimum,
|
|
734
|
+
CBC with a random IV and a separate MAC. CWE-327.
|
|
735
|
+
severity: ERROR
|
|
736
|
+
languages:
|
|
737
|
+
- python
|
|
738
|
+
metadata:
|
|
739
|
+
cwe:
|
|
740
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
741
|
+
owasp:
|
|
742
|
+
- A02:2021 - Cryptographic Failures
|
|
743
|
+
category: security
|
|
744
|
+
subcategory:
|
|
745
|
+
- vuln
|
|
746
|
+
technology:
|
|
747
|
+
- pycryptodome
|
|
748
|
+
- cryptography
|
|
749
|
+
likelihood: MEDIUM
|
|
750
|
+
impact: HIGH
|
|
751
|
+
confidence: HIGH
|
|
752
|
+
vulnerability_class:
|
|
753
|
+
- Cryptographic Issues
|
|
754
|
+
patterns:
|
|
755
|
+
- pattern-either:
|
|
756
|
+
- pattern: $ALGO.new($KEY, $ALGO.MODE_ECB)
|
|
757
|
+
- pattern: Cipher($ALGO, modes.ECB(), ...)
|
|
758
|
+
- id: narvy.python.crypto.empty-or-literal-aes-key.empty-or-literal-aes-key
|
|
759
|
+
message: AES key is an empty byte string or a short string literal, not a securely generated key. This
|
|
760
|
+
is either a debug leftover or a hardcoded key; both defeat the cipher. Generate keys with os.urandom(32)
|
|
761
|
+
or secrets.token_bytes(32) and load from a secret store. CWE-321, CWE-1204.
|
|
762
|
+
severity: ERROR
|
|
763
|
+
languages:
|
|
764
|
+
- python
|
|
765
|
+
metadata:
|
|
766
|
+
cwe:
|
|
767
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
768
|
+
- 'CWE-1204: Generation of Weak Initialization Vector'
|
|
769
|
+
owasp:
|
|
770
|
+
- A02:2021 - Cryptographic Failures
|
|
771
|
+
category: security
|
|
772
|
+
subcategory:
|
|
773
|
+
- vuln
|
|
774
|
+
technology:
|
|
775
|
+
- pycryptodome
|
|
776
|
+
- cryptography
|
|
777
|
+
likelihood: MEDIUM
|
|
778
|
+
impact: HIGH
|
|
779
|
+
confidence: HIGH
|
|
780
|
+
vulnerability_class:
|
|
781
|
+
- Cryptographic Issues
|
|
782
|
+
- Hard-coded Credentials
|
|
783
|
+
patterns:
|
|
784
|
+
- pattern-either:
|
|
785
|
+
- pattern: AES.new(b"", ...)
|
|
786
|
+
- pattern: AES.new("", ...)
|
|
787
|
+
- pattern: AES.new(b"$LIT", ...)
|
|
788
|
+
- pattern: AES.new("$LIT", ...)
|
|
789
|
+
- id: narvy.python.crypto.flask-secret-key-env-fallback.flask-secret-key-env-fallback
|
|
790
|
+
message: Flask `SECRET_KEY` is set with a hardcoded fallback '$LITERAL' when the environment variable
|
|
791
|
+
is unset. Used for session-cookie signing and CSRF tokens. If the env var is missing the SECRET_KEY
|
|
792
|
+
reduces to a public constant and ALL session cookies can be forged. Fail fast at startup with `app.config['SECRET_KEY']
|
|
793
|
+
= os.environ['FLASK_SECRET']`.
|
|
794
|
+
severity: ERROR
|
|
795
|
+
languages:
|
|
796
|
+
- python
|
|
797
|
+
metadata:
|
|
798
|
+
cwe:
|
|
799
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
800
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
801
|
+
owasp:
|
|
802
|
+
- A02:2021 - Cryptographic Failures
|
|
803
|
+
- A07:2021 - Identification and Authentication Failures
|
|
804
|
+
category: security
|
|
805
|
+
subcategory:
|
|
806
|
+
- vuln
|
|
807
|
+
technology:
|
|
808
|
+
- flask
|
|
809
|
+
- python
|
|
810
|
+
likelihood: HIGH
|
|
811
|
+
impact: HIGH
|
|
812
|
+
confidence: HIGH
|
|
813
|
+
vulnerability_class:
|
|
814
|
+
- Cryptographic Issues
|
|
815
|
+
- Hard-coded Credentials
|
|
816
|
+
patterns:
|
|
817
|
+
- pattern-either:
|
|
818
|
+
- pattern: '$APP.config[$KEY] = os.getenv($ENV, "$LITERAL")
|
|
819
|
+
|
|
820
|
+
'
|
|
821
|
+
- pattern: '$APP.config[$KEY] = os.environ.get($ENV, "$LITERAL")
|
|
822
|
+
|
|
823
|
+
'
|
|
824
|
+
- pattern: '$APP.config[$KEY] = os.environ.get($ENV) or "$LITERAL"
|
|
825
|
+
|
|
826
|
+
'
|
|
827
|
+
- pattern: '$APP.config[$KEY] = os.getenv($ENV) or "$LITERAL"
|
|
828
|
+
|
|
829
|
+
'
|
|
830
|
+
- pattern: '$APP.config.update(..., SECRET_KEY=os.environ.get($ENV, "$LITERAL"), ...)
|
|
831
|
+
|
|
832
|
+
'
|
|
833
|
+
- pattern: '$APP.config.update(..., SECRET_KEY=os.getenv($ENV, "$LITERAL"), ...)
|
|
834
|
+
|
|
835
|
+
'
|
|
836
|
+
- metavariable-regex:
|
|
837
|
+
metavariable: $LITERAL
|
|
838
|
+
regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
|
|
839
|
+
- metavariable-regex:
|
|
840
|
+
metavariable: $KEY
|
|
841
|
+
regex: ^['"]SECRET_KEY['"]$
|
|
842
|
+
- id: narvy.python.crypto.hardcoded-env-fallback-to-cipher.hardcoded-env-fallback-to-cipher
|
|
843
|
+
message: Hardcoded fallback secret '$LITERAL' is passed as a symmetric cipher key when the env var is
|
|
844
|
+
unset. Defeats the entire crypto operation. Fail fast on missing env at startup and derive the key
|
|
845
|
+
via HKDF / PBKDF2 with a per-record salt.
|
|
846
|
+
severity: ERROR
|
|
847
|
+
languages:
|
|
848
|
+
- python
|
|
849
|
+
metadata:
|
|
850
|
+
cwe:
|
|
851
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
852
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
853
|
+
owasp:
|
|
854
|
+
- A02:2021 - Cryptographic Failures
|
|
855
|
+
category: security
|
|
856
|
+
subcategory:
|
|
857
|
+
- vuln
|
|
858
|
+
technology:
|
|
859
|
+
- python
|
|
860
|
+
- cryptography
|
|
861
|
+
- pycryptodome
|
|
862
|
+
likelihood: HIGH
|
|
863
|
+
impact: HIGH
|
|
864
|
+
confidence: HIGH
|
|
865
|
+
vulnerability_class:
|
|
866
|
+
- Cryptographic Issues
|
|
867
|
+
- Hard-coded Credentials
|
|
868
|
+
patterns:
|
|
869
|
+
- pattern-either:
|
|
870
|
+
- pattern: 'AES.new(os.getenv($ENV, "$LITERAL").encode(...), ...)
|
|
871
|
+
|
|
872
|
+
'
|
|
873
|
+
- pattern: 'AES.new(os.environ.get($ENV, "$LITERAL").encode(...), ...)
|
|
874
|
+
|
|
875
|
+
'
|
|
876
|
+
- pattern: 'AES.new((os.environ.get($ENV) or "$LITERAL").encode(...), ...)
|
|
877
|
+
|
|
878
|
+
'
|
|
879
|
+
- pattern: 'algorithms.AES(os.getenv($ENV, "$LITERAL").encode(...))
|
|
880
|
+
|
|
881
|
+
'
|
|
882
|
+
- pattern: 'algorithms.AES(os.environ.get($ENV, "$LITERAL").encode(...))
|
|
883
|
+
|
|
884
|
+
'
|
|
885
|
+
- pattern: '$KDF.derive(os.getenv($ENV, "$LITERAL").encode(...))
|
|
886
|
+
|
|
887
|
+
'
|
|
888
|
+
- pattern: '$KDF.derive(os.environ.get($ENV, "$LITERAL").encode(...))
|
|
889
|
+
|
|
890
|
+
'
|
|
891
|
+
- pattern: '$K = os.getenv($ENV, "$LITERAL")
|
|
892
|
+
|
|
893
|
+
...
|
|
894
|
+
|
|
895
|
+
AES.new($K.encode(...), ...)
|
|
896
|
+
|
|
897
|
+
'
|
|
898
|
+
- pattern: '$K = os.environ.get($ENV, "$LITERAL")
|
|
899
|
+
|
|
900
|
+
...
|
|
901
|
+
|
|
902
|
+
AES.new($K.encode(...), ...)
|
|
903
|
+
|
|
904
|
+
'
|
|
905
|
+
- metavariable-regex:
|
|
906
|
+
metavariable: $LITERAL
|
|
907
|
+
regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
|
|
908
|
+
- id: narvy.python.crypto.hardcoded-env-fallback-to-hash.hardcoded-env-fallback-to-hash
|
|
909
|
+
message: Hardcoded fallback secret '$LITERAL' is used when the environment variable is unset, and is
|
|
910
|
+
then fed into a cryptographic hash. If the env var is missing in any deployment, the hash input reduces
|
|
911
|
+
to a public constant. Use HKDF / PBKDF2 / scrypt / Argon2, and fail fast on missing env at startup.
|
|
912
|
+
severity: ERROR
|
|
913
|
+
languages:
|
|
914
|
+
- python
|
|
915
|
+
metadata:
|
|
916
|
+
cwe:
|
|
917
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
918
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
919
|
+
- 'CWE-916: Use of Password Hash With Insufficient Computational Effort'
|
|
920
|
+
owasp:
|
|
921
|
+
- A02:2021 - Cryptographic Failures
|
|
922
|
+
category: security
|
|
923
|
+
subcategory:
|
|
924
|
+
- vuln
|
|
925
|
+
technology:
|
|
926
|
+
- python
|
|
927
|
+
- hashlib
|
|
928
|
+
likelihood: HIGH
|
|
929
|
+
impact: HIGH
|
|
930
|
+
confidence: HIGH
|
|
931
|
+
vulnerability_class:
|
|
932
|
+
- Cryptographic Issues
|
|
933
|
+
- Hard-coded Credentials
|
|
934
|
+
patterns:
|
|
935
|
+
- pattern-either:
|
|
936
|
+
- pattern: 'hashlib.$ALGO(os.getenv($ENV, "$LITERAL").encode(...))
|
|
937
|
+
|
|
938
|
+
'
|
|
939
|
+
- pattern: 'hashlib.$ALGO(os.getenv($ENV, "$LITERAL").encode())
|
|
940
|
+
|
|
941
|
+
'
|
|
942
|
+
- pattern: 'hashlib.$ALGO(os.environ.get($ENV, "$LITERAL").encode(...))
|
|
943
|
+
|
|
944
|
+
'
|
|
945
|
+
- pattern: 'hashlib.$ALGO(os.environ.get($ENV, "$LITERAL").encode())
|
|
946
|
+
|
|
947
|
+
'
|
|
948
|
+
- pattern: 'hashlib.$ALGO((os.environ.get($ENV) or "$LITERAL").encode(...))
|
|
949
|
+
|
|
950
|
+
'
|
|
951
|
+
- pattern: 'hashlib.$ALGO((os.getenv($ENV) or "$LITERAL").encode(...))
|
|
952
|
+
|
|
953
|
+
'
|
|
954
|
+
- pattern: '$K = os.getenv($ENV, "$LITERAL")
|
|
955
|
+
|
|
956
|
+
...
|
|
957
|
+
|
|
958
|
+
hashlib.$ALGO($K.encode(...))
|
|
959
|
+
|
|
960
|
+
'
|
|
961
|
+
- pattern: '$K = os.environ.get($ENV, "$LITERAL")
|
|
962
|
+
|
|
963
|
+
...
|
|
964
|
+
|
|
965
|
+
hashlib.$ALGO($K.encode(...))
|
|
966
|
+
|
|
967
|
+
'
|
|
968
|
+
- pattern: '$K = os.environ.get($ENV) or "$LITERAL"
|
|
969
|
+
|
|
970
|
+
...
|
|
971
|
+
|
|
972
|
+
hashlib.$ALGO($K.encode(...))
|
|
973
|
+
|
|
974
|
+
'
|
|
975
|
+
- pattern: '$K = os.getenv($ENV) or "$LITERAL"
|
|
976
|
+
|
|
977
|
+
...
|
|
978
|
+
|
|
979
|
+
hashlib.$ALGO($K.encode(...))
|
|
980
|
+
|
|
981
|
+
'
|
|
982
|
+
- pattern: "$K = os.getenv($ENV)\nif not $K:\n $K = \"$LITERAL\"\n...\nhashlib.$ALGO($K.encode(...))\n"
|
|
983
|
+
- pattern: "$K = os.environ.get($ENV)\nif not $K:\n $K = \"$LITERAL\"\n...\nhashlib.$ALGO($K.encode(...))\n"
|
|
984
|
+
- metavariable-regex:
|
|
985
|
+
metavariable: $LITERAL
|
|
986
|
+
regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
|
|
987
|
+
- id: narvy.python.crypto.hardcoded-env-fallback-to-hmac.hardcoded-env-fallback-to-hmac
|
|
988
|
+
message: Hardcoded fallback secret '$LITERAL' is used as the HMAC key when the environment variable
|
|
989
|
+
is unset. Anyone reading this source can forge valid MACs. Fail fast on missing env at startup.
|
|
990
|
+
severity: ERROR
|
|
991
|
+
languages:
|
|
992
|
+
- python
|
|
993
|
+
metadata:
|
|
994
|
+
cwe:
|
|
995
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
996
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
997
|
+
owasp:
|
|
998
|
+
- A02:2021 - Cryptographic Failures
|
|
999
|
+
category: security
|
|
1000
|
+
subcategory:
|
|
1001
|
+
- vuln
|
|
1002
|
+
technology:
|
|
1003
|
+
- python
|
|
1004
|
+
- hmac
|
|
1005
|
+
likelihood: HIGH
|
|
1006
|
+
impact: HIGH
|
|
1007
|
+
confidence: HIGH
|
|
1008
|
+
vulnerability_class:
|
|
1009
|
+
- Cryptographic Issues
|
|
1010
|
+
- Hard-coded Credentials
|
|
1011
|
+
patterns:
|
|
1012
|
+
- pattern-either:
|
|
1013
|
+
- pattern: 'hmac.new(os.getenv($ENV, "$LITERAL").encode(...), ...)
|
|
1014
|
+
|
|
1015
|
+
'
|
|
1016
|
+
- pattern: 'hmac.new(os.environ.get($ENV, "$LITERAL").encode(...), ...)
|
|
1017
|
+
|
|
1018
|
+
'
|
|
1019
|
+
- pattern: 'hmac.new((os.environ.get($ENV) or "$LITERAL").encode(...), ...)
|
|
1020
|
+
|
|
1021
|
+
'
|
|
1022
|
+
- pattern: 'hmac.new((os.getenv($ENV) or "$LITERAL").encode(...), ...)
|
|
1023
|
+
|
|
1024
|
+
'
|
|
1025
|
+
- pattern: '$K = os.getenv($ENV, "$LITERAL")
|
|
1026
|
+
|
|
1027
|
+
...
|
|
1028
|
+
|
|
1029
|
+
hmac.new($K.encode(...), ...)
|
|
1030
|
+
|
|
1031
|
+
'
|
|
1032
|
+
- pattern: '$K = os.environ.get($ENV, "$LITERAL")
|
|
1033
|
+
|
|
1034
|
+
...
|
|
1035
|
+
|
|
1036
|
+
hmac.new($K.encode(...), ...)
|
|
1037
|
+
|
|
1038
|
+
'
|
|
1039
|
+
- pattern: '$K = os.environ.get($ENV) or "$LITERAL"
|
|
1040
|
+
|
|
1041
|
+
...
|
|
1042
|
+
|
|
1043
|
+
hmac.new($K.encode(...), ...)
|
|
1044
|
+
|
|
1045
|
+
'
|
|
1046
|
+
- pattern: "$K = os.getenv($ENV)\nif not $K:\n $K = \"$LITERAL\"\n...\nhmac.new($K.encode(...),\
|
|
1047
|
+
\ ...)\n"
|
|
1048
|
+
- metavariable-regex:
|
|
1049
|
+
metavariable: $LITERAL
|
|
1050
|
+
regex: ^[A-Za-z0-9 _!@#$%^&*()+=,.:?<>\[\]{}|/\\-]{1,64}$
|
|
1051
|
+
- id: narvy.python.crypto.insecure-uuid4-substitute.insecure-uuid-from-weak-random
|
|
1052
|
+
message: A UUID-like identifier is being built from `random`/`os.urandom` seeded predictably or from
|
|
1053
|
+
`uuid.uuid1` (which embeds the MAC address and a timestamp - both guessable/enumerable). For anything
|
|
1054
|
+
used as a security token (session id, password-reset id, API key), use `uuid.uuid4()` backed by `os.urandom`,
|
|
1055
|
+
or `secrets.token_urlsafe()`. CWE-330.
|
|
1056
|
+
severity: WARNING
|
|
1057
|
+
languages:
|
|
1058
|
+
- python
|
|
1059
|
+
metadata:
|
|
1060
|
+
cwe:
|
|
1061
|
+
- 'CWE-330: Use of Insufficiently Random Values'
|
|
1062
|
+
owasp:
|
|
1063
|
+
- A02:2021 - Cryptographic Failures
|
|
1064
|
+
category: security
|
|
1065
|
+
subcategory:
|
|
1066
|
+
- vuln
|
|
1067
|
+
technology:
|
|
1068
|
+
- python
|
|
1069
|
+
likelihood: LOW
|
|
1070
|
+
impact: MEDIUM
|
|
1071
|
+
confidence: MEDIUM
|
|
1072
|
+
vulnerability_class:
|
|
1073
|
+
- Cryptographic Issues
|
|
1074
|
+
patterns:
|
|
1075
|
+
- pattern: uuid.uuid1(...)
|
|
1076
|
+
- id: narvy.python.crypto.md5-as-password-hash.md5-as-password-hash
|
|
1077
|
+
message: 'MD5 digest used to hash what looks like a password/credential value. MD5 is fast to brute-force
|
|
1078
|
+
with commodity GPUs and has no built-in salt. Use a slow, salted KDF: bcrypt, scrypt, PBKDF2-HMAC-SHA256,
|
|
1079
|
+
or Argon2. CWE-916.'
|
|
1080
|
+
severity: ERROR
|
|
1081
|
+
languages:
|
|
1082
|
+
- python
|
|
1083
|
+
metadata:
|
|
1084
|
+
cwe:
|
|
1085
|
+
- 'CWE-916: Use of Password Hash With Insufficient Computational Effort'
|
|
1086
|
+
owasp:
|
|
1087
|
+
- A02:2021 - Cryptographic Failures
|
|
1088
|
+
category: security
|
|
1089
|
+
subcategory:
|
|
1090
|
+
- vuln
|
|
1091
|
+
technology:
|
|
1092
|
+
- python
|
|
1093
|
+
- hashlib
|
|
1094
|
+
likelihood: HIGH
|
|
1095
|
+
impact: HIGH
|
|
1096
|
+
confidence: MEDIUM
|
|
1097
|
+
vulnerability_class:
|
|
1098
|
+
- Cryptographic Issues
|
|
1099
|
+
patterns:
|
|
1100
|
+
- pattern-either:
|
|
1101
|
+
- pattern: hashlib.md5($PW.encode(...)).hexdigest()
|
|
1102
|
+
- pattern: hashlib.md5($PW.encode(...)).digest()
|
|
1103
|
+
- metavariable-regex:
|
|
1104
|
+
metavariable: $PW
|
|
1105
|
+
regex: (?i)(pass|pwd|secret|credential)
|
|
1106
|
+
- id: narvy.python.crypto.non-cryptographic-random-for-security.non-cryptographic-random-for-security
|
|
1107
|
+
message: The `random` module is not a cryptographically secure PRNG - it is a Mersenne Twister, fully
|
|
1108
|
+
predictable from a handful of outputs. Using it to build a token/password/reset-code/secret value
|
|
1109
|
+
lets an attacker predict future or past outputs. Use `secrets.token_urlsafe()` / `secrets.token_hex()`
|
|
1110
|
+
/ `os.urandom()` instead. CWE-338.
|
|
1111
|
+
severity: ERROR
|
|
1112
|
+
languages:
|
|
1113
|
+
- python
|
|
1114
|
+
metadata:
|
|
1115
|
+
cwe:
|
|
1116
|
+
- 'CWE-338: Use of Cryptographically Weak PRNG'
|
|
1117
|
+
owasp:
|
|
1118
|
+
- A02:2021 - Cryptographic Failures
|
|
1119
|
+
category: security
|
|
1120
|
+
subcategory:
|
|
1121
|
+
- vuln
|
|
1122
|
+
technology:
|
|
1123
|
+
- python
|
|
1124
|
+
likelihood: MEDIUM
|
|
1125
|
+
impact: HIGH
|
|
1126
|
+
confidence: MEDIUM
|
|
1127
|
+
vulnerability_class:
|
|
1128
|
+
- Cryptographic Issues
|
|
1129
|
+
patterns:
|
|
1130
|
+
- pattern-either:
|
|
1131
|
+
- pattern: '$TOK = random.$FN(...)
|
|
1132
|
+
|
|
1133
|
+
'
|
|
1134
|
+
- pattern: '$TOK = "".join(random.$FN(...) for $I in range(...))
|
|
1135
|
+
|
|
1136
|
+
'
|
|
1137
|
+
- metavariable-regex:
|
|
1138
|
+
metavariable: $FN
|
|
1139
|
+
regex: ^(random|randint|randrange|choice|choices|getrandbits)$
|
|
1140
|
+
- metavariable-regex:
|
|
1141
|
+
metavariable: $TOK
|
|
1142
|
+
regex: (?i)(token|secret|password|passwd|reset_code|otp|api_key|session_id|csrf)
|
|
1143
|
+
- id: narvy.python.crypto.ssl-cert-verification-disabled.ssl-cert-verification-disabled
|
|
1144
|
+
message: TLS certificate verification is explicitly disabled (CERT_NONE / check_hostname=False / verify=False).
|
|
1145
|
+
This makes the connection trivially interceptable by an on-path attacker (MITM), even over HTTPS.
|
|
1146
|
+
Only ever acceptable for local, throwaway test fixtures - never for anything that touches real credentials
|
|
1147
|
+
or production data. CWE-295.
|
|
1148
|
+
severity: ERROR
|
|
1149
|
+
languages:
|
|
1150
|
+
- python
|
|
1151
|
+
metadata:
|
|
1152
|
+
cwe:
|
|
1153
|
+
- 'CWE-295: Improper Certificate Validation'
|
|
1154
|
+
owasp:
|
|
1155
|
+
- A02:2021 - Cryptographic Failures
|
|
1156
|
+
category: security
|
|
1157
|
+
subcategory:
|
|
1158
|
+
- vuln
|
|
1159
|
+
technology:
|
|
1160
|
+
- python
|
|
1161
|
+
- ssl
|
|
1162
|
+
- requests
|
|
1163
|
+
likelihood: HIGH
|
|
1164
|
+
impact: HIGH
|
|
1165
|
+
confidence: HIGH
|
|
1166
|
+
vulnerability_class:
|
|
1167
|
+
- Cryptographic Issues
|
|
1168
|
+
- Improper Authentication
|
|
1169
|
+
patterns:
|
|
1170
|
+
- pattern-either:
|
|
1171
|
+
- pattern: $CTX.verify_mode = ssl.CERT_NONE
|
|
1172
|
+
- pattern: $CTX.check_hostname = False
|
|
1173
|
+
- pattern: requests.$METHOD(..., verify=False, ...)
|
|
1174
|
+
- pattern: requests.Session().request(..., verify=False, ...)
|
|
1175
|
+
- pattern: ssl._create_unverified_context(...)
|
|
1176
|
+
- pattern-not: requests.$METHOD("http://localhost$REST", ..., verify=False, ...)
|
|
1177
|
+
- id: narvy.python.crypto.unauthenticated-cbc-no-mac.unauthenticated-cbc-no-mac
|
|
1178
|
+
message: CBC-mode encryption with no accompanying HMAC/authentication tag detected nearby. Unauthenticated
|
|
1179
|
+
CBC is vulnerable to padding-oracle and bit-flipping attacks. Prefer AES-GCM (authenticated) or add
|
|
1180
|
+
an encrypt-then-MAC (HMAC-SHA256) construction. CWE-327.
|
|
1181
|
+
severity: WARNING
|
|
1182
|
+
languages:
|
|
1183
|
+
- python
|
|
1184
|
+
metadata:
|
|
1185
|
+
cwe:
|
|
1186
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
1187
|
+
owasp:
|
|
1188
|
+
- A02:2021 - Cryptographic Failures
|
|
1189
|
+
category: security
|
|
1190
|
+
subcategory:
|
|
1191
|
+
- vuln
|
|
1192
|
+
technology:
|
|
1193
|
+
- pycryptodome
|
|
1194
|
+
likelihood: LOW
|
|
1195
|
+
impact: HIGH
|
|
1196
|
+
confidence: LOW
|
|
1197
|
+
vulnerability_class:
|
|
1198
|
+
- Cryptographic Issues
|
|
1199
|
+
patterns:
|
|
1200
|
+
- pattern: $CIPHER = AES.new($KEY, AES.MODE_CBC, ...)
|
|
1201
|
+
- pattern-not-inside: '$CIPHER = AES.new($KEY, AES.MODE_CBC, ...)
|
|
1202
|
+
|
|
1203
|
+
...
|
|
1204
|
+
|
|
1205
|
+
hmac.new(...)
|
|
1206
|
+
|
|
1207
|
+
'
|
|
1208
|
+
- pattern-not-inside: '$CIPHER = AES.new($KEY, AES.MODE_CBC, ...)
|
|
1209
|
+
|
|
1210
|
+
...
|
|
1211
|
+
|
|
1212
|
+
$HM.update(...)
|
|
1213
|
+
|
|
1214
|
+
'
|
|
1215
|
+
- id: narvy.python.crypto.weak-dsa-key-size.weak-dsa-key-size
|
|
1216
|
+
message: DSA key size $BITS is below the 2048-bit minimum. Undersized DSA keys are vulnerable to discrete-log
|
|
1217
|
+
attacks with modern compute. CWE-326.
|
|
1218
|
+
severity: WARNING
|
|
1219
|
+
languages:
|
|
1220
|
+
- python
|
|
1221
|
+
metadata:
|
|
1222
|
+
cwe:
|
|
1223
|
+
- 'CWE-326: Inadequate Encryption Strength'
|
|
1224
|
+
owasp:
|
|
1225
|
+
- A02:2021 - Cryptographic Failures
|
|
1226
|
+
category: security
|
|
1227
|
+
subcategory:
|
|
1228
|
+
- vuln
|
|
1229
|
+
technology:
|
|
1230
|
+
- cryptography
|
|
1231
|
+
- pycryptodome
|
|
1232
|
+
likelihood: LOW
|
|
1233
|
+
impact: HIGH
|
|
1234
|
+
confidence: HIGH
|
|
1235
|
+
vulnerability_class:
|
|
1236
|
+
- Cryptographic Issues
|
|
1237
|
+
patterns:
|
|
1238
|
+
- pattern-either:
|
|
1239
|
+
- pattern: dsa.generate_private_key(key_size=$BITS, ...)
|
|
1240
|
+
- pattern: DSA.generate($BITS, ...)
|
|
1241
|
+
- metavariable-comparison:
|
|
1242
|
+
metavariable: $BITS
|
|
1243
|
+
comparison: int($BITS) < 2048
|
|
1244
|
+
- id: narvy.python.crypto.weak-ec-curve.weak-ec-curve
|
|
1245
|
+
message: Elliptic-curve key generated on curve '$CURVE', which provides less than 128 bits of security.
|
|
1246
|
+
Use SECP256R1 (P-256) or a stronger curve. CWE-326.
|
|
1247
|
+
severity: WARNING
|
|
1248
|
+
languages:
|
|
1249
|
+
- python
|
|
1250
|
+
metadata:
|
|
1251
|
+
cwe:
|
|
1252
|
+
- 'CWE-326: Inadequate Encryption Strength'
|
|
1253
|
+
owasp:
|
|
1254
|
+
- A02:2021 - Cryptographic Failures
|
|
1255
|
+
category: security
|
|
1256
|
+
subcategory:
|
|
1257
|
+
- vuln
|
|
1258
|
+
technology:
|
|
1259
|
+
- cryptography
|
|
1260
|
+
likelihood: LOW
|
|
1261
|
+
impact: HIGH
|
|
1262
|
+
confidence: MEDIUM
|
|
1263
|
+
vulnerability_class:
|
|
1264
|
+
- Cryptographic Issues
|
|
1265
|
+
patterns:
|
|
1266
|
+
- pattern: ec.generate_private_key(ec.$CURVE(), ...)
|
|
1267
|
+
- metavariable-regex:
|
|
1268
|
+
metavariable: $CURVE
|
|
1269
|
+
regex: ^(SECP192R1|SECT163K1|SECT163R2)$
|
|
1270
|
+
- id: narvy.python.crypto.weak-hash-cryptography-hazmat.weak-hash-cryptography-hazmat
|
|
1271
|
+
message: Weak hash algorithm '$ALGO' instantiated from cryptography.hazmat.primitives.hashes. MD5 and
|
|
1272
|
+
SHA1 are broken for collision resistance, whether the digest is used standalone (hashes.Hash), inside
|
|
1273
|
+
an HMAC, or as a signature padding hash. Use hashes.SHA256() or stronger. CWE-327.
|
|
1274
|
+
severity: WARNING
|
|
1275
|
+
languages:
|
|
1276
|
+
- python
|
|
1277
|
+
metadata:
|
|
1278
|
+
cwe:
|
|
1279
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
1280
|
+
owasp:
|
|
1281
|
+
- A02:2021 - Cryptographic Failures
|
|
1282
|
+
category: security
|
|
1283
|
+
subcategory:
|
|
1284
|
+
- vuln
|
|
1285
|
+
technology:
|
|
1286
|
+
- python
|
|
1287
|
+
- cryptography
|
|
1288
|
+
likelihood: MEDIUM
|
|
1289
|
+
impact: MEDIUM
|
|
1290
|
+
confidence: HIGH
|
|
1291
|
+
vulnerability_class:
|
|
1292
|
+
- Cryptographic Issues
|
|
1293
|
+
references:
|
|
1294
|
+
- https://cwe.mitre.org/data/definitions/327.html
|
|
1295
|
+
patterns:
|
|
1296
|
+
- pattern: hashes.$ALGO()
|
|
1297
|
+
- metavariable-regex:
|
|
1298
|
+
metavariable: $ALGO
|
|
1299
|
+
regex: ^(MD5|SHA1)$
|
|
1300
|
+
- id: narvy.python.crypto.weak-hash-hashlib.weak-hash-hashlib
|
|
1301
|
+
message: Weak hash algorithm '$ALGO' from hashlib. MD5, SHA1, MD4 and MD2 are broken for collision resistance
|
|
1302
|
+
and unsuitable for integrity checks, digital signatures, or password storage. Use SHA-256 or better
|
|
1303
|
+
for integrity, and Argon2/bcrypt/scrypt for passwords. CWE-327.
|
|
1304
|
+
severity: WARNING
|
|
1305
|
+
languages:
|
|
1306
|
+
- python
|
|
1307
|
+
metadata:
|
|
1308
|
+
cwe:
|
|
1309
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
1310
|
+
owasp:
|
|
1311
|
+
- A02:2021 - Cryptographic Failures
|
|
1312
|
+
category: security
|
|
1313
|
+
subcategory:
|
|
1314
|
+
- vuln
|
|
1315
|
+
technology:
|
|
1316
|
+
- python
|
|
1317
|
+
- hashlib
|
|
1318
|
+
likelihood: MEDIUM
|
|
1319
|
+
impact: MEDIUM
|
|
1320
|
+
confidence: HIGH
|
|
1321
|
+
vulnerability_class:
|
|
1322
|
+
- Cryptographic Issues
|
|
1323
|
+
references:
|
|
1324
|
+
- https://cwe.mitre.org/data/definitions/327.html
|
|
1325
|
+
patterns:
|
|
1326
|
+
- pattern-either:
|
|
1327
|
+
- pattern: hashlib.$ALGO(...)
|
|
1328
|
+
- pattern: hashlib.new("$ALGO", ...)
|
|
1329
|
+
- pattern: hashlib.new($X, name="$ALGO", ...)
|
|
1330
|
+
- metavariable-regex:
|
|
1331
|
+
metavariable: $ALGO
|
|
1332
|
+
regex: (?i)^(md5|md4|md2|sha1|sha)$
|
|
1333
|
+
- pattern-not: hashlib.$ALGO(..., usedforsecurity=False, ...)
|
|
1334
|
+
- pattern-not: hashlib.new("$ALGO", ..., usedforsecurity=False, ...)
|
|
1335
|
+
- id: narvy.python.crypto.weak-hash-pycryptodome.weak-hash-pycryptodome
|
|
1336
|
+
message: Weak hash algorithm '$ALGO' from PyCryptodome/PyCrypto. MD5/MD2/MD4/SHA1 are broken for collision
|
|
1337
|
+
resistance. Use SHA256, SHA3_256, or BLAKE2 instead. CWE-327.
|
|
1338
|
+
severity: WARNING
|
|
1339
|
+
languages:
|
|
1340
|
+
- python
|
|
1341
|
+
metadata:
|
|
1342
|
+
cwe:
|
|
1343
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
1344
|
+
owasp:
|
|
1345
|
+
- A02:2021 - Cryptographic Failures
|
|
1346
|
+
category: security
|
|
1347
|
+
subcategory:
|
|
1348
|
+
- vuln
|
|
1349
|
+
technology:
|
|
1350
|
+
- pycryptodome
|
|
1351
|
+
- pycrypto
|
|
1352
|
+
likelihood: MEDIUM
|
|
1353
|
+
impact: MEDIUM
|
|
1354
|
+
confidence: HIGH
|
|
1355
|
+
vulnerability_class:
|
|
1356
|
+
- Cryptographic Issues
|
|
1357
|
+
patterns:
|
|
1358
|
+
- pattern-either:
|
|
1359
|
+
- pattern: Crypto.Hash.$ALGO.new(...)
|
|
1360
|
+
- pattern: $ALGO.new(...)
|
|
1361
|
+
- metavariable-regex:
|
|
1362
|
+
metavariable: $ALGO
|
|
1363
|
+
regex: ^(MD5|MD4|MD2|SHA1|SHA)$
|
|
1364
|
+
- id: narvy.python.crypto.weak-rsa-key-size.weak-rsa-key-size
|
|
1365
|
+
message: RSA key size $BITS is below 3072 bits. NIST SP 800-57 rates 2048-bit RSA as acceptable only
|
|
1366
|
+
through 2030; 3072 bits is the current recommendation for keys expected to remain in use beyond that,
|
|
1367
|
+
and is a safer default for any key generated today. CWE-326.
|
|
1368
|
+
severity: WARNING
|
|
1369
|
+
languages:
|
|
1370
|
+
- python
|
|
1371
|
+
metadata:
|
|
1372
|
+
cwe:
|
|
1373
|
+
- 'CWE-326: Inadequate Encryption Strength'
|
|
1374
|
+
owasp:
|
|
1375
|
+
- A02:2021 - Cryptographic Failures
|
|
1376
|
+
category: security
|
|
1377
|
+
subcategory:
|
|
1378
|
+
- vuln
|
|
1379
|
+
technology:
|
|
1380
|
+
- cryptography
|
|
1381
|
+
- pycryptodome
|
|
1382
|
+
likelihood: LOW
|
|
1383
|
+
impact: HIGH
|
|
1384
|
+
confidence: HIGH
|
|
1385
|
+
vulnerability_class:
|
|
1386
|
+
- Cryptographic Issues
|
|
1387
|
+
patterns:
|
|
1388
|
+
- pattern-either:
|
|
1389
|
+
- pattern: rsa.generate_private_key(..., key_size=$BITS, ...)
|
|
1390
|
+
- pattern: RSA.generate($BITS, ...)
|
|
1391
|
+
- metavariable-comparison:
|
|
1392
|
+
metavariable: $BITS
|
|
1393
|
+
comparison: int($BITS) < 3072
|
|
1394
|
+
- id: narvy.python.crypto.weak-tls-protocol-version.weak-tls-protocol-version
|
|
1395
|
+
message: Deprecated/insecure TLS protocol version '$PROTO' requested explicitly. SSLv2, SSLv3, and TLSv1.0/1.1
|
|
1396
|
+
all have known cryptographic weaknesses (POODLE, BEAST) and are disallowed by PCI-DSS since mid-2018.
|
|
1397
|
+
Use `ssl.PROTOCOL_TLS_CLIENT` or explicitly require TLS 1.2+. CWE-327.
|
|
1398
|
+
severity: ERROR
|
|
1399
|
+
languages:
|
|
1400
|
+
- python
|
|
1401
|
+
metadata:
|
|
1402
|
+
cwe:
|
|
1403
|
+
- 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm'
|
|
1404
|
+
owasp:
|
|
1405
|
+
- A02:2021 - Cryptographic Failures
|
|
1406
|
+
category: security
|
|
1407
|
+
subcategory:
|
|
1408
|
+
- vuln
|
|
1409
|
+
technology:
|
|
1410
|
+
- python
|
|
1411
|
+
- ssl
|
|
1412
|
+
likelihood: MEDIUM
|
|
1413
|
+
impact: HIGH
|
|
1414
|
+
confidence: HIGH
|
|
1415
|
+
vulnerability_class:
|
|
1416
|
+
- Cryptographic Issues
|
|
1417
|
+
patterns:
|
|
1418
|
+
- pattern: ssl.$PROTO
|
|
1419
|
+
- metavariable-regex:
|
|
1420
|
+
metavariable: $PROTO
|
|
1421
|
+
regex: ^PROTOCOL_(SSLv2|SSLv3|TLSv1|TLSv1_1)$
|
|
1422
|
+
- id: narvy.python.csrf.flask-wtf-csrf-enabled-false.flask-wtf-csrf-enabled-false
|
|
1423
|
+
message: '`WTF_CSRF_ENABLED` is explicitly set to `False`. This disables CSRF token validation on every
|
|
1424
|
+
form submitted through this Flask app, making every state-changing POST/PUT/DELETE endpoint that relies
|
|
1425
|
+
on cookie-based session auth forgeable from any other site the victim has open (CWE-352). Remove this
|
|
1426
|
+
line, or scope the exemption to the one view that genuinely needs it with `@csrf.exempt`.'
|
|
1427
|
+
severity: ERROR
|
|
1428
|
+
languages:
|
|
1429
|
+
- python
|
|
1430
|
+
metadata:
|
|
1431
|
+
cwe:
|
|
1432
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
1433
|
+
owasp:
|
|
1434
|
+
- A01:2021 - Broken Access Control
|
|
1435
|
+
category: security
|
|
1436
|
+
subcategory:
|
|
1437
|
+
- vuln
|
|
1438
|
+
technology:
|
|
1439
|
+
- flask
|
|
1440
|
+
- flask-wtf
|
|
1441
|
+
likelihood: MEDIUM
|
|
1442
|
+
impact: HIGH
|
|
1443
|
+
confidence: HIGH
|
|
1444
|
+
vulnerability_class:
|
|
1445
|
+
- Cross-Site Request Forgery (CSRF)
|
|
1446
|
+
patterns:
|
|
1447
|
+
- pattern-either:
|
|
1448
|
+
- pattern: $APP.config["WTF_CSRF_ENABLED"] = False
|
|
1449
|
+
- pattern: $APP.config['WTF_CSRF_ENABLED'] = False
|
|
1450
|
+
- pattern: $APP.config.WTF_CSRF_ENABLED = False
|
|
1451
|
+
- pattern: WTF_CSRF_ENABLED = False
|
|
1452
|
+
- id: narvy.python.csrf.flask-wtf-csrf-exempt-usage.flask-wtf-csrf-exempt-usage
|
|
1453
|
+
message: '`csrf.exempt(...)` opts its argument out of CSRF protection. When the argument is a Blueprint
|
|
1454
|
+
(rather than a single view function), every state-changing route registered on it - present and future
|
|
1455
|
+
- loses CSRF protection at once, which is easy to lose track of as the blueprint grows (CWE-352).
|
|
1456
|
+
Semgrep cannot tell a Blueprint apart from a view function here without symbol resolution, so this
|
|
1457
|
+
is flagged for manual review either way: confirm every exempted route is genuinely authenticated by
|
|
1458
|
+
something other than cookies (e.g. a redirect-based external IdP callback, or a token-checked API
|
|
1459
|
+
view).'
|
|
1460
|
+
severity: WARNING
|
|
1461
|
+
languages:
|
|
1462
|
+
- python
|
|
1463
|
+
metadata:
|
|
1464
|
+
cwe:
|
|
1465
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
1466
|
+
owasp:
|
|
1467
|
+
- A01:2021 - Broken Access Control
|
|
1468
|
+
category: security
|
|
1469
|
+
subcategory:
|
|
1470
|
+
- audit
|
|
1471
|
+
technology:
|
|
1472
|
+
- flask
|
|
1473
|
+
- flask-wtf
|
|
1474
|
+
likelihood: LOW
|
|
1475
|
+
impact: MEDIUM
|
|
1476
|
+
confidence: LOW
|
|
1477
|
+
vulnerability_class:
|
|
1478
|
+
- Cross-Site Request Forgery (CSRF)
|
|
1479
|
+
patterns:
|
|
1480
|
+
- pattern: $CSRF.exempt($ARG)
|
|
1481
|
+
- metavariable-regex:
|
|
1482
|
+
metavariable: $CSRF
|
|
1483
|
+
regex: ^csrf$
|
|
1484
|
+
- id: narvy.python.csrf.flask-wtf-imported-but-never-initialized.flask-wtf-imported-but-never-initialized
|
|
1485
|
+
message: '`CSRFProtect` is imported from `flask_wtf.csrf` but the module never constructs it against
|
|
1486
|
+
the app (`CSRFProtect(app)`) or calls `.init_app(app)` on an instance of it. The import alone protects
|
|
1487
|
+
nothing - every form in the app is submitted without CSRF token validation (CWE-352). Either wire
|
|
1488
|
+
it up (`CSRFProtect(app)`/`csrf.init_app(app)`) or remove the dead import if CSRF protection is intentionally
|
|
1489
|
+
handled elsewhere.'
|
|
1490
|
+
severity: WARNING
|
|
1491
|
+
languages:
|
|
1492
|
+
- python
|
|
1493
|
+
metadata:
|
|
1494
|
+
cwe:
|
|
1495
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
1496
|
+
owasp:
|
|
1497
|
+
- A01:2021 - Broken Access Control
|
|
1498
|
+
category: security
|
|
1499
|
+
subcategory:
|
|
1500
|
+
- audit
|
|
1501
|
+
technology:
|
|
1502
|
+
- flask
|
|
1503
|
+
- flask-wtf
|
|
1504
|
+
likelihood: LOW
|
|
1505
|
+
impact: MEDIUM
|
|
1506
|
+
confidence: LOW
|
|
1507
|
+
vulnerability_class:
|
|
1508
|
+
- Cross-Site Request Forgery (CSRF)
|
|
1509
|
+
patterns:
|
|
1510
|
+
- pattern-either:
|
|
1511
|
+
- pattern: from flask_wtf.csrf import CSRFProtect
|
|
1512
|
+
- pattern: from flask_wtf import CSRFProtect
|
|
1513
|
+
- pattern-not-inside: '$IMPORT
|
|
1514
|
+
|
|
1515
|
+
...
|
|
1516
|
+
|
|
1517
|
+
CSRFProtect($APP)
|
|
1518
|
+
|
|
1519
|
+
'
|
|
1520
|
+
- pattern-not-inside: '$IMPORT
|
|
1521
|
+
|
|
1522
|
+
...
|
|
1523
|
+
|
|
1524
|
+
$CSRF = CSRFProtect()
|
|
1525
|
+
|
|
1526
|
+
...
|
|
1527
|
+
|
|
1528
|
+
$CSRF.init_app($APP)
|
|
1529
|
+
|
|
1530
|
+
'
|
|
1531
|
+
- pattern-not-inside: '$IMPORT
|
|
1532
|
+
|
|
1533
|
+
...
|
|
1534
|
+
|
|
1535
|
+
$CSRF = CSRFProtect(...)
|
|
1536
|
+
|
|
1537
|
+
'
|
|
1538
|
+
- id: narvy.python.csv-injection.csv-writer-formula-injection.csv-writer-formula-injection
|
|
1539
|
+
message: A value from request data is written to a CSV via `csv.writer` without checking for a leading
|
|
1540
|
+
`=`, `+`, `-`, or `@`. Spreadsheet applications (Excel, LibreOffice, Google Sheets) treat a cell starting
|
|
1541
|
+
with one of those characters as a formula; a value like `=cmd|'/c calc'!A1` opened by a victim who
|
|
1542
|
+
later imports this export can trigger formula execution (CSV/Formula Injection, CWE-1236). Prefix
|
|
1543
|
+
any cell beginning with `=+-@` with a single quote or tab character before writing.
|
|
1544
|
+
severity: WARNING
|
|
1545
|
+
languages:
|
|
1546
|
+
- python
|
|
1547
|
+
metadata:
|
|
1548
|
+
cwe:
|
|
1549
|
+
- 'CWE-1236: Improper Neutralization of Formula Elements in a CSV File'
|
|
1550
|
+
owasp:
|
|
1551
|
+
- A03:2021 - Injection
|
|
1552
|
+
category: security
|
|
1553
|
+
subcategory:
|
|
1554
|
+
- vuln
|
|
1555
|
+
technology:
|
|
1556
|
+
- python
|
|
1557
|
+
likelihood: LOW
|
|
1558
|
+
impact: MEDIUM
|
|
1559
|
+
confidence: LOW
|
|
1560
|
+
vulnerability_class:
|
|
1561
|
+
- Improper Validation
|
|
1562
|
+
patterns:
|
|
1563
|
+
- pattern-either:
|
|
1564
|
+
- pattern: $W.writerow([..., request.$KIND.$ATTR(...), ...])
|
|
1565
|
+
- pattern: $W.writerow([..., request.$KIND[$X], ...])
|
|
1566
|
+
- metavariable-regex:
|
|
1567
|
+
metavariable: $KIND
|
|
1568
|
+
regex: ^(args|form|values|GET|POST|json)$
|
|
1569
|
+
- id: narvy.python.ldap-injection.python-ldap-bind-dn-tainted.python-ldap-bind-dn-tainted
|
|
1570
|
+
message: A distinguished name (DN) passed to `simple_bind_s()` is built by concatenating request data.
|
|
1571
|
+
Beyond filter injection, an unsanitized DN can let an attacker manipulate the identity the bind authenticates
|
|
1572
|
+
as, or - on some LDAP servers - bind with an empty password against a partial/malformed DN to achieve
|
|
1573
|
+
an unauthenticated (anonymous) bind. CWE-90. Look the DN up from a trusted directory of known usernames
|
|
1574
|
+
instead of building it from request input.
|
|
1575
|
+
severity: WARNING
|
|
1576
|
+
languages:
|
|
1577
|
+
- python
|
|
1578
|
+
metadata:
|
|
1579
|
+
cwe:
|
|
1580
|
+
- 'CWE-90: Improper Neutralization of Special Elements used in an LDAP Query (''LDAP Injection'')'
|
|
1581
|
+
owasp:
|
|
1582
|
+
- A07:2021 - Identification and Authentication Failures
|
|
1583
|
+
category: security
|
|
1584
|
+
subcategory:
|
|
1585
|
+
- vuln
|
|
1586
|
+
technology:
|
|
1587
|
+
- python-ldap
|
|
1588
|
+
likelihood: LOW
|
|
1589
|
+
impact: HIGH
|
|
1590
|
+
confidence: LOW
|
|
1591
|
+
vulnerability_class:
|
|
1592
|
+
- Improper Authentication
|
|
1593
|
+
patterns:
|
|
1594
|
+
- pattern-either:
|
|
1595
|
+
- pattern: $CONN.simple_bind_s(f"...{$VAR}...", $PW)
|
|
1596
|
+
- pattern: $CONN.simple_bind_s("..." + $VAR, $PW)
|
|
1597
|
+
- id: narvy.python.ldap-injection.python-ldap-search-filter-tainted.python-ldap-search-filter-tainted
|
|
1598
|
+
message: An LDAP search filter is built by string interpolation with a value from request data, then
|
|
1599
|
+
passed to `search_s`/`search_ext_s`. LDAP filter syntax uses `*`, `(`, `)`, `\`, and `NUL` as metacharacters;
|
|
1600
|
+
an unescaped value lets an attacker widen the filter (`*)(uid=*))(|(uid=*`) to bypass an intended
|
|
1601
|
+
restriction or enumerate unrelated records (CWE-90, LDAP injection). Escape the value with `ldap.filter.escape_filter_chars()`
|
|
1602
|
+
before interpolating it into the filter string.
|
|
1603
|
+
severity: ERROR
|
|
1604
|
+
languages:
|
|
1605
|
+
- python
|
|
1606
|
+
metadata:
|
|
1607
|
+
cwe:
|
|
1608
|
+
- 'CWE-90: Improper Neutralization of Special Elements used in an LDAP Query (''LDAP Injection'')'
|
|
1609
|
+
owasp:
|
|
1610
|
+
- A03:2021 - Injection
|
|
1611
|
+
category: security
|
|
1612
|
+
subcategory:
|
|
1613
|
+
- vuln
|
|
1614
|
+
technology:
|
|
1615
|
+
- python-ldap
|
|
1616
|
+
- ldap3
|
|
1617
|
+
likelihood: MEDIUM
|
|
1618
|
+
impact: HIGH
|
|
1619
|
+
confidence: MEDIUM
|
|
1620
|
+
vulnerability_class:
|
|
1621
|
+
- Improper Validation
|
|
1622
|
+
patterns:
|
|
1623
|
+
- pattern-either:
|
|
1624
|
+
- pattern: $CONN.search_s($BASE, $SCOPE, f"...{$VAR}...")
|
|
1625
|
+
- pattern: $CONN.search_s($BASE, $SCOPE, "..." + $VAR)
|
|
1626
|
+
- pattern: $CONN.search_s($BASE, $SCOPE, "...".format($VAR))
|
|
1627
|
+
- pattern: $CONN.search_ext_s($BASE, $SCOPE, f"...{$VAR}...")
|
|
1628
|
+
- pattern: $CONN.search($BASE, f"...{$VAR}...")
|
|
1629
|
+
- pattern-not: $CONN.search_s($BASE, $SCOPE, "...")
|
|
1630
|
+
- pattern-not-inside: '$ESCAPED = ldap.filter.escape_filter_chars($VAR)
|
|
1631
|
+
|
|
1632
|
+
...
|
|
1633
|
+
|
|
1634
|
+
'
|
|
1635
|
+
- id: narvy.python.mass_assignment.django-model-create-kwargs-from-request.django-model-create-kwargs-from-request
|
|
1636
|
+
message: A Django model is created with `**request.$KIND` unpacked directly as keyword arguments. Every
|
|
1637
|
+
key present in the request dict is assigned to a model field with no allowlist, so a client can set
|
|
1638
|
+
fields the view never intended to expose - privilege flags (`is_staff`, `is_superuser`), foreign keys,
|
|
1639
|
+
or price/quantity fields owned by server-side logic (CWE-915, mass assignment). Assign each expected
|
|
1640
|
+
field explicitly, or validate through a form/ serializer that declares its own field list.
|
|
1641
|
+
severity: WARNING
|
|
1642
|
+
languages:
|
|
1643
|
+
- python
|
|
1644
|
+
metadata:
|
|
1645
|
+
cwe:
|
|
1646
|
+
- 'CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes'
|
|
1647
|
+
owasp:
|
|
1648
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
1649
|
+
category: security
|
|
1650
|
+
subcategory:
|
|
1651
|
+
- vuln
|
|
1652
|
+
technology:
|
|
1653
|
+
- python
|
|
1654
|
+
- django
|
|
1655
|
+
likelihood: LOW
|
|
1656
|
+
impact: HIGH
|
|
1657
|
+
confidence: MEDIUM
|
|
1658
|
+
vulnerability_class:
|
|
1659
|
+
- Improper Access Control
|
|
1660
|
+
patterns:
|
|
1661
|
+
- pattern-either:
|
|
1662
|
+
- pattern: $MODEL.objects.create(**request.$KIND)
|
|
1663
|
+
- pattern: $MODEL.objects.create(**request.$KIND.dict())
|
|
1664
|
+
- metavariable-regex:
|
|
1665
|
+
metavariable: $KIND
|
|
1666
|
+
regex: ^(GET|POST|data|query_params)$
|
|
1667
|
+
- id: narvy.python.mass_assignment.django-orm-bulk-mutation-kwargs-from-request.django-orm-bulk-mutation-kwargs-from-request
|
|
1668
|
+
message: A Django ORM call mutates a model with `**request.$KIND` unpacked directly as keyword arguments
|
|
1669
|
+
- an instance/model-utils `.update()`, a `QuerySet.filter(...).update()` bulk update, or a `get_or_create()`/`update_or_create()`
|
|
1670
|
+
convenience constructor. Every key present in the request dict overwrites the matching field with
|
|
1671
|
+
no allowlist - a client can set fields the view never intended to expose (CWE-915, mass assignment).
|
|
1672
|
+
Assign each expected field explicitly instead of unpacking the request dict.
|
|
1673
|
+
severity: WARNING
|
|
1674
|
+
languages:
|
|
1675
|
+
- python
|
|
1676
|
+
metadata:
|
|
1677
|
+
cwe:
|
|
1678
|
+
- 'CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes'
|
|
1679
|
+
owasp:
|
|
1680
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
1681
|
+
category: security
|
|
1682
|
+
subcategory:
|
|
1683
|
+
- vuln
|
|
1684
|
+
technology:
|
|
1685
|
+
- python
|
|
1686
|
+
- django
|
|
1687
|
+
likelihood: LOW
|
|
1688
|
+
impact: HIGH
|
|
1689
|
+
confidence: MEDIUM
|
|
1690
|
+
vulnerability_class:
|
|
1691
|
+
- Improper Access Control
|
|
1692
|
+
patterns:
|
|
1693
|
+
- pattern-either:
|
|
1694
|
+
- pattern: $TARGET.update(**request.$KIND)
|
|
1695
|
+
- pattern: $MODEL.objects.get_or_create(**request.$KIND)
|
|
1696
|
+
- pattern: $MODEL.objects.update_or_create(**request.$KIND)
|
|
1697
|
+
- metavariable-regex:
|
|
1698
|
+
metavariable: $KIND
|
|
1699
|
+
regex: ^(GET|POST|data|query_params)$
|
|
1700
|
+
- id: narvy.python.misc.drf-serializer-fields-all-sensitive-model.drf-serializer-fields-all-sensitive-model
|
|
1701
|
+
message: A Django REST Framework `ModelSerializer` sets `fields = "__all__"` inside a class whose name
|
|
1702
|
+
suggests it wraps a security-sensitive model (User/Account/Admin/Permission/Role). Combined with a
|
|
1703
|
+
writable serializer, this lets a client set every model field - including ones like `is_staff`, `is_admin`,
|
|
1704
|
+
or `role` - directly from request JSON (mass assignment, CWE-915). Enumerate fields explicitly and
|
|
1705
|
+
add privileged fields to `read_only_fields`.
|
|
1706
|
+
severity: WARNING
|
|
1707
|
+
languages:
|
|
1708
|
+
- python
|
|
1709
|
+
metadata:
|
|
1710
|
+
cwe:
|
|
1711
|
+
- 'CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes'
|
|
1712
|
+
owasp:
|
|
1713
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
1714
|
+
category: security
|
|
1715
|
+
subcategory:
|
|
1716
|
+
- audit
|
|
1717
|
+
technology:
|
|
1718
|
+
- python
|
|
1719
|
+
- django
|
|
1720
|
+
- djangorestframework
|
|
1721
|
+
likelihood: LOW
|
|
1722
|
+
impact: HIGH
|
|
1723
|
+
confidence: LOW
|
|
1724
|
+
vulnerability_class:
|
|
1725
|
+
- Improper Access Control
|
|
1726
|
+
patterns:
|
|
1727
|
+
- pattern-either:
|
|
1728
|
+
- pattern-inside: "class $SER(serializers.ModelSerializer):\n ...\n class Meta:\n ...\n"
|
|
1729
|
+
- pattern-inside: "class $SER(serializers.HyperlinkedModelSerializer):\n ...\n class Meta:\n\
|
|
1730
|
+
\ ...\n"
|
|
1731
|
+
- pattern-inside: "class $SER(ModelSerializer):\n ...\n class Meta:\n ...\n"
|
|
1732
|
+
- pattern-inside: "class $SER(HyperlinkedModelSerializer):\n ...\n class Meta:\n ...\n"
|
|
1733
|
+
- pattern: fields = "__all__"
|
|
1734
|
+
- pattern-not-inside: "class $SER(...):\n ...\n class Meta:\n ...\n read_only_fields\
|
|
1735
|
+
\ = (...)\n"
|
|
1736
|
+
- id: narvy.python.misc.insecure-file-permission.insecure-file-permission
|
|
1737
|
+
message: '`os.chmod()` / `os.open()` sets world-writable (and often world-readable) permissions (0o777
|
|
1738
|
+
/ 0o666). Any local user or process can then modify or read this file, which is dangerous for config
|
|
1739
|
+
files, key material, or scripts that get executed later (CWE-732). Use the narrowest mode that works,
|
|
1740
|
+
e.g. 0o600 for secrets or 0o644 for regular files.'
|
|
1741
|
+
severity: WARNING
|
|
1742
|
+
languages:
|
|
1743
|
+
- python
|
|
1744
|
+
metadata:
|
|
1745
|
+
cwe:
|
|
1746
|
+
- 'CWE-732: Incorrect Permission Assignment for Critical Resource'
|
|
1747
|
+
owasp:
|
|
1748
|
+
- A01:2021 - Broken Access Control
|
|
1749
|
+
category: security
|
|
1750
|
+
subcategory:
|
|
1751
|
+
- audit
|
|
1752
|
+
technology:
|
|
1753
|
+
- python
|
|
1754
|
+
likelihood: LOW
|
|
1755
|
+
impact: MEDIUM
|
|
1756
|
+
confidence: HIGH
|
|
1757
|
+
vulnerability_class:
|
|
1758
|
+
- Improper Access Control
|
|
1759
|
+
patterns:
|
|
1760
|
+
- pattern-either:
|
|
1761
|
+
- pattern: os.chmod($PATH, 0o777)
|
|
1762
|
+
- pattern: os.chmod($PATH, 0o666)
|
|
1763
|
+
- pattern: os.chmod($PATH, 0777)
|
|
1764
|
+
- pattern: os.chmod($PATH, 0666)
|
|
1765
|
+
- pattern: os.chmod($PATH, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
|
1766
|
+
- pattern: os.open($PATH, $FLAGS, 0o777)
|
|
1767
|
+
- pattern: os.open($PATH, $FLAGS, 0o666)
|
|
1768
|
+
- id: narvy.python.misc.insecure-tempfile-mktemp.insecure-tempfile-mktemp
|
|
1769
|
+
message: '`tempfile.mktemp()` returns a filename but does not create the file. Between the name being
|
|
1770
|
+
returned and the caller opening it, another local process can create a file (or symlink) at that path,
|
|
1771
|
+
leading to a TOCTOU race that can be used for data tampering or symlink attacks (CWE-377). The Python
|
|
1772
|
+
docs themselves deprecate this function for exactly this reason. Use `tempfile.NamedTemporaryFile()`
|
|
1773
|
+
or `tempfile.mkstemp()`, which create the file atomically.'
|
|
1774
|
+
severity: WARNING
|
|
1775
|
+
languages:
|
|
1776
|
+
- python
|
|
1777
|
+
metadata:
|
|
1778
|
+
cwe:
|
|
1779
|
+
- 'CWE-377: Insecure Temporary File'
|
|
1780
|
+
owasp:
|
|
1781
|
+
- A01:2021 - Broken Access Control
|
|
1782
|
+
category: security
|
|
1783
|
+
subcategory:
|
|
1784
|
+
- vuln
|
|
1785
|
+
technology:
|
|
1786
|
+
- python
|
|
1787
|
+
likelihood: LOW
|
|
1788
|
+
impact: MEDIUM
|
|
1789
|
+
confidence: HIGH
|
|
1790
|
+
vulnerability_class:
|
|
1791
|
+
- Insecure Temporary File
|
|
1792
|
+
patterns:
|
|
1793
|
+
- pattern: tempfile.mktemp(...)
|
|
1794
|
+
- id: narvy.python.misc.redos-dynamic-regex-pattern.redos-dynamic-regex-pattern
|
|
1795
|
+
message: A regular-expression pattern is compiled/matched directly from request-derived data (`request.args`/`request.form`/`request.GET`/
|
|
1796
|
+
`request.POST`). An attacker who controls the pattern can supply a catastrophically-backtracking regex,
|
|
1797
|
+
causing a denial of service when Python's backtracking `re` engine evaluates it (ReDoS, CWE-1333).
|
|
1798
|
+
Validate/allow-list the pattern, cap its length, or run matching in a subprocess with a hard timeout.
|
|
1799
|
+
severity: WARNING
|
|
1800
|
+
languages:
|
|
1801
|
+
- python
|
|
1802
|
+
metadata:
|
|
1803
|
+
cwe:
|
|
1804
|
+
- 'CWE-1333: Inefficient Regular Expression Complexity'
|
|
1805
|
+
- 'CWE-400: Uncontrolled Resource Consumption'
|
|
1806
|
+
owasp:
|
|
1807
|
+
- A05:2021 - Security Misconfiguration
|
|
1808
|
+
category: security
|
|
1809
|
+
subcategory:
|
|
1810
|
+
- audit
|
|
1811
|
+
technology:
|
|
1812
|
+
- python
|
|
1813
|
+
- flask
|
|
1814
|
+
- django
|
|
1815
|
+
likelihood: LOW
|
|
1816
|
+
impact: MEDIUM
|
|
1817
|
+
confidence: MEDIUM
|
|
1818
|
+
vulnerability_class:
|
|
1819
|
+
- Denial-of-Service (DoS)
|
|
1820
|
+
patterns:
|
|
1821
|
+
- pattern-either:
|
|
1822
|
+
- pattern: re.compile(request.$KIND.$ATTR(...))
|
|
1823
|
+
- pattern: re.compile(request.$KIND[$X])
|
|
1824
|
+
- pattern: re.match(request.$KIND.$ATTR(...), ...)
|
|
1825
|
+
- pattern: re.search(request.$KIND.$ATTR(...), ...)
|
|
1826
|
+
- metavariable-regex:
|
|
1827
|
+
metavariable: $KIND
|
|
1828
|
+
regex: ^(args|form|values|GET|POST|json)$
|
|
1829
|
+
- id: narvy.python.nosqli.mongoengine-raw-query-tainted.mongoengine-raw-query-tainted
|
|
1830
|
+
message: '`mongoengine`''s `.objects(__raw__=...)` escape hatch is populated with a dict built from
|
|
1831
|
+
request data. `__raw__` bypasses mongoengine''s field validation/coercion entirely and is passed to
|
|
1832
|
+
MongoDB near-verbatim, so the same operator-injection risk applies (CWE-943). Prefer mongoengine''s
|
|
1833
|
+
typed field lookups (`Model.objects(field=value)`) which validate and coerce input.'
|
|
1834
|
+
severity: WARNING
|
|
1835
|
+
languages:
|
|
1836
|
+
- python
|
|
1837
|
+
metadata:
|
|
1838
|
+
cwe:
|
|
1839
|
+
- 'CWE-943: Improper Neutralization of Special Elements in Data Query Logic'
|
|
1840
|
+
owasp:
|
|
1841
|
+
- A03:2021 - Injection
|
|
1842
|
+
category: security
|
|
1843
|
+
subcategory:
|
|
1844
|
+
- vuln
|
|
1845
|
+
technology:
|
|
1846
|
+
- mongoengine
|
|
1847
|
+
- mongodb
|
|
1848
|
+
likelihood: MEDIUM
|
|
1849
|
+
impact: MEDIUM
|
|
1850
|
+
confidence: MEDIUM
|
|
1851
|
+
vulnerability_class:
|
|
1852
|
+
- SQL Injection
|
|
1853
|
+
patterns:
|
|
1854
|
+
- pattern-either:
|
|
1855
|
+
- pattern: $MODEL.objects(__raw__=request.get_json())
|
|
1856
|
+
- pattern: $MODEL.objects(__raw__=request.json)
|
|
1857
|
+
- id: narvy.python.nosqli.pymongo-filter-from-raw-request-json.pymongo-filter-from-raw-request-json
|
|
1858
|
+
message: 'A pymongo `find`/`find_one`/`update_one`/`delete_one` filter dict is built directly from `request.json`/`request.get_json()`
|
|
1859
|
+
without restricting it to expected scalar fields. MongoDB interprets keys starting with `$` inside
|
|
1860
|
+
a value position as query operators, so a client sending `{"password": {"$ne": null}}` instead of
|
|
1861
|
+
a string turns an equality check into "password is not null" - a classic NoSQL authentication-bypass
|
|
1862
|
+
primitive (CWE-943). Build the filter explicitly from named, type-checked fields instead of passing
|
|
1863
|
+
the request body through.'
|
|
1864
|
+
severity: ERROR
|
|
1865
|
+
languages:
|
|
1866
|
+
- python
|
|
1867
|
+
metadata:
|
|
1868
|
+
cwe:
|
|
1869
|
+
- 'CWE-943: Improper Neutralization of Special Elements in Data Query Logic'
|
|
1870
|
+
owasp:
|
|
1871
|
+
- A03:2021 - Injection
|
|
1872
|
+
category: security
|
|
1873
|
+
subcategory:
|
|
1874
|
+
- vuln
|
|
1875
|
+
technology:
|
|
1876
|
+
- pymongo
|
|
1877
|
+
- mongodb
|
|
1878
|
+
- flask
|
|
1879
|
+
likelihood: HIGH
|
|
1880
|
+
impact: HIGH
|
|
1881
|
+
confidence: MEDIUM
|
|
1882
|
+
vulnerability_class:
|
|
1883
|
+
- SQL Injection
|
|
1884
|
+
patterns:
|
|
1885
|
+
- pattern-either:
|
|
1886
|
+
- pattern: $COLL.find(request.get_json())
|
|
1887
|
+
- pattern: $COLL.find_one(request.get_json())
|
|
1888
|
+
- pattern: $COLL.find(request.json)
|
|
1889
|
+
- pattern: $COLL.find_one(request.json)
|
|
1890
|
+
- pattern: $COLL.find(request.json[$X])
|
|
1891
|
+
- pattern: $COLL.find_one(request.json[$X])
|
|
1892
|
+
- pattern: $COLL.update_one(request.get_json(), ...)
|
|
1893
|
+
- pattern: $COLL.delete_one(request.get_json())
|
|
1894
|
+
- metavariable-regex:
|
|
1895
|
+
metavariable: $COLL
|
|
1896
|
+
regex: (?i)(collection|coll|db\.\w+)
|
|
1897
|
+
- id: narvy.python.nosqli.pymongo-filter-value-from-request-arg.pymongo-filter-value-from-request-arg
|
|
1898
|
+
message: A pymongo query filter's *value* (not just the whole filter dict) is set directly to a raw
|
|
1899
|
+
request field (`request.args.get("id")`/`request.form["x"]`) without type coercion. If the client
|
|
1900
|
+
passes a JSON body instead of a query string, or the framework parses the value into a dict/list rather
|
|
1901
|
+
than a plain string, this reopens the same MongoDB operator-injection bypass (`$ne`, `$gt`, `$regex`)
|
|
1902
|
+
as a full raw filter would (CWE-943). Coerce the expected type explicitly (`str(...)`, `ObjectId(...)`)
|
|
1903
|
+
before using it in a filter.
|
|
1904
|
+
severity: WARNING
|
|
1905
|
+
languages:
|
|
1906
|
+
- python
|
|
1907
|
+
metadata:
|
|
1908
|
+
cwe:
|
|
1909
|
+
- 'CWE-943: Improper Neutralization of Special Elements in Data Query Logic'
|
|
1910
|
+
owasp:
|
|
1911
|
+
- A03:2021 - Injection
|
|
1912
|
+
category: security
|
|
1913
|
+
subcategory:
|
|
1914
|
+
- vuln
|
|
1915
|
+
technology:
|
|
1916
|
+
- pymongo
|
|
1917
|
+
- mongodb
|
|
1918
|
+
- flask
|
|
1919
|
+
likelihood: MEDIUM
|
|
1920
|
+
impact: MEDIUM
|
|
1921
|
+
confidence: LOW
|
|
1922
|
+
vulnerability_class:
|
|
1923
|
+
- SQL Injection
|
|
1924
|
+
patterns:
|
|
1925
|
+
- pattern-either:
|
|
1926
|
+
- pattern: '{$KEY: request.args.get($X)}'
|
|
1927
|
+
- pattern: '{$KEY: request.json.get($X)}'
|
|
1928
|
+
- pattern: '{$KEY: request.json[$X]}'
|
|
1929
|
+
- pattern-not: '{$KEY: str(request.args.get($X))}'
|
|
1930
|
+
- pattern-not: '{$KEY: int(request.args.get($X))}'
|
|
1931
|
+
- metavariable-regex:
|
|
1932
|
+
metavariable: $KEY
|
|
1933
|
+
regex: (?i)("|')(username|user|email|password|role|admin|id|_id)("|')
|
|
1934
|
+
- id: narvy.python.open-redirect.django-httpresponseredirect-tainted.django-httpresponseredirect-tainted
|
|
1935
|
+
message: Django `HttpResponseRedirect()`/`redirect()` target comes directly from request data with no
|
|
1936
|
+
host/path validation. Same phishing pivot as the Flask case (CWE-601) - an attacker-controlled `next`
|
|
1937
|
+
parameter sends an authenticated user off-site. Use `django.utils.http.url_has_allowed_host_and_scheme()`
|
|
1938
|
+
to validate before redirecting.
|
|
1939
|
+
severity: WARNING
|
|
1940
|
+
languages:
|
|
1941
|
+
- python
|
|
1942
|
+
metadata:
|
|
1943
|
+
cwe:
|
|
1944
|
+
- 'CWE-601: URL Redirection to Untrusted Site (''Open Redirect'')'
|
|
1945
|
+
owasp:
|
|
1946
|
+
- A01:2021 - Broken Access Control
|
|
1947
|
+
category: security
|
|
1948
|
+
subcategory:
|
|
1949
|
+
- vuln
|
|
1950
|
+
technology:
|
|
1951
|
+
- django
|
|
1952
|
+
likelihood: MEDIUM
|
|
1953
|
+
impact: MEDIUM
|
|
1954
|
+
confidence: MEDIUM
|
|
1955
|
+
vulnerability_class:
|
|
1956
|
+
- Open Redirect
|
|
1957
|
+
patterns:
|
|
1958
|
+
- pattern-either:
|
|
1959
|
+
- pattern: HttpResponseRedirect(request.$KIND.$ATTR(...))
|
|
1960
|
+
- pattern: HttpResponseRedirect(request.$KIND[$X])
|
|
1961
|
+
- pattern: redirect(request.$KIND.$ATTR(...))
|
|
1962
|
+
- metavariable-regex:
|
|
1963
|
+
metavariable: $KIND
|
|
1964
|
+
regex: ^(GET|POST)$
|
|
1965
|
+
- id: narvy.python.open-redirect.flask-redirect-tainted.flask-redirect-tainted
|
|
1966
|
+
message: Flask `redirect()` target URL comes directly from request data (`request.args.get("next")`,
|
|
1967
|
+
etc.) with no check that it points somewhere on this site. An attacker sends a link like `/login?next=https://evil.example/phish`
|
|
1968
|
+
and the app helpfully redirects a just-authenticated user straight to the phishing page (CWE-601).
|
|
1969
|
+
Validate the target is a relative path or matches an allowlisted host before redirecting.
|
|
1970
|
+
severity: WARNING
|
|
1971
|
+
languages:
|
|
1972
|
+
- python
|
|
1973
|
+
metadata:
|
|
1974
|
+
cwe:
|
|
1975
|
+
- 'CWE-601: URL Redirection to Untrusted Site (''Open Redirect'')'
|
|
1976
|
+
owasp:
|
|
1977
|
+
- A01:2021 - Broken Access Control
|
|
1978
|
+
category: security
|
|
1979
|
+
subcategory:
|
|
1980
|
+
- vuln
|
|
1981
|
+
technology:
|
|
1982
|
+
- flask
|
|
1983
|
+
likelihood: MEDIUM
|
|
1984
|
+
impact: MEDIUM
|
|
1985
|
+
confidence: MEDIUM
|
|
1986
|
+
vulnerability_class:
|
|
1987
|
+
- Open Redirect
|
|
1988
|
+
patterns:
|
|
1989
|
+
- pattern-either:
|
|
1990
|
+
- pattern: redirect(request.$KIND.$ATTR(...))
|
|
1991
|
+
- pattern: redirect(request.$KIND[$X])
|
|
1992
|
+
- pattern: redirect(request.$KIND.get(...))
|
|
1993
|
+
- pattern: '$URL = request.$KIND.$ATTR(...)
|
|
1994
|
+
|
|
1995
|
+
...
|
|
1996
|
+
|
|
1997
|
+
redirect($URL)
|
|
1998
|
+
|
|
1999
|
+
'
|
|
2000
|
+
- metavariable-regex:
|
|
2001
|
+
metavariable: $KIND
|
|
2002
|
+
regex: ^(args|form|values|GET|POST)$
|
|
2003
|
+
- id: narvy.python.paramiko.connect-without-host-key-check.connect-without-host-key-check
|
|
2004
|
+
message: '`paramiko.client.SSHClient()` is used to connect without ever calling `set_missing_host_key_policy()`
|
|
2005
|
+
on it and without loading system/user known-hosts (`load_system_host_keys()` / `load_host_keys()`).
|
|
2006
|
+
paramiko''s own default policy in this state rejects unknown hosts, but a client built this way frequently
|
|
2007
|
+
gets "fixed" later by someone adding `AutoAddPolicy()` the first time it throws on an unrecognized
|
|
2008
|
+
host, silently reopening the man-in-the-middle exposure this rule''s sibling flags (CWE-322). Load
|
|
2009
|
+
a known_hosts file explicitly and keep the default `RejectPolicy`, or pin expected host keys.'
|
|
2010
|
+
severity: INFO
|
|
2011
|
+
languages:
|
|
2012
|
+
- python
|
|
2013
|
+
metadata:
|
|
2014
|
+
cwe:
|
|
2015
|
+
- 'CWE-322: Key Exchange without Entity Authentication'
|
|
2016
|
+
owasp:
|
|
2017
|
+
- A02:2021 - Cryptographic Failures
|
|
2018
|
+
category: security
|
|
2019
|
+
subcategory:
|
|
2020
|
+
- audit
|
|
2021
|
+
technology:
|
|
2022
|
+
- python
|
|
2023
|
+
- paramiko
|
|
2024
|
+
likelihood: LOW
|
|
2025
|
+
impact: MEDIUM
|
|
2026
|
+
confidence: LOW
|
|
2027
|
+
vulnerability_class:
|
|
2028
|
+
- Improper Authentication
|
|
2029
|
+
patterns:
|
|
2030
|
+
- pattern: '$CLIENT = paramiko.SSHClient()
|
|
2031
|
+
|
|
2032
|
+
...
|
|
2033
|
+
|
|
2034
|
+
$CLIENT.connect(...)
|
|
2035
|
+
|
|
2036
|
+
'
|
|
2037
|
+
- pattern-not-inside: '$CLIENT = paramiko.SSHClient()
|
|
2038
|
+
|
|
2039
|
+
...
|
|
2040
|
+
|
|
2041
|
+
$CLIENT.set_missing_host_key_policy(...)
|
|
2042
|
+
|
|
2043
|
+
...
|
|
2044
|
+
|
|
2045
|
+
'
|
|
2046
|
+
- pattern-not-inside: '$CLIENT = paramiko.SSHClient()
|
|
2047
|
+
|
|
2048
|
+
...
|
|
2049
|
+
|
|
2050
|
+
$CLIENT.load_system_host_keys(...)
|
|
2051
|
+
|
|
2052
|
+
...
|
|
2053
|
+
|
|
2054
|
+
'
|
|
2055
|
+
- pattern-not-inside: '$CLIENT = paramiko.SSHClient()
|
|
2056
|
+
|
|
2057
|
+
...
|
|
2058
|
+
|
|
2059
|
+
$CLIENT.load_host_keys(...)
|
|
2060
|
+
|
|
2061
|
+
...
|
|
2062
|
+
|
|
2063
|
+
'
|
|
2064
|
+
- id: narvy.python.paramiko.implicit-trust-host-key.implicit-trust-host-key
|
|
2065
|
+
message: '`set_missing_host_key_policy()` is called with `AutoAddPolicy` or `WarningPolicy`. Both accept
|
|
2066
|
+
a host key the client has never seen before without any verification - `AutoAddPolicy` saves it silently,
|
|
2067
|
+
`WarningPolicy` only logs. Either way, a machine-in-the-middle presenting its own key instead of the
|
|
2068
|
+
real server''s is accepted transparently, defeating SSH host authentication (CWE-322). Use the default
|
|
2069
|
+
`RejectPolicy`, or pin the expected host key and verify it explicitly with a custom `paramiko.MissingHostKeyPolicy`
|
|
2070
|
+
subclass.'
|
|
2071
|
+
severity: ERROR
|
|
2072
|
+
languages:
|
|
2073
|
+
- python
|
|
2074
|
+
metadata:
|
|
2075
|
+
cwe:
|
|
2076
|
+
- 'CWE-322: Key Exchange without Entity Authentication'
|
|
2077
|
+
owasp:
|
|
2078
|
+
- A02:2021 - Cryptographic Failures
|
|
2079
|
+
category: security
|
|
2080
|
+
subcategory:
|
|
2081
|
+
- vuln
|
|
2082
|
+
technology:
|
|
2083
|
+
- python
|
|
2084
|
+
- paramiko
|
|
2085
|
+
likelihood: MEDIUM
|
|
2086
|
+
impact: HIGH
|
|
2087
|
+
confidence: HIGH
|
|
2088
|
+
vulnerability_class:
|
|
2089
|
+
- Improper Authentication
|
|
2090
|
+
patterns:
|
|
2091
|
+
- pattern-either:
|
|
2092
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
2093
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.AutoAddPolicy)
|
|
2094
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
|
|
2095
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.client.AutoAddPolicy)
|
|
2096
|
+
- pattern: $CLIENT.set_missing_host_key_policy(AutoAddPolicy())
|
|
2097
|
+
- pattern: $CLIENT.set_missing_host_key_policy(AutoAddPolicy)
|
|
2098
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.WarningPolicy())
|
|
2099
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.WarningPolicy)
|
|
2100
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.client.WarningPolicy())
|
|
2101
|
+
- pattern: $CLIENT.set_missing_host_key_policy(paramiko.client.WarningPolicy)
|
|
2102
|
+
- pattern: $CLIENT.set_missing_host_key_policy(WarningPolicy())
|
|
2103
|
+
- pattern: $CLIENT.set_missing_host_key_policy(WarningPolicy)
|
|
2104
|
+
- id: narvy.python.path-traversal.django-fileresponse-tainted.django-fileresponse-tainted
|
|
2105
|
+
message: Django `FileResponse(open(...))` opens a file whose path is built from request data. Same arbitrary-file-read
|
|
2106
|
+
risk as the generic `open()` case (CWE-22), but the response streams the content straight back to
|
|
2107
|
+
the client, so a successful traversal is immediately readable rather than just server-side. Validate
|
|
2108
|
+
the resolved path stays under the intended media root before opening.
|
|
2109
|
+
severity: ERROR
|
|
2110
|
+
languages:
|
|
2111
|
+
- python
|
|
2112
|
+
metadata:
|
|
2113
|
+
cwe:
|
|
2114
|
+
- 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
|
|
2115
|
+
owasp:
|
|
2116
|
+
- A01:2021 - Broken Access Control
|
|
2117
|
+
category: security
|
|
2118
|
+
subcategory:
|
|
2119
|
+
- vuln
|
|
2120
|
+
technology:
|
|
2121
|
+
- django
|
|
2122
|
+
likelihood: HIGH
|
|
2123
|
+
impact: HIGH
|
|
2124
|
+
confidence: MEDIUM
|
|
2125
|
+
vulnerability_class:
|
|
2126
|
+
- Path Traversal
|
|
2127
|
+
patterns:
|
|
2128
|
+
- pattern-either:
|
|
2129
|
+
- pattern: FileResponse(open(request.$KIND.$ATTR(...), ...))
|
|
2130
|
+
- pattern: FileResponse(open(request.$KIND[$X], ...))
|
|
2131
|
+
- metavariable-regex:
|
|
2132
|
+
metavariable: $KIND
|
|
2133
|
+
regex: ^(GET|POST)$
|
|
2134
|
+
- id: narvy.python.path-traversal.flask-send-file-tainted.flask-send-file-tainted
|
|
2135
|
+
message: '`send_file()`/`send_from_directory()` is called with a path/filename argument built directly
|
|
2136
|
+
from request data without validation. `send_file()` does not sanitize `../` sequences on the caller''s
|
|
2137
|
+
behalf when the path is fully attacker-supplied (only `send_from_directory`''s `filename` argument
|
|
2138
|
+
is checked, and only if you actually use that API correctly). Arbitrary file read (CWE-22). Validate
|
|
2139
|
+
the filename against an allowlist or strip path separators before use.'
|
|
2140
|
+
severity: ERROR
|
|
2141
|
+
languages:
|
|
2142
|
+
- python
|
|
2143
|
+
metadata:
|
|
2144
|
+
cwe:
|
|
2145
|
+
- 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
|
|
2146
|
+
owasp:
|
|
2147
|
+
- A01:2021 - Broken Access Control
|
|
2148
|
+
category: security
|
|
2149
|
+
subcategory:
|
|
2150
|
+
- vuln
|
|
2151
|
+
technology:
|
|
2152
|
+
- flask
|
|
2153
|
+
likelihood: HIGH
|
|
2154
|
+
impact: HIGH
|
|
2155
|
+
confidence: MEDIUM
|
|
2156
|
+
vulnerability_class:
|
|
2157
|
+
- Path Traversal
|
|
2158
|
+
patterns:
|
|
2159
|
+
- pattern-either:
|
|
2160
|
+
- pattern: send_file(request.$KIND.$ATTR(...))
|
|
2161
|
+
- pattern: send_file(request.$KIND[$X])
|
|
2162
|
+
- pattern: send_file(f"...{request.$KIND.$ATTR(...)}...")
|
|
2163
|
+
- metavariable-regex:
|
|
2164
|
+
metavariable: $KIND
|
|
2165
|
+
regex: ^(args|form|values|GET|POST|json)$
|
|
2166
|
+
- id: narvy.python.path-traversal.open-tainted-path.open-tainted-path
|
|
2167
|
+
message: '`open()` is called with a filesystem path built directly from request data. Without normalizing
|
|
2168
|
+
and validating the path stays inside an intended base directory, an attacker can supply `../../etc/passwd`-style
|
|
2169
|
+
segments to read (or, on a write-mode open, overwrite) arbitrary files reachable by the process (CWE-22).
|
|
2170
|
+
Resolve the path with `os.path.realpath()`, then verify it is a descendant of the intended base directory
|
|
2171
|
+
before opening.'
|
|
2172
|
+
severity: ERROR
|
|
2173
|
+
languages:
|
|
2174
|
+
- python
|
|
2175
|
+
metadata:
|
|
2176
|
+
cwe:
|
|
2177
|
+
- 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
|
|
2178
|
+
owasp:
|
|
2179
|
+
- A01:2021 - Broken Access Control
|
|
2180
|
+
category: security
|
|
2181
|
+
subcategory:
|
|
2182
|
+
- vuln
|
|
2183
|
+
technology:
|
|
2184
|
+
- python
|
|
2185
|
+
- flask
|
|
2186
|
+
- django
|
|
2187
|
+
likelihood: HIGH
|
|
2188
|
+
impact: HIGH
|
|
2189
|
+
confidence: MEDIUM
|
|
2190
|
+
vulnerability_class:
|
|
2191
|
+
- Path Traversal
|
|
2192
|
+
patterns:
|
|
2193
|
+
- pattern-either:
|
|
2194
|
+
- pattern: open(request.$KIND.$ATTR(...), ...)
|
|
2195
|
+
- pattern: open(request.$KIND[$X], ...)
|
|
2196
|
+
- pattern: open(f"...{request.$KIND.$ATTR(...)}...", ...)
|
|
2197
|
+
- pattern: open(os.path.join($BASE, request.$KIND.$ATTR(...)), ...)
|
|
2198
|
+
- pattern: '$P = request.$KIND.$ATTR(...)
|
|
2199
|
+
|
|
2200
|
+
...
|
|
2201
|
+
|
|
2202
|
+
open($P, ...)
|
|
2203
|
+
|
|
2204
|
+
'
|
|
2205
|
+
- metavariable-regex:
|
|
2206
|
+
metavariable: $KIND
|
|
2207
|
+
regex: ^(args|form|values|GET|POST|json)$
|
|
2208
|
+
- id: narvy.python.path-traversal.zipfile-extractall-untrusted.zipfile-extractall-untrusted-archive
|
|
2209
|
+
message: '`ZipFile.extractall()` is called on an archive that came from an upload/request/network fetch,
|
|
2210
|
+
with no per-member path check first. A malicious zip can contain entries with `../` path segments
|
|
2211
|
+
or absolute paths ("Zip Slip") that extract outside the target directory, overwriting arbitrary files
|
|
2212
|
+
(CWE-22). Iterate `zip.namelist()` and reject any entry whose resolved path escapes the destination
|
|
2213
|
+
directory before calling `extractall()`.'
|
|
2214
|
+
severity: ERROR
|
|
2215
|
+
languages:
|
|
2216
|
+
- python
|
|
2217
|
+
metadata:
|
|
2218
|
+
cwe:
|
|
2219
|
+
- 'CWE-22: Improper Limitation of a Pathname to a Restricted Directory (''Path Traversal'')'
|
|
2220
|
+
owasp:
|
|
2221
|
+
- A01:2021 - Broken Access Control
|
|
2222
|
+
category: security
|
|
2223
|
+
subcategory:
|
|
2224
|
+
- vuln
|
|
2225
|
+
technology:
|
|
2226
|
+
- python
|
|
2227
|
+
likelihood: MEDIUM
|
|
2228
|
+
impact: HIGH
|
|
2229
|
+
confidence: LOW
|
|
2230
|
+
vulnerability_class:
|
|
2231
|
+
- Path Traversal
|
|
2232
|
+
patterns:
|
|
2233
|
+
- pattern: $ZIP.extractall(...)
|
|
2234
|
+
- pattern-inside: '$ZIP = zipfile.ZipFile($SRC, ...)
|
|
2235
|
+
|
|
2236
|
+
...
|
|
2237
|
+
|
|
2238
|
+
'
|
|
2239
|
+
- metavariable-regex:
|
|
2240
|
+
metavariable: $SRC
|
|
2241
|
+
regex: (?i)(request|upload|response|download)
|
|
2242
|
+
- id: narvy.python.pyramid.csrf-disabled-globally.csrf-disabled-globally
|
|
2243
|
+
message: Pyramid's default CSRF check is turned off application-wide via `set_default_csrf_options(require_csrf=False)`.
|
|
2244
|
+
Every unsafe-method view (POST/PUT/PATCH/DELETE) in the app loses its automatic CSRF token check unless
|
|
2245
|
+
it re-enables it individually. Pass `require_csrf=True` (the framework default), or drop the kwarg
|
|
2246
|
+
entirely and only opt specific views out. CWE-352.
|
|
2247
|
+
severity: ERROR
|
|
2248
|
+
languages:
|
|
2249
|
+
- python
|
|
2250
|
+
metadata:
|
|
2251
|
+
cwe:
|
|
2252
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
2253
|
+
owasp:
|
|
2254
|
+
- A01:2021 - Broken Access Control
|
|
2255
|
+
category: security
|
|
2256
|
+
subcategory:
|
|
2257
|
+
- vuln
|
|
2258
|
+
technology:
|
|
2259
|
+
- pyramid
|
|
2260
|
+
likelihood: MEDIUM
|
|
2261
|
+
impact: HIGH
|
|
2262
|
+
confidence: HIGH
|
|
2263
|
+
vulnerability_class:
|
|
2264
|
+
- Cross-Site Request Forgery (CSRF)
|
|
2265
|
+
patterns:
|
|
2266
|
+
- pattern-either:
|
|
2267
|
+
- pattern: $CONFIGURATOR.set_default_csrf_options(..., require_csrf=False, ...)
|
|
2268
|
+
- pattern: config.set_default_csrf_options(..., require_csrf=False, ...)
|
|
2269
|
+
- id: narvy.python.pyramid.csrf-disabled-per-view.csrf-disabled-per-view
|
|
2270
|
+
message: This view opts out of Pyramid's automatic CSRF check via `require_csrf=False` on `@view_config`/`add_view`.
|
|
2271
|
+
If the view accepts an unsafe HTTP method (POST/PUT/PATCH/DELETE) and changes state, this reopens
|
|
2272
|
+
it to cross-site request forgery even if CSRF protection is enabled globally elsewhere. Confirm this
|
|
2273
|
+
view is either read-only or protected by an equivalent check (API token, custom header requirement)
|
|
2274
|
+
before disabling it. CWE-352.
|
|
2275
|
+
severity: WARNING
|
|
2276
|
+
languages:
|
|
2277
|
+
- python
|
|
2278
|
+
metadata:
|
|
2279
|
+
cwe:
|
|
2280
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
2281
|
+
owasp:
|
|
2282
|
+
- A01:2021 - Broken Access Control
|
|
2283
|
+
category: security
|
|
2284
|
+
subcategory:
|
|
2285
|
+
- vuln
|
|
2286
|
+
technology:
|
|
2287
|
+
- pyramid
|
|
2288
|
+
likelihood: LOW
|
|
2289
|
+
impact: HIGH
|
|
2290
|
+
confidence: MEDIUM
|
|
2291
|
+
vulnerability_class:
|
|
2292
|
+
- Cross-Site Request Forgery (CSRF)
|
|
2293
|
+
patterns:
|
|
2294
|
+
- pattern-either:
|
|
2295
|
+
- pattern: view_config(..., require_csrf=False, ...)
|
|
2296
|
+
- pattern: pyramid.view.view_config(..., require_csrf=False, ...)
|
|
2297
|
+
- pattern: $CONFIGURATOR.add_view(..., require_csrf=False, ...)
|
|
2298
|
+
- id: narvy.python.pyramid.response-object-echoes-request-data.response-object-echoes-request-data
|
|
2299
|
+
message: A Pyramid `Response` is built (or its body/text attribute set) by directly interpolating request-derived
|
|
2300
|
+
data (`request.GET`/`request.POST`/`request.params`/`request.matchdict`) instead of rendering it through
|
|
2301
|
+
a Chameleon/Jinja2/Mako template, which auto-escapes. `Response` does no HTML escaping of its own,
|
|
2302
|
+
so any HTML/JS in the request value reaches the browser verbatim. Reflected XSS (CWE-79). Render through
|
|
2303
|
+
a template, or escape the value with `markupsafe.escape()`/`html.escape()` before it reaches the response
|
|
2304
|
+
body.
|
|
2305
|
+
severity: ERROR
|
|
2306
|
+
languages:
|
|
2307
|
+
- python
|
|
2308
|
+
metadata:
|
|
2309
|
+
cwe:
|
|
2310
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
2311
|
+
owasp:
|
|
2312
|
+
- A03:2021 - Injection
|
|
2313
|
+
category: security
|
|
2314
|
+
subcategory:
|
|
2315
|
+
- vuln
|
|
2316
|
+
technology:
|
|
2317
|
+
- pyramid
|
|
2318
|
+
likelihood: HIGH
|
|
2319
|
+
impact: MEDIUM
|
|
2320
|
+
confidence: MEDIUM
|
|
2321
|
+
vulnerability_class:
|
|
2322
|
+
- Cross-Site-Scripting (XSS)
|
|
2323
|
+
mode: taint
|
|
2324
|
+
pattern-sources:
|
|
2325
|
+
- patterns:
|
|
2326
|
+
- pattern-either:
|
|
2327
|
+
- pattern: request.GET
|
|
2328
|
+
- pattern: request.GET.get(...)
|
|
2329
|
+
- pattern: request.GET[...]
|
|
2330
|
+
- pattern: request.POST
|
|
2331
|
+
- pattern: request.POST.get(...)
|
|
2332
|
+
- pattern: request.POST[...]
|
|
2333
|
+
- pattern: request.params
|
|
2334
|
+
- pattern: request.params.get(...)
|
|
2335
|
+
- pattern: request.params[...]
|
|
2336
|
+
- pattern: request.matchdict
|
|
2337
|
+
- pattern: request.matchdict.get(...)
|
|
2338
|
+
- pattern: request.matchdict[...]
|
|
2339
|
+
pattern-sinks:
|
|
2340
|
+
- patterns:
|
|
2341
|
+
- pattern-either:
|
|
2342
|
+
- pattern: Response($SINK)
|
|
2343
|
+
- pattern: pyramid.response.Response($SINK)
|
|
2344
|
+
- pattern: $RESP.text = $SINK
|
|
2345
|
+
- pattern: $RESP.body = $SINK
|
|
2346
|
+
- pattern: $RESP.ubody = $SINK
|
|
2347
|
+
- pattern: $RESP.unicode_body = $SINK
|
|
2348
|
+
pattern-sanitizers:
|
|
2349
|
+
- patterns:
|
|
2350
|
+
- pattern: escape(...)
|
|
2351
|
+
- patterns:
|
|
2352
|
+
- pattern: markupsafe.escape(...)
|
|
2353
|
+
- patterns:
|
|
2354
|
+
- pattern: html.escape(...)
|
|
2355
|
+
- id: narvy.python.pyramid.session-cookie-not-httponly.session-cookie-not-httponly
|
|
2356
|
+
message: Pyramid auth-ticket or session cookie constructed without `httponly=True` (either omitted,
|
|
2357
|
+
which is not the secure choice for this kind of cookie, or explicitly set to False). Client-side JavaScript
|
|
2358
|
+
can read this cookie, so any XSS elsewhere in the app can be used to steal the session/auth-ticket
|
|
2359
|
+
value directly. Pass `httponly=True`. CWE-1004.
|
|
2360
|
+
severity: WARNING
|
|
2361
|
+
languages:
|
|
2362
|
+
- python
|
|
2363
|
+
metadata:
|
|
2364
|
+
cwe:
|
|
2365
|
+
- 'CWE-1004: Sensitive Cookie Without ''HttpOnly'' Flag'
|
|
2366
|
+
owasp:
|
|
2367
|
+
- A05:2021 - Security Misconfiguration
|
|
2368
|
+
category: security
|
|
2369
|
+
subcategory:
|
|
2370
|
+
- vuln
|
|
2371
|
+
technology:
|
|
2372
|
+
- pyramid
|
|
2373
|
+
likelihood: LOW
|
|
2374
|
+
impact: MEDIUM
|
|
2375
|
+
confidence: MEDIUM
|
|
2376
|
+
vulnerability_class:
|
|
2377
|
+
- Cookie Security
|
|
2378
|
+
patterns:
|
|
2379
|
+
- pattern-either:
|
|
2380
|
+
- pattern: AuthTktAuthenticationPolicy(..., httponly=False, ...)
|
|
2381
|
+
- pattern: pyramid.authentication.AuthTktAuthenticationPolicy(..., httponly=False, ...)
|
|
2382
|
+
- pattern: SignedCookieSessionFactory(..., httponly=False, ...)
|
|
2383
|
+
- pattern: pyramid.session.SignedCookieSessionFactory(..., httponly=False, ...)
|
|
2384
|
+
- patterns:
|
|
2385
|
+
- pattern-either:
|
|
2386
|
+
- pattern: AuthTktAuthenticationPolicy(...)
|
|
2387
|
+
- pattern: pyramid.authentication.AuthTktAuthenticationPolicy(...)
|
|
2388
|
+
- pattern: SignedCookieSessionFactory(...)
|
|
2389
|
+
- pattern: pyramid.session.SignedCookieSessionFactory(...)
|
|
2390
|
+
- pattern-not: AuthTktAuthenticationPolicy(..., httponly=$H, ...)
|
|
2391
|
+
- pattern-not: pyramid.authentication.AuthTktAuthenticationPolicy(..., httponly=$H, ...)
|
|
2392
|
+
- pattern-not: SignedCookieSessionFactory(..., httponly=$H, ...)
|
|
2393
|
+
- pattern-not: pyramid.session.SignedCookieSessionFactory(..., httponly=$H, ...)
|
|
2394
|
+
- pattern-not: AuthTktAuthenticationPolicy(..., **$KW)
|
|
2395
|
+
- pattern-not: pyramid.authentication.AuthTktAuthenticationPolicy(..., **$KW)
|
|
2396
|
+
- pattern-not: SignedCookieSessionFactory(..., **$KW)
|
|
2397
|
+
- pattern-not: pyramid.session.SignedCookieSessionFactory(..., **$KW)
|
|
2398
|
+
- id: narvy.python.pyramid.session-cookie-not-secure.session-cookie-not-secure
|
|
2399
|
+
message: Pyramid auth-ticket or session cookie constructed without `secure=True` (either omitted or
|
|
2400
|
+
explicitly set to False). Without it, the browser will happily send this cookie over a plaintext HTTP
|
|
2401
|
+
connection, exposing the session/auth-ticket value to anyone on the network path. Pass `secure=True`
|
|
2402
|
+
for any deployment that is reachable over HTTPS (which should be all of them). CWE-614.
|
|
2403
|
+
severity: WARNING
|
|
2404
|
+
languages:
|
|
2405
|
+
- python
|
|
2406
|
+
metadata:
|
|
2407
|
+
cwe:
|
|
2408
|
+
- 'CWE-614: Sensitive Cookie in HTTPS Session Without ''Secure'' Attribute'
|
|
2409
|
+
owasp:
|
|
2410
|
+
- A05:2021 - Security Misconfiguration
|
|
2411
|
+
category: security
|
|
2412
|
+
subcategory:
|
|
2413
|
+
- vuln
|
|
2414
|
+
technology:
|
|
2415
|
+
- pyramid
|
|
2416
|
+
likelihood: LOW
|
|
2417
|
+
impact: MEDIUM
|
|
2418
|
+
confidence: MEDIUM
|
|
2419
|
+
vulnerability_class:
|
|
2420
|
+
- Cookie Security
|
|
2421
|
+
patterns:
|
|
2422
|
+
- pattern-either:
|
|
2423
|
+
- pattern: AuthTktAuthenticationPolicy(..., secure=False, ...)
|
|
2424
|
+
- pattern: pyramid.authentication.AuthTktAuthenticationPolicy(..., secure=False, ...)
|
|
2425
|
+
- pattern: SignedCookieSessionFactory(..., secure=False, ...)
|
|
2426
|
+
- pattern: pyramid.session.SignedCookieSessionFactory(..., secure=False, ...)
|
|
2427
|
+
- patterns:
|
|
2428
|
+
- pattern-either:
|
|
2429
|
+
- pattern: AuthTktAuthenticationPolicy(...)
|
|
2430
|
+
- pattern: pyramid.authentication.AuthTktAuthenticationPolicy(...)
|
|
2431
|
+
- pattern: SignedCookieSessionFactory(...)
|
|
2432
|
+
- pattern: pyramid.session.SignedCookieSessionFactory(...)
|
|
2433
|
+
- pattern-not: AuthTktAuthenticationPolicy(..., secure=$S, ...)
|
|
2434
|
+
- pattern-not: pyramid.authentication.AuthTktAuthenticationPolicy(..., secure=$S, ...)
|
|
2435
|
+
- pattern-not: SignedCookieSessionFactory(..., secure=$S, ...)
|
|
2436
|
+
- pattern-not: pyramid.session.SignedCookieSessionFactory(..., secure=$S, ...)
|
|
2437
|
+
- pattern-not: AuthTktAuthenticationPolicy(..., **$KW)
|
|
2438
|
+
- pattern-not: pyramid.authentication.AuthTktAuthenticationPolicy(..., **$KW)
|
|
2439
|
+
- pattern-not: SignedCookieSessionFactory(..., **$KW)
|
|
2440
|
+
- pattern-not: pyramid.session.SignedCookieSessionFactory(..., **$KW)
|
|
2441
|
+
- id: narvy.python.pyramid.session-cookie-samesite-unset.session-cookie-samesite-unset
|
|
2442
|
+
message: Pyramid auth-ticket or session cookie constructed without a `samesite` policy (omitted, or
|
|
2443
|
+
explicitly `samesite=None`). Without SameSite=Lax/Strict, this cookie is sent on cross-site requests
|
|
2444
|
+
too, which is what makes CSRF against session-authenticated endpoints possible in the first place.
|
|
2445
|
+
Pass `samesite="Lax"` (or `"Strict"` for the most sensitive cookies). CWE-352.
|
|
2446
|
+
severity: WARNING
|
|
2447
|
+
languages:
|
|
2448
|
+
- python
|
|
2449
|
+
metadata:
|
|
2450
|
+
cwe:
|
|
2451
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
2452
|
+
owasp:
|
|
2453
|
+
- A05:2021 - Security Misconfiguration
|
|
2454
|
+
category: security
|
|
2455
|
+
subcategory:
|
|
2456
|
+
- vuln
|
|
2457
|
+
technology:
|
|
2458
|
+
- pyramid
|
|
2459
|
+
likelihood: LOW
|
|
2460
|
+
impact: MEDIUM
|
|
2461
|
+
confidence: MEDIUM
|
|
2462
|
+
vulnerability_class:
|
|
2463
|
+
- Cookie Security
|
|
2464
|
+
- Cross-Site Request Forgery (CSRF)
|
|
2465
|
+
patterns:
|
|
2466
|
+
- pattern-either:
|
|
2467
|
+
- pattern: AuthTktAuthenticationPolicy(..., samesite=None, ...)
|
|
2468
|
+
- pattern: pyramid.authentication.AuthTktAuthenticationPolicy(..., samesite=None, ...)
|
|
2469
|
+
- pattern: SignedCookieSessionFactory(..., samesite=None, ...)
|
|
2470
|
+
- pattern: pyramid.session.SignedCookieSessionFactory(..., samesite=None, ...)
|
|
2471
|
+
- patterns:
|
|
2472
|
+
- pattern-either:
|
|
2473
|
+
- pattern: AuthTktAuthenticationPolicy(...)
|
|
2474
|
+
- pattern: pyramid.authentication.AuthTktAuthenticationPolicy(...)
|
|
2475
|
+
- pattern: SignedCookieSessionFactory(...)
|
|
2476
|
+
- pattern: pyramid.session.SignedCookieSessionFactory(...)
|
|
2477
|
+
- pattern-not: AuthTktAuthenticationPolicy(..., samesite=$SS, ...)
|
|
2478
|
+
- pattern-not: pyramid.authentication.AuthTktAuthenticationPolicy(..., samesite=$SS, ...)
|
|
2479
|
+
- pattern-not: SignedCookieSessionFactory(..., samesite=$SS, ...)
|
|
2480
|
+
- pattern-not: pyramid.session.SignedCookieSessionFactory(..., samesite=$SS, ...)
|
|
2481
|
+
- pattern-not: AuthTktAuthenticationPolicy(..., **$KW)
|
|
2482
|
+
- pattern-not: pyramid.authentication.AuthTktAuthenticationPolicy(..., **$KW)
|
|
2483
|
+
- pattern-not: SignedCookieSessionFactory(..., **$KW)
|
|
2484
|
+
- pattern-not: pyramid.session.SignedCookieSessionFactory(..., **$KW)
|
|
2485
|
+
- id: narvy.python.secrets.hardcoded-credential-assignment.hardcoded-credential-assignment
|
|
2486
|
+
message: A variable named like a credential (password/api_key/secret/token) is assigned a plain string
|
|
2487
|
+
literal rather than being read from an environment variable or secret store. Anyone with source or
|
|
2488
|
+
VCS history access recovers the value (CWE-798). Load it from `os.environ` and fail startup if missing.
|
|
2489
|
+
severity: WARNING
|
|
2490
|
+
languages:
|
|
2491
|
+
- python
|
|
2492
|
+
metadata:
|
|
2493
|
+
cwe:
|
|
2494
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
2495
|
+
owasp:
|
|
2496
|
+
- A07:2021 - Identification and Authentication Failures
|
|
2497
|
+
category: security
|
|
2498
|
+
subcategory:
|
|
2499
|
+
- vuln
|
|
2500
|
+
technology:
|
|
2501
|
+
- python
|
|
2502
|
+
likelihood: MEDIUM
|
|
2503
|
+
impact: HIGH
|
|
2504
|
+
confidence: LOW
|
|
2505
|
+
vulnerability_class:
|
|
2506
|
+
- Hard-coded Secrets
|
|
2507
|
+
paths:
|
|
2508
|
+
exclude:
|
|
2509
|
+
- '**/test*/**'
|
|
2510
|
+
- '**/tests/**'
|
|
2511
|
+
- '**/*test*.py'
|
|
2512
|
+
- '**/*example*.py'
|
|
2513
|
+
- '**/docs_src/**'
|
|
2514
|
+
- '**/examples/**'
|
|
2515
|
+
- '**/conftest.py'
|
|
2516
|
+
- '**/fixtures/**'
|
|
2517
|
+
patterns:
|
|
2518
|
+
- pattern-either:
|
|
2519
|
+
- pattern: $VAR = "$LITERAL"
|
|
2520
|
+
- metavariable-regex:
|
|
2521
|
+
metavariable: $VAR
|
|
2522
|
+
regex: (?i)^([a-z0-9]+_)*(password|passwd|passwsd|pwd|passphrase|secret|secret_key|api_key|apikey|api_secret|access_token|auth_token|refresh_token|session_token|client_secret|private_key|token|credential)s?$
|
|
2523
|
+
- metavariable-regex:
|
|
2524
|
+
metavariable: $LITERAL
|
|
2525
|
+
regex: ^.{8,}$
|
|
2526
|
+
- metavariable-regex:
|
|
2527
|
+
metavariable: $LITERAL
|
|
2528
|
+
regex: ^(?!.*(\{\}|\{\d|\{[a-zA-Z_]|%s|%d|xxxx|XXXX|changeme|CHANGEME|your[-_ ]|placeholder|example|redacted|\*\*\*)).*$
|
|
2529
|
+
- pattern-not: $VAR = os.environ[$X]
|
|
2530
|
+
- pattern-not: $VAR = os.getenv($X)
|
|
2531
|
+
- pattern-not: $VAR = ""
|
|
2532
|
+
- id: narvy.python.secrets.logger-credential-leak.logger-credential-leak
|
|
2533
|
+
message: A logging call includes a variable named like a credential (password/token/secret/api_key)
|
|
2534
|
+
in its message. Logs routinely end up in less-restricted storage than the application itself (log
|
|
2535
|
+
aggregators, ticket attachments, `journalctl`, support bundles); writing a live credential there is
|
|
2536
|
+
an information-disclosure bug (CWE-532). Redact or omit the value before logging.
|
|
2537
|
+
severity: WARNING
|
|
2538
|
+
languages:
|
|
2539
|
+
- python
|
|
2540
|
+
metadata:
|
|
2541
|
+
cwe:
|
|
2542
|
+
- 'CWE-532: Insertion of Sensitive Information into Log File'
|
|
2543
|
+
owasp:
|
|
2544
|
+
- A09:2021 - Security Logging and Monitoring Failures
|
|
2545
|
+
category: security
|
|
2546
|
+
subcategory:
|
|
2547
|
+
- vuln
|
|
2548
|
+
technology:
|
|
2549
|
+
- python
|
|
2550
|
+
likelihood: MEDIUM
|
|
2551
|
+
impact: MEDIUM
|
|
2552
|
+
confidence: LOW
|
|
2553
|
+
vulnerability_class:
|
|
2554
|
+
- Mishandled Sensitive Information
|
|
2555
|
+
patterns:
|
|
2556
|
+
- pattern-either:
|
|
2557
|
+
- pattern: logging.$LEVEL(f"...{$VAR}...")
|
|
2558
|
+
- pattern: logger.$LEVEL(f"...{$VAR}...")
|
|
2559
|
+
- pattern: $LOG.$LEVEL(f"...{$VAR}...")
|
|
2560
|
+
- metavariable-regex:
|
|
2561
|
+
metavariable: $LEVEL
|
|
2562
|
+
regex: ^(debug|info|warning|warn|error|critical|exception)$
|
|
2563
|
+
- metavariable-regex:
|
|
2564
|
+
metavariable: $VAR
|
|
2565
|
+
regex: (?i)^(password|passwd|pwd|token|secret|api_key|apikey|access_token|auth_token|credential)s?$
|
|
2566
|
+
- id: narvy.python.session.django-csrf-exempt-view.django-csrf-exempt-view
|
|
2567
|
+
message: View is decorated with `@csrf_exempt`, opting it out of Django's CSRF protection entirely.
|
|
2568
|
+
If the view performs a state-changing action (POST/PUT/DELETE that writes data) and relies on cookie-based
|
|
2569
|
+
session auth, it is now forgeable from any other site the victim has open (CWE-352). Only use `@csrf_exempt`
|
|
2570
|
+
for endpoints authenticated by something other than cookies (e.g. a signed API token checked in the
|
|
2571
|
+
view), and say so in a comment.
|
|
2572
|
+
severity: WARNING
|
|
2573
|
+
languages:
|
|
2574
|
+
- python
|
|
2575
|
+
metadata:
|
|
2576
|
+
cwe:
|
|
2577
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
2578
|
+
owasp:
|
|
2579
|
+
- A01:2021 - Broken Access Control
|
|
2580
|
+
category: security
|
|
2581
|
+
subcategory:
|
|
2582
|
+
- vuln
|
|
2583
|
+
technology:
|
|
2584
|
+
- django
|
|
2585
|
+
likelihood: MEDIUM
|
|
2586
|
+
impact: MEDIUM
|
|
2587
|
+
confidence: LOW
|
|
2588
|
+
vulnerability_class:
|
|
2589
|
+
- Cross-Site Request Forgery (CSRF)
|
|
2590
|
+
patterns:
|
|
2591
|
+
- pattern: "@csrf_exempt\ndef $VIEW(...):\n ...\n"
|
|
2592
|
+
- id: narvy.python.session.django-debug-true-in-settings.django-debug-true-in-settings
|
|
2593
|
+
message: '`DEBUG = True` in a Django settings file. In debug mode Django shows full stack traces with
|
|
2594
|
+
local variables, settings values (including secrets, unless explicitly redacted), and SQL queries
|
|
2595
|
+
to any visitor who triggers a 500 - a serious information disclosure if this file is ever the one
|
|
2596
|
+
loaded in production (CWE-489, CWE-215). Default to `DEBUG = False` and flip it on only via an environment
|
|
2597
|
+
variable in local dev.'
|
|
2598
|
+
severity: WARNING
|
|
2599
|
+
languages:
|
|
2600
|
+
- python
|
|
2601
|
+
metadata:
|
|
2602
|
+
cwe:
|
|
2603
|
+
- 'CWE-489: Active Debug Code'
|
|
2604
|
+
- 'CWE-215: Insertion of Sensitive Information Into Debugging Code'
|
|
2605
|
+
owasp:
|
|
2606
|
+
- A05:2021 - Security Misconfiguration
|
|
2607
|
+
category: security
|
|
2608
|
+
subcategory:
|
|
2609
|
+
- vuln
|
|
2610
|
+
technology:
|
|
2611
|
+
- django
|
|
2612
|
+
likelihood: LOW
|
|
2613
|
+
impact: MEDIUM
|
|
2614
|
+
confidence: LOW
|
|
2615
|
+
vulnerability_class:
|
|
2616
|
+
- Active Debug Code
|
|
2617
|
+
patterns:
|
|
2618
|
+
- pattern: DEBUG = True
|
|
2619
|
+
- id: narvy.python.session.django-samesite-none-or-disabled.django-samesite-none-or-disabled
|
|
2620
|
+
message: Session/CSRF cookie SameSite policy is set to "None" or disabled. SameSite=None allows the
|
|
2621
|
+
cookie to be sent on cross-site requests, which is the exact condition CSRF protection exists to prevent
|
|
2622
|
+
(CWE-352). Use `SESSION_COOKIE_SAMESITE = "Lax"` (or "Strict") unless you have a documented cross-site
|
|
2623
|
+
embedding need paired with other CSRF defenses.
|
|
2624
|
+
severity: WARNING
|
|
2625
|
+
languages:
|
|
2626
|
+
- python
|
|
2627
|
+
metadata:
|
|
2628
|
+
cwe:
|
|
2629
|
+
- 'CWE-352: Cross-Site Request Forgery (CSRF)'
|
|
2630
|
+
owasp:
|
|
2631
|
+
- A01:2021 - Broken Access Control
|
|
2632
|
+
category: security
|
|
2633
|
+
subcategory:
|
|
2634
|
+
- vuln
|
|
2635
|
+
technology:
|
|
2636
|
+
- django
|
|
2637
|
+
likelihood: LOW
|
|
2638
|
+
impact: MEDIUM
|
|
2639
|
+
confidence: MEDIUM
|
|
2640
|
+
vulnerability_class:
|
|
2641
|
+
- Cross-Site Request Forgery (CSRF)
|
|
2642
|
+
patterns:
|
|
2643
|
+
- pattern-either:
|
|
2644
|
+
- pattern: SESSION_COOKIE_SAMESITE = None
|
|
2645
|
+
- pattern: SESSION_COOKIE_SAMESITE = "None"
|
|
2646
|
+
- pattern: CSRF_COOKIE_SAMESITE = None
|
|
2647
|
+
- pattern: CSRF_COOKIE_SAMESITE = "None"
|
|
2648
|
+
- id: narvy.python.session.django-secret-key-hardcoded-literal.django-secret-key-hardcoded-literal
|
|
2649
|
+
message: '`SECRET_KEY` is assigned a plain string literal directly in settings, with no environment/secret-manager
|
|
2650
|
+
indirection. Django''s SECRET_KEY signs sessions, CSRF tokens, and password-reset links; a value committed
|
|
2651
|
+
to source control (and therefore to every clone, fork, and CI log) is a hardcoded cryptographic key
|
|
2652
|
+
(CWE-321, CWE-798). Load it from an environment variable and fail startup if unset.'
|
|
2653
|
+
severity: ERROR
|
|
2654
|
+
languages:
|
|
2655
|
+
- python
|
|
2656
|
+
metadata:
|
|
2657
|
+
cwe:
|
|
2658
|
+
- 'CWE-321: Use of Hard-coded Cryptographic Key'
|
|
2659
|
+
- 'CWE-798: Use of Hard-coded Credentials'
|
|
2660
|
+
owasp:
|
|
2661
|
+
- A02:2021 - Cryptographic Failures
|
|
2662
|
+
category: security
|
|
2663
|
+
subcategory:
|
|
2664
|
+
- vuln
|
|
2665
|
+
technology:
|
|
2666
|
+
- django
|
|
2667
|
+
likelihood: HIGH
|
|
2668
|
+
impact: HIGH
|
|
2669
|
+
confidence: MEDIUM
|
|
2670
|
+
vulnerability_class:
|
|
2671
|
+
- Cryptographic Issues
|
|
2672
|
+
- Hard-coded Credentials
|
|
2673
|
+
paths:
|
|
2674
|
+
exclude:
|
|
2675
|
+
- '**/test*/**'
|
|
2676
|
+
- '**/tests/**'
|
|
2677
|
+
- '**/*test*.py'
|
|
2678
|
+
- '**/*example*.py'
|
|
2679
|
+
- '**/docs_src/**'
|
|
2680
|
+
- '**/examples/**'
|
|
2681
|
+
patterns:
|
|
2682
|
+
- pattern: SECRET_KEY = "$LITERAL"
|
|
2683
|
+
- metavariable-regex:
|
|
2684
|
+
metavariable: $LITERAL
|
|
2685
|
+
regex: ^.{8,}$
|
|
2686
|
+
- id: narvy.python.session.django-session-cookie-insecure-settings.django-session-cookie-insecure-settings
|
|
2687
|
+
message: Django settings explicitly disable a session-cookie security flag (`SESSION_COOKIE_SECURE =
|
|
2688
|
+
False`, `SESSION_COOKIE_HTTPONLY = False`, or `CSRF_COOKIE_SECURE = False`). This allows the session/CSRF
|
|
2689
|
+
cookie to be sent over plain HTTP or read from JavaScript, undermining transport security and XSS-hardening
|
|
2690
|
+
respectively (CWE-614, CWE-1004).
|
|
2691
|
+
severity: WARNING
|
|
2692
|
+
languages:
|
|
2693
|
+
- python
|
|
2694
|
+
metadata:
|
|
2695
|
+
cwe:
|
|
2696
|
+
- 'CWE-614: Sensitive Cookie in HTTPS Session Without ''Secure'' Attribute'
|
|
2697
|
+
- 'CWE-1004: Sensitive Cookie Without ''HttpOnly'' Flag'
|
|
2698
|
+
owasp:
|
|
2699
|
+
- A05:2021 - Security Misconfiguration
|
|
2700
|
+
category: security
|
|
2701
|
+
subcategory:
|
|
2702
|
+
- vuln
|
|
2703
|
+
technology:
|
|
2704
|
+
- django
|
|
2705
|
+
likelihood: MEDIUM
|
|
2706
|
+
impact: MEDIUM
|
|
2707
|
+
confidence: HIGH
|
|
2708
|
+
vulnerability_class:
|
|
2709
|
+
- Cookie Security
|
|
2710
|
+
patterns:
|
|
2711
|
+
- pattern-either:
|
|
2712
|
+
- pattern: SESSION_COOKIE_SECURE = False
|
|
2713
|
+
- pattern: SESSION_COOKIE_HTTPONLY = False
|
|
2714
|
+
- pattern: CSRF_COOKIE_SECURE = False
|
|
2715
|
+
- id: narvy.python.session.flask-debug-mode-enabled.flask-debug-mode-enabled
|
|
2716
|
+
message: Flask app is run with `debug=True` (or `app.debug = True`). The Werkzeug debugger it enables
|
|
2717
|
+
exposes an interactive Python console on every unhandled exception - if this ever reaches a production
|
|
2718
|
+
deployment, any visitor who triggers an error gets a remote Python shell (CWE-489, active debug code).
|
|
2719
|
+
Gate `debug=` behind an environment-controlled flag that defaults to `False`, and never hardcode `True`.
|
|
2720
|
+
severity: ERROR
|
|
2721
|
+
languages:
|
|
2722
|
+
- python
|
|
2723
|
+
metadata:
|
|
2724
|
+
cwe:
|
|
2725
|
+
- 'CWE-489: Active Debug Code'
|
|
2726
|
+
owasp:
|
|
2727
|
+
- A05:2021 - Security Misconfiguration
|
|
2728
|
+
category: security
|
|
2729
|
+
subcategory:
|
|
2730
|
+
- vuln
|
|
2731
|
+
technology:
|
|
2732
|
+
- flask
|
|
2733
|
+
likelihood: MEDIUM
|
|
2734
|
+
impact: HIGH
|
|
2735
|
+
confidence: MEDIUM
|
|
2736
|
+
vulnerability_class:
|
|
2737
|
+
- Active Debug Code
|
|
2738
|
+
patterns:
|
|
2739
|
+
- pattern-either:
|
|
2740
|
+
- pattern: $APP.run(..., debug=True, ...)
|
|
2741
|
+
- pattern: $APP.debug = True
|
|
2742
|
+
- pattern: $APP.config["DEBUG"] = True
|
|
2743
|
+
- pattern: $APP.config['DEBUG'] = True
|
|
2744
|
+
- id: narvy.python.session.flask-set-cookie-missing-httponly.flask-set-cookie-missing-httponly
|
|
2745
|
+
message: '`set_cookie()` is called without `httponly=True`. A cookie without HttpOnly is readable from
|
|
2746
|
+
JavaScript via `document.cookie`, so any XSS on the page can exfiltrate it (session hijack). CWE-1004.
|
|
2747
|
+
Set `httponly=True`, or `SESSION_COOKIE_HTTPONLY = True` globally.'
|
|
2748
|
+
severity: WARNING
|
|
2749
|
+
languages:
|
|
2750
|
+
- python
|
|
2751
|
+
metadata:
|
|
2752
|
+
cwe:
|
|
2753
|
+
- 'CWE-1004: Sensitive Cookie Without ''HttpOnly'' Flag'
|
|
2754
|
+
owasp:
|
|
2755
|
+
- A05:2021 - Security Misconfiguration
|
|
2756
|
+
category: security
|
|
2757
|
+
subcategory:
|
|
2758
|
+
- vuln
|
|
2759
|
+
technology:
|
|
2760
|
+
- flask
|
|
2761
|
+
likelihood: MEDIUM
|
|
2762
|
+
impact: MEDIUM
|
|
2763
|
+
confidence: MEDIUM
|
|
2764
|
+
vulnerability_class:
|
|
2765
|
+
- Cookie Security
|
|
2766
|
+
patterns:
|
|
2767
|
+
- pattern: $RESP.set_cookie($NAME, $VALUE, ...)
|
|
2768
|
+
- pattern-not: $RESP.set_cookie($NAME, $VALUE, ..., httponly=True, ...)
|
|
2769
|
+
- pattern-not: $RESP.set_cookie($NAME, $VALUE, ..., httponly=$X, ...)
|
|
2770
|
+
- id: narvy.python.session.flask-set-cookie-missing-secure.flask-set-cookie-missing-secure
|
|
2771
|
+
message: '`set_cookie()` is called without `secure=True`. Without the Secure flag, the browser will
|
|
2772
|
+
also send this cookie over plain HTTP, exposing it to network eavesdroppers and active MITM (CWE-614).
|
|
2773
|
+
Set `secure=True` for any cookie carrying session/auth state, or set `SESSION_COOKIE_SECURE = True`
|
|
2774
|
+
globally in Flask config.'
|
|
2775
|
+
severity: WARNING
|
|
2776
|
+
languages:
|
|
2777
|
+
- python
|
|
2778
|
+
metadata:
|
|
2779
|
+
cwe:
|
|
2780
|
+
- 'CWE-614: Sensitive Cookie in HTTPS Session Without ''Secure'' Attribute'
|
|
2781
|
+
owasp:
|
|
2782
|
+
- A05:2021 - Security Misconfiguration
|
|
2783
|
+
category: security
|
|
2784
|
+
subcategory:
|
|
2785
|
+
- vuln
|
|
2786
|
+
technology:
|
|
2787
|
+
- flask
|
|
2788
|
+
likelihood: MEDIUM
|
|
2789
|
+
impact: MEDIUM
|
|
2790
|
+
confidence: MEDIUM
|
|
2791
|
+
vulnerability_class:
|
|
2792
|
+
- Cookie Security
|
|
2793
|
+
patterns:
|
|
2794
|
+
- pattern: $RESP.set_cookie($NAME, $VALUE, ...)
|
|
2795
|
+
- pattern-not: $RESP.set_cookie($NAME, $VALUE, ..., secure=True, ...)
|
|
2796
|
+
- pattern-not: $RESP.set_cookie($NAME, $VALUE, ..., secure=$X, ...)
|
|
2797
|
+
- id: narvy.python.session.password-compared-with-equality.password-compared-with-equality
|
|
2798
|
+
message: A value named like a password/token/secret is compared with `==` instead of a constant-time
|
|
2799
|
+
comparison. Python's `==` on strings short-circuits on the first differing byte, so the comparison
|
|
2800
|
+
time leaks how many leading characters were correct - a timing side channel an attacker can exploit
|
|
2801
|
+
to recover the secret byte by byte (CWE-208). Use `hmac.compare_digest(a, b)` instead.
|
|
2802
|
+
severity: WARNING
|
|
2803
|
+
languages:
|
|
2804
|
+
- python
|
|
2805
|
+
metadata:
|
|
2806
|
+
cwe:
|
|
2807
|
+
- 'CWE-208: Observable Timing Discrepancy'
|
|
2808
|
+
owasp:
|
|
2809
|
+
- A02:2021 - Cryptographic Failures
|
|
2810
|
+
category: security
|
|
2811
|
+
subcategory:
|
|
2812
|
+
- vuln
|
|
2813
|
+
technology:
|
|
2814
|
+
- python
|
|
2815
|
+
likelihood: LOW
|
|
2816
|
+
impact: MEDIUM
|
|
2817
|
+
confidence: LOW
|
|
2818
|
+
vulnerability_class:
|
|
2819
|
+
- Improper Authentication
|
|
2820
|
+
patterns:
|
|
2821
|
+
- pattern-either:
|
|
2822
|
+
- pattern: "if $STORED_SECRET == $PROVIDED_SECRET:\n ...\n"
|
|
2823
|
+
- pattern: "if $PROVIDED_SECRET == $STORED_SECRET:\n ...\n"
|
|
2824
|
+
- metavariable-regex:
|
|
2825
|
+
metavariable: $STORED_SECRET
|
|
2826
|
+
regex: (?i)\.(password|passwd|pwd|secret|token|api_key|signature|hmac|mac)$
|
|
2827
|
+
- pattern-not-inside: '$R = hmac.compare_digest(...)
|
|
2828
|
+
|
|
2829
|
+
...
|
|
2830
|
+
|
|
2831
|
+
'
|
|
2832
|
+
- id: narvy.python.sqli.django-extra-tainted.django-extra-tainted
|
|
2833
|
+
message: QuerySet `.extra()` is called with a `where=`/`select=`/`tables=` clause built by string interpolation.
|
|
2834
|
+
`.extra()` SQL fragments are inserted into the query almost verbatim; any interpolated variable is
|
|
2835
|
+
SQL injection (CWE-89). `.extra()` is deprecated for this exact reason - use the ORM's expression
|
|
2836
|
+
API or `.raw()` with bound `params`.
|
|
2837
|
+
severity: ERROR
|
|
2838
|
+
languages:
|
|
2839
|
+
- python
|
|
2840
|
+
metadata:
|
|
2841
|
+
cwe:
|
|
2842
|
+
- 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
|
|
2843
|
+
owasp:
|
|
2844
|
+
- A03:2021 - Injection
|
|
2845
|
+
category: security
|
|
2846
|
+
subcategory:
|
|
2847
|
+
- vuln
|
|
2848
|
+
technology:
|
|
2849
|
+
- django
|
|
2850
|
+
likelihood: HIGH
|
|
2851
|
+
impact: HIGH
|
|
2852
|
+
confidence: MEDIUM
|
|
2853
|
+
vulnerability_class:
|
|
2854
|
+
- SQL Injection
|
|
2855
|
+
patterns:
|
|
2856
|
+
- pattern-either:
|
|
2857
|
+
- pattern: $QS.extra(where=[f"...{$VAR}..."])
|
|
2858
|
+
- pattern: $QS.extra(where=["..." + $VAR])
|
|
2859
|
+
- pattern: '$QS.extra(select={$K: f"...{$VAR}..."})'
|
|
2860
|
+
- id: narvy.python.sqli.django-queryset-raw-tainted.django-queryset-raw-tainted
|
|
2861
|
+
message: '`Model.objects.raw()` is called with a SQL string built by concatenation or interpolation
|
|
2862
|
+
rather than passed as a fixed literal with the `params=` argument. Django''s `.raw()` does not auto-parameterize
|
|
2863
|
+
the SQL text itself - only the `params` list is safely bound. SQL injection (CWE-89). Use `Model.objects.raw("...
|
|
2864
|
+
WHERE id = %s", [user_id])`.'
|
|
2865
|
+
severity: ERROR
|
|
2866
|
+
languages:
|
|
2867
|
+
- python
|
|
2868
|
+
metadata:
|
|
2869
|
+
cwe:
|
|
2870
|
+
- 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
|
|
2871
|
+
owasp:
|
|
2872
|
+
- A03:2021 - Injection
|
|
2873
|
+
category: security
|
|
2874
|
+
subcategory:
|
|
2875
|
+
- vuln
|
|
2876
|
+
technology:
|
|
2877
|
+
- django
|
|
2878
|
+
likelihood: HIGH
|
|
2879
|
+
impact: HIGH
|
|
2880
|
+
confidence: HIGH
|
|
2881
|
+
vulnerability_class:
|
|
2882
|
+
- SQL Injection
|
|
2883
|
+
patterns:
|
|
2884
|
+
- pattern-either:
|
|
2885
|
+
- pattern: $MODEL.objects.raw("..." + $VAR)
|
|
2886
|
+
- pattern: $MODEL.objects.raw($VAR + "...")
|
|
2887
|
+
- pattern: $MODEL.objects.raw(f"...{$VAR}...")
|
|
2888
|
+
- pattern: $MODEL.objects.raw("...".format(...))
|
|
2889
|
+
- pattern: $MODEL.objects.raw("..." % $VAR)
|
|
2890
|
+
- id: narvy.python.sqli.pymongo-eval-where-injection.pymongo-eval-where-injection
|
|
2891
|
+
message: MongoDB `$where` clause (or `db.eval()`) built with a tainted string. `$where` executes JavaScript
|
|
2892
|
+
server-side against every document; an attacker-controlled fragment here is server-side NoSQL code
|
|
2893
|
+
injection, not just a filter bypass (CWE-943, CWE-94). Use standard MongoDB query operators instead
|
|
2894
|
+
of `$where`.
|
|
2895
|
+
severity: ERROR
|
|
2896
|
+
languages:
|
|
2897
|
+
- python
|
|
2898
|
+
metadata:
|
|
2899
|
+
cwe:
|
|
2900
|
+
- 'CWE-943: Improper Neutralization of Special Elements in Data Query Logic'
|
|
2901
|
+
- 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
|
|
2902
|
+
owasp:
|
|
2903
|
+
- A03:2021 - Injection
|
|
2904
|
+
category: security
|
|
2905
|
+
subcategory:
|
|
2906
|
+
- vuln
|
|
2907
|
+
technology:
|
|
2908
|
+
- pymongo
|
|
2909
|
+
- mongodb
|
|
2910
|
+
likelihood: MEDIUM
|
|
2911
|
+
impact: HIGH
|
|
2912
|
+
confidence: MEDIUM
|
|
2913
|
+
vulnerability_class:
|
|
2914
|
+
- SQL Injection
|
|
2915
|
+
patterns:
|
|
2916
|
+
- pattern-either:
|
|
2917
|
+
- pattern: '{"$where": f"...{$VAR}..."}'
|
|
2918
|
+
- pattern: '{"$where": "..." + $VAR}'
|
|
2919
|
+
- pattern: $DB.eval(f"...{$VAR}...")
|
|
2920
|
+
- pattern: $DB.eval("..." + $VAR)
|
|
2921
|
+
- id: narvy.python.sqli.sqlalchemy-execute-string-concat.sqlalchemy-execute-string-concat
|
|
2922
|
+
message: '`.execute()` on a SQLAlchemy Connection/Session/Engine called with a raw string built by concatenation
|
|
2923
|
+
or f-string rather than a parameterized `text()` construct with bound params. SQL injection (CWE-89).'
|
|
2924
|
+
severity: ERROR
|
|
2925
|
+
languages:
|
|
2926
|
+
- python
|
|
2927
|
+
metadata:
|
|
2928
|
+
cwe:
|
|
2929
|
+
- 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
|
|
2930
|
+
owasp:
|
|
2931
|
+
- A03:2021 - Injection
|
|
2932
|
+
category: security
|
|
2933
|
+
subcategory:
|
|
2934
|
+
- vuln
|
|
2935
|
+
technology:
|
|
2936
|
+
- sqlalchemy
|
|
2937
|
+
likelihood: HIGH
|
|
2938
|
+
impact: HIGH
|
|
2939
|
+
confidence: MEDIUM
|
|
2940
|
+
vulnerability_class:
|
|
2941
|
+
- SQL Injection
|
|
2942
|
+
patterns:
|
|
2943
|
+
- pattern-either:
|
|
2944
|
+
- pattern: $SESSION.execute("..." + $VAR)
|
|
2945
|
+
- pattern: $SESSION.execute(f"...{$VAR}...")
|
|
2946
|
+
- pattern: $ENGINE.connect().execute("..." + $VAR)
|
|
2947
|
+
- metavariable-regex:
|
|
2948
|
+
metavariable: $SESSION
|
|
2949
|
+
regex: (?i)(session|conn|connection|engine)
|
|
2950
|
+
- id: narvy.python.sqli.sqlalchemy-text-tainted.sqlalchemy-text-tainted
|
|
2951
|
+
message: 'SQLAlchemy `text()` construct built by string interpolation rather than using bound parameters
|
|
2952
|
+
(`text("... WHERE id = :id").bindparams(id=x)` or `conn.execute(text(...), {"id": x})`). Interpolating
|
|
2953
|
+
a variable directly into the SQL text defeats SQLAlchemy''s parameterization and is SQL injection
|
|
2954
|
+
(CWE-89).'
|
|
2955
|
+
severity: ERROR
|
|
2956
|
+
languages:
|
|
2957
|
+
- python
|
|
2958
|
+
metadata:
|
|
2959
|
+
cwe:
|
|
2960
|
+
- 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
|
|
2961
|
+
owasp:
|
|
2962
|
+
- A03:2021 - Injection
|
|
2963
|
+
category: security
|
|
2964
|
+
subcategory:
|
|
2965
|
+
- vuln
|
|
2966
|
+
technology:
|
|
2967
|
+
- sqlalchemy
|
|
2968
|
+
likelihood: HIGH
|
|
2969
|
+
impact: HIGH
|
|
2970
|
+
confidence: HIGH
|
|
2971
|
+
vulnerability_class:
|
|
2972
|
+
- SQL Injection
|
|
2973
|
+
patterns:
|
|
2974
|
+
- pattern-either:
|
|
2975
|
+
- pattern: text(f"...{$VAR}...")
|
|
2976
|
+
- pattern: text("..." + $VAR)
|
|
2977
|
+
- pattern: text($VAR + "...")
|
|
2978
|
+
- pattern: text("...".format($VAR))
|
|
2979
|
+
- pattern: text("..." % $VAR)
|
|
2980
|
+
- pattern-not: text("...")
|
|
2981
|
+
- id: narvy.python.sqli.formatted-sql-string-to-execute.formatted-sql-string-to-execute
|
|
2982
|
+
message: >-
|
|
2983
|
+
A SQL query built with Python string formatting (%, .format(), an f-string
|
|
2984
|
+
interpolation, or + concatenation) reaches cursor.execute / cursor.executemany.
|
|
2985
|
+
If any interpolated value is attacker-controlled this is SQL injection (CWE-89).
|
|
2986
|
+
Build queries with bound parameters instead: execute(sql, (params,)) or use an
|
|
2987
|
+
ORM with bound parameters.
|
|
2988
|
+
severity: ERROR
|
|
2989
|
+
languages:
|
|
2990
|
+
- python
|
|
2991
|
+
mode: taint
|
|
2992
|
+
metadata:
|
|
2993
|
+
cwe:
|
|
2994
|
+
- 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command (''SQL Injection'')'
|
|
2995
|
+
owasp:
|
|
2996
|
+
- 'A03:2021 - Injection'
|
|
2997
|
+
category: security
|
|
2998
|
+
subcategory:
|
|
2999
|
+
- audit
|
|
3000
|
+
technology:
|
|
3001
|
+
- python
|
|
3002
|
+
- sqlite3
|
|
3003
|
+
- psycopg2
|
|
3004
|
+
- mysql-connector
|
|
3005
|
+
- aiopg
|
|
3006
|
+
- asyncpg
|
|
3007
|
+
- pymysql
|
|
3008
|
+
likelihood: MEDIUM
|
|
3009
|
+
impact: HIGH
|
|
3010
|
+
confidence: MEDIUM
|
|
3011
|
+
vulnerability_class:
|
|
3012
|
+
- Improper Validation
|
|
3013
|
+
references:
|
|
3014
|
+
- https://cwe.mitre.org/data/definitions/89.html
|
|
3015
|
+
pattern-sources:
|
|
3016
|
+
- patterns:
|
|
3017
|
+
- pattern: '"$SQL" % $X'
|
|
3018
|
+
- metavariable-regex:
|
|
3019
|
+
metavariable: $SQL
|
|
3020
|
+
regex: (?i).*\b(select|insert\s+into|update|delete\s+from|drop\s+table|alter\s+table|create\s+table|merge|truncate|where|values|set|from)\b.*
|
|
3021
|
+
- patterns:
|
|
3022
|
+
- pattern: '"$SQL".format(...)'
|
|
3023
|
+
- metavariable-regex:
|
|
3024
|
+
metavariable: $SQL
|
|
3025
|
+
regex: (?i).*\b(select|insert\s+into|update|delete\s+from|drop\s+table|alter\s+table|create\s+table|merge|truncate|where|values|set|from)\b.*
|
|
3026
|
+
- pattern: 'f"...{$X}..."'
|
|
3027
|
+
- patterns:
|
|
3028
|
+
- pattern: '"$SQL" + $X'
|
|
3029
|
+
- metavariable-regex:
|
|
3030
|
+
metavariable: $SQL
|
|
3031
|
+
regex: (?i).*\b(select|insert\s+into|update|delete\s+from|drop\s+table|alter\s+table|create\s+table|merge|truncate|where|values|set|from)\b.*
|
|
3032
|
+
pattern-sinks:
|
|
3033
|
+
- pattern: $CURSOR.execute($QUERY, ...)
|
|
3034
|
+
- pattern: $CURSOR.execute($QUERY)
|
|
3035
|
+
- pattern: $CURSOR.executemany($QUERY, ...)
|
|
3036
|
+
- pattern: $CURSOR.executemany($QUERY)
|
|
3037
|
+
- id: narvy.python.ssrf.httpx-tainted-url.httpx-tainted-url
|
|
3038
|
+
message: '`httpx` client call with a URL built directly from request data. Same SSRF risk (CWE-918)
|
|
3039
|
+
as `requests`/`urllib` - httpx is increasingly the default HTTP client in async FastAPI codebases
|
|
3040
|
+
and is just as exploitable if the URL/host is attacker-controlled.'
|
|
3041
|
+
severity: ERROR
|
|
3042
|
+
languages:
|
|
3043
|
+
- python
|
|
3044
|
+
metadata:
|
|
3045
|
+
cwe:
|
|
3046
|
+
- 'CWE-918: Server-Side Request Forgery (SSRF)'
|
|
3047
|
+
owasp:
|
|
3048
|
+
- A10:2021 - Server-Side Request Forgery
|
|
3049
|
+
category: security
|
|
3050
|
+
subcategory:
|
|
3051
|
+
- vuln
|
|
3052
|
+
technology:
|
|
3053
|
+
- python
|
|
3054
|
+
- httpx
|
|
3055
|
+
- fastapi
|
|
3056
|
+
likelihood: HIGH
|
|
3057
|
+
impact: HIGH
|
|
3058
|
+
confidence: MEDIUM
|
|
3059
|
+
vulnerability_class:
|
|
3060
|
+
- Server-Side Request Forgery (SSRF)
|
|
3061
|
+
patterns:
|
|
3062
|
+
- pattern-either:
|
|
3063
|
+
- pattern: httpx.$METHOD($URL)
|
|
3064
|
+
- pattern: await $CLIENT.$METHOD($URL)
|
|
3065
|
+
- metavariable-regex:
|
|
3066
|
+
metavariable: $URL
|
|
3067
|
+
regex: (?i)(request\.(args|form|json|values|get)|query_params|path_params)
|
|
3068
|
+
- metavariable-regex:
|
|
3069
|
+
metavariable: $METHOD
|
|
3070
|
+
regex: ^(get|post|put|delete|head|patch|request)$
|
|
3071
|
+
- id: narvy.python.ssrf.requests-tainted-url.requests-tainted-url
|
|
3072
|
+
message: '`requests.$METHOD()` is called with a URL built directly from request data (`request.args`,
|
|
3073
|
+
`request.form`, `request.json`, a Django `request.GET`, or a FastAPI path/query parameter). Without
|
|
3074
|
+
validating the target host against an allowlist, an attacker can point this request at internal services
|
|
3075
|
+
(cloud metadata endpoints, admin panels, internal APIs) that are not otherwise reachable from the
|
|
3076
|
+
internet. SSRF (CWE-918).'
|
|
3077
|
+
severity: ERROR
|
|
3078
|
+
languages:
|
|
3079
|
+
- python
|
|
3080
|
+
metadata:
|
|
3081
|
+
cwe:
|
|
3082
|
+
- 'CWE-918: Server-Side Request Forgery (SSRF)'
|
|
3083
|
+
owasp:
|
|
3084
|
+
- A10:2021 - Server-Side Request Forgery
|
|
3085
|
+
category: security
|
|
3086
|
+
subcategory:
|
|
3087
|
+
- vuln
|
|
3088
|
+
technology:
|
|
3089
|
+
- python
|
|
3090
|
+
- requests
|
|
3091
|
+
- flask
|
|
3092
|
+
- django
|
|
3093
|
+
likelihood: HIGH
|
|
3094
|
+
impact: HIGH
|
|
3095
|
+
confidence: MEDIUM
|
|
3096
|
+
vulnerability_class:
|
|
3097
|
+
- Server-Side Request Forgery (SSRF)
|
|
3098
|
+
patterns:
|
|
3099
|
+
- pattern-either:
|
|
3100
|
+
- pattern: requests.$METHOD(request.$KIND.$ATTR(...))
|
|
3101
|
+
- pattern: requests.$METHOD(request.$KIND[$X])
|
|
3102
|
+
- pattern: requests.$METHOD(request.$KIND.get(...))
|
|
3103
|
+
- pattern: requests.$METHOD(f"...{request.$KIND.$ATTR(...)}...")
|
|
3104
|
+
- pattern: requests.$METHOD("..." + request.$KIND.$ATTR(...))
|
|
3105
|
+
- pattern: '$URL = request.$KIND.$ATTR(...)
|
|
3106
|
+
|
|
3107
|
+
...
|
|
3108
|
+
|
|
3109
|
+
requests.$METHOD($URL)
|
|
3110
|
+
|
|
3111
|
+
'
|
|
3112
|
+
- pattern: '$URL = request.$KIND[$X]
|
|
3113
|
+
|
|
3114
|
+
...
|
|
3115
|
+
|
|
3116
|
+
requests.$METHOD($URL)
|
|
3117
|
+
|
|
3118
|
+
'
|
|
3119
|
+
- metavariable-regex:
|
|
3120
|
+
metavariable: $METHOD
|
|
3121
|
+
regex: ^(get|post|put|delete|head|patch|request)$
|
|
3122
|
+
- metavariable-regex:
|
|
3123
|
+
metavariable: $KIND
|
|
3124
|
+
regex: ^(args|form|json|values|GET|POST|query_params|path_params)$
|
|
3125
|
+
- id: narvy.python.ssrf.tainted-host-only.tainted-host-only
|
|
3126
|
+
message: A request's target *host* (not the full URL) is built from request data and then f-string-interpolated
|
|
3127
|
+
into a `requests`/`urllib` call (`f"http://{host}/..."`). Restricting only the path while letting
|
|
3128
|
+
the host be attacker-controlled is still full SSRF (CWE-918) - validate the host against an allowlist
|
|
3129
|
+
before building the URL.
|
|
3130
|
+
severity: ERROR
|
|
3131
|
+
languages:
|
|
3132
|
+
- python
|
|
3133
|
+
metadata:
|
|
3134
|
+
cwe:
|
|
3135
|
+
- 'CWE-918: Server-Side Request Forgery (SSRF)'
|
|
3136
|
+
owasp:
|
|
3137
|
+
- A10:2021 - Server-Side Request Forgery
|
|
3138
|
+
category: security
|
|
3139
|
+
subcategory:
|
|
3140
|
+
- vuln
|
|
3141
|
+
technology:
|
|
3142
|
+
- python
|
|
3143
|
+
- requests
|
|
3144
|
+
likelihood: MEDIUM
|
|
3145
|
+
impact: HIGH
|
|
3146
|
+
confidence: MEDIUM
|
|
3147
|
+
vulnerability_class:
|
|
3148
|
+
- Server-Side Request Forgery (SSRF)
|
|
3149
|
+
patterns:
|
|
3150
|
+
- pattern-either:
|
|
3151
|
+
- pattern: '$HOST = request.$KIND.$ATTR(...)
|
|
3152
|
+
|
|
3153
|
+
...
|
|
3154
|
+
|
|
3155
|
+
requests.$METHOD(f"http://{$HOST}$REST")
|
|
3156
|
+
|
|
3157
|
+
'
|
|
3158
|
+
- pattern: '$HOST = request.$KIND.$ATTR(...)
|
|
3159
|
+
|
|
3160
|
+
...
|
|
3161
|
+
|
|
3162
|
+
requests.$METHOD(f"https://{$HOST}$REST")
|
|
3163
|
+
|
|
3164
|
+
'
|
|
3165
|
+
- metavariable-regex:
|
|
3166
|
+
metavariable: $KIND
|
|
3167
|
+
regex: ^(args|form|json|values|GET|POST)$
|
|
3168
|
+
- id: narvy.python.ssrf.urllib-tainted-url.urllib-tainted-url
|
|
3169
|
+
message: '`urllib.request.urlopen()` is called with a URL built directly from request data. Same SSRF
|
|
3170
|
+
risk as the `requests`-library case (CWE-918): the server will fetch whatever host/path the attacker
|
|
3171
|
+
supplies, including internal-only addresses. Validate the target against an allowlist of hosts, or
|
|
3172
|
+
resolve and check the IP is not in a private/link-local range before fetching.'
|
|
3173
|
+
severity: ERROR
|
|
3174
|
+
languages:
|
|
3175
|
+
- python
|
|
3176
|
+
metadata:
|
|
3177
|
+
cwe:
|
|
3178
|
+
- 'CWE-918: Server-Side Request Forgery (SSRF)'
|
|
3179
|
+
owasp:
|
|
3180
|
+
- A10:2021 - Server-Side Request Forgery
|
|
3181
|
+
category: security
|
|
3182
|
+
subcategory:
|
|
3183
|
+
- vuln
|
|
3184
|
+
technology:
|
|
3185
|
+
- python
|
|
3186
|
+
likelihood: HIGH
|
|
3187
|
+
impact: HIGH
|
|
3188
|
+
confidence: MEDIUM
|
|
3189
|
+
vulnerability_class:
|
|
3190
|
+
- Server-Side Request Forgery (SSRF)
|
|
3191
|
+
patterns:
|
|
3192
|
+
- pattern-either:
|
|
3193
|
+
- pattern: urllib.request.urlopen(request.$KIND.$ATTR(...))
|
|
3194
|
+
- pattern: urllib.request.urlopen(request.$KIND[$X])
|
|
3195
|
+
- pattern: urlopen(request.$KIND.$ATTR(...))
|
|
3196
|
+
- pattern: '$URL = request.$KIND.$ATTR(...)
|
|
3197
|
+
|
|
3198
|
+
...
|
|
3199
|
+
|
|
3200
|
+
urllib.request.urlopen($URL)
|
|
3201
|
+
|
|
3202
|
+
'
|
|
3203
|
+
- metavariable-regex:
|
|
3204
|
+
metavariable: $KIND
|
|
3205
|
+
regex: ^(args|form|json|values|GET|POST)$
|
|
3206
|
+
- id: narvy.python.transport.bind-all-interfaces.bind-all-interfaces
|
|
3207
|
+
message: Server socket / dev-server binds to `0.0.0.0` (all interfaces) explicitly in application code.
|
|
3208
|
+
This exposes the service on every network interface, including ones only meant to be internal/loopback,
|
|
3209
|
+
bypassing whatever network-level restriction the deployment intended (CWE-668). Bind to a specific
|
|
3210
|
+
interface, or make the bind address configurable via environment and default it to `127.0.0.1` for
|
|
3211
|
+
anything not meant to be internet-facing.
|
|
3212
|
+
severity: WARNING
|
|
3213
|
+
languages:
|
|
3214
|
+
- python
|
|
3215
|
+
metadata:
|
|
3216
|
+
cwe:
|
|
3217
|
+
- 'CWE-668: Exposure of Resource to Wrong Sphere'
|
|
3218
|
+
owasp:
|
|
3219
|
+
- A05:2021 - Security Misconfiguration
|
|
3220
|
+
category: security
|
|
3221
|
+
subcategory:
|
|
3222
|
+
- vuln
|
|
3223
|
+
technology:
|
|
3224
|
+
- python
|
|
3225
|
+
- flask
|
|
3226
|
+
likelihood: LOW
|
|
3227
|
+
impact: MEDIUM
|
|
3228
|
+
confidence: LOW
|
|
3229
|
+
vulnerability_class:
|
|
3230
|
+
- Mishandled Sensitive Information
|
|
3231
|
+
patterns:
|
|
3232
|
+
- pattern-either:
|
|
3233
|
+
- pattern: $APP.run(host="0.0.0.0", ...)
|
|
3234
|
+
- pattern: socket.bind(("0.0.0.0", $PORT))
|
|
3235
|
+
- id: narvy.python.transport.cleartext-http-literal.cleartext-http-literal
|
|
3236
|
+
message: Cleartext HTTP URL detected. Traffic to this endpoint is unencrypted and vulnerable to MITM
|
|
3237
|
+
(eavesdropping, tampering). Use HTTPS. CWE-319.
|
|
3238
|
+
severity: WARNING
|
|
3239
|
+
languages:
|
|
3240
|
+
- python
|
|
3241
|
+
metadata:
|
|
3242
|
+
cwe:
|
|
3243
|
+
- 'CWE-319: Cleartext Transmission of Sensitive Information'
|
|
3244
|
+
owasp:
|
|
3245
|
+
- A02:2021 - Cryptographic Failures
|
|
3246
|
+
- A03:2017 - Sensitive Data Exposure
|
|
3247
|
+
category: security
|
|
3248
|
+
subcategory:
|
|
3249
|
+
- vuln
|
|
3250
|
+
technology:
|
|
3251
|
+
- python
|
|
3252
|
+
likelihood: MEDIUM
|
|
3253
|
+
impact: MEDIUM
|
|
3254
|
+
confidence: HIGH
|
|
3255
|
+
vulnerability_class:
|
|
3256
|
+
- Insecure Communication
|
|
3257
|
+
references:
|
|
3258
|
+
- https://cwe.mitre.org/data/definitions/319.html
|
|
3259
|
+
- https://owasp.org/www-community/vulnerabilities/Insecure_Transport
|
|
3260
|
+
paths:
|
|
3261
|
+
exclude:
|
|
3262
|
+
- '**/fuzzer/payloads.py'
|
|
3263
|
+
- '**/fuzzer/**/payloads*.py'
|
|
3264
|
+
- '**/oob/blind_payloads.py'
|
|
3265
|
+
- '**/*_analyzer.py'
|
|
3266
|
+
- '**/payloads/*.py'
|
|
3267
|
+
patterns:
|
|
3268
|
+
- pattern-either:
|
|
3269
|
+
- pattern: '"$URL"'
|
|
3270
|
+
- pattern: '''$URL'''
|
|
3271
|
+
- metavariable-regex:
|
|
3272
|
+
metavariable: $URL
|
|
3273
|
+
regex: ^http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0|169\.254\.|10\.\d+\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|\[::1\]|[a-zA-Z0-9.-]+\.(local|internal|test|example)(/|$|:)|schemas\.|www\.w3\.org|xmlns\.com|purl\.org|ns\.adobe\.com|java\.sun\.com|jabber\.org|x\.org|evil\.com)[a-zA-Z0-9._~:/?#\[\]@!$&'()*+,;=%-]+$
|
|
3274
|
+
- id: narvy.python.xpath-injection.lxml-xpath-tainted.lxml-xpath-tainted
|
|
3275
|
+
message: An XPath expression evaluated by `lxml`/`xml.etree`'s `.xpath()` is built by string interpolation
|
|
3276
|
+
with request data. Like SQL, XPath has no built-in parameter binding for string-built queries; an
|
|
3277
|
+
attacker can inject expression syntax (`' or '1'='1`) to select unintended nodes or bypass an authentication
|
|
3278
|
+
check implemented as an XPath query. CWE-643 (XPath Injection). Use `.xpath(expr, var=$VAR)` with
|
|
3279
|
+
XPath variable binding instead of interpolating the value into the expression string.
|
|
3280
|
+
severity: ERROR
|
|
3281
|
+
languages:
|
|
3282
|
+
- python
|
|
3283
|
+
metadata:
|
|
3284
|
+
cwe:
|
|
3285
|
+
- 'CWE-643: Improper Neutralization of Data within XPath Expressions (''XPath Injection'')'
|
|
3286
|
+
owasp:
|
|
3287
|
+
- A03:2021 - Injection
|
|
3288
|
+
category: security
|
|
3289
|
+
subcategory:
|
|
3290
|
+
- vuln
|
|
3291
|
+
technology:
|
|
3292
|
+
- lxml
|
|
3293
|
+
- python
|
|
3294
|
+
likelihood: MEDIUM
|
|
3295
|
+
impact: MEDIUM
|
|
3296
|
+
confidence: MEDIUM
|
|
3297
|
+
vulnerability_class:
|
|
3298
|
+
- Improper Validation
|
|
3299
|
+
patterns:
|
|
3300
|
+
- pattern-either:
|
|
3301
|
+
- pattern: $TREE.xpath(f"...{$VAR}...")
|
|
3302
|
+
- pattern: $TREE.xpath("..." + $VAR)
|
|
3303
|
+
- pattern: $TREE.xpath("...".format($VAR))
|
|
3304
|
+
- pattern: $TREE.xpath("..." % $VAR)
|
|
3305
|
+
- pattern-not: $TREE.xpath("...")
|
|
3306
|
+
- id: narvy.python.xss.django-context-autoescape-disabled.django-context-autoescape-disabled
|
|
3307
|
+
message: A Django template `Context` is constructed with `autoescape=False`. Every variable rendered
|
|
3308
|
+
through this context skips Django's automatic HTML escaping (CWE-79). If any of the rendered data
|
|
3309
|
+
originates from a request, a database row a user can edit, or any other attacker-influenced source,
|
|
3310
|
+
this is XSS. Remove the `autoescape=False` argument, or escape the specific values that need to stay
|
|
3311
|
+
unescaped with `mark_safe()` after running them through `bleach.clean()` or similar.
|
|
3312
|
+
severity: WARNING
|
|
3313
|
+
languages:
|
|
3314
|
+
- python
|
|
3315
|
+
metadata:
|
|
3316
|
+
cwe:
|
|
3317
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3318
|
+
owasp:
|
|
3319
|
+
- A03:2021 - Injection
|
|
3320
|
+
category: security
|
|
3321
|
+
subcategory:
|
|
3322
|
+
- vuln
|
|
3323
|
+
technology:
|
|
3324
|
+
- django
|
|
3325
|
+
likelihood: LOW
|
|
3326
|
+
impact: HIGH
|
|
3327
|
+
confidence: MEDIUM
|
|
3328
|
+
vulnerability_class:
|
|
3329
|
+
- Cross-Site-Scripting (XSS)
|
|
3330
|
+
patterns:
|
|
3331
|
+
- pattern-either:
|
|
3332
|
+
- pattern: Context(..., autoescape=False, ...)
|
|
3333
|
+
- pattern: RequestContext(..., autoescape=False, ...)
|
|
3334
|
+
- id: narvy.python.xss.django-html-email-body-tainted.django-html-email-body-tainted
|
|
3335
|
+
message: An `EmailMessage` whose `content_subtype` is set to `"html"` is built from request data. Mail
|
|
3336
|
+
clients render this body as raw HTML with no equivalent of Django's template auto-escaping applied
|
|
3337
|
+
- if an attacker controls the value, they control the HTML/script that renders in whoever's inbox
|
|
3338
|
+
receives it (CWE-79, email-delivered XSS). Escape the value with `django.utils.html.escape()` before
|
|
3339
|
+
interpolating it into the HTML body, or build the email through a Django template (auto-escaped) instead
|
|
3340
|
+
of raw string concatenation/formatting.
|
|
3341
|
+
severity: WARNING
|
|
3342
|
+
languages:
|
|
3343
|
+
- python
|
|
3344
|
+
metadata:
|
|
3345
|
+
cwe:
|
|
3346
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3347
|
+
owasp:
|
|
3348
|
+
- A03:2021 - Injection
|
|
3349
|
+
category: security
|
|
3350
|
+
subcategory:
|
|
3351
|
+
- vuln
|
|
3352
|
+
technology:
|
|
3353
|
+
- django
|
|
3354
|
+
likelihood: MEDIUM
|
|
3355
|
+
impact: MEDIUM
|
|
3356
|
+
confidence: MEDIUM
|
|
3357
|
+
vulnerability_class:
|
|
3358
|
+
- Cross-Site-Scripting (XSS)
|
|
3359
|
+
patterns:
|
|
3360
|
+
- pattern-inside: "def $FUNC(...):\n ...\n $EMAIL.content_subtype = \"html\"\n ...\n"
|
|
3361
|
+
- pattern-either:
|
|
3362
|
+
- pattern: django.core.mail.EmailMessage($SUBJ, request.$KIND.$ATTR(...), ...)
|
|
3363
|
+
- pattern: EmailMessage($SUBJ, request.$KIND.$ATTR(...), ...)
|
|
3364
|
+
- pattern: '$BODY = request.$KIND.$ATTR(...)
|
|
3365
|
+
|
|
3366
|
+
...
|
|
3367
|
+
|
|
3368
|
+
django.core.mail.EmailMessage($SUBJ, $BODY, ...)
|
|
3369
|
+
|
|
3370
|
+
'
|
|
3371
|
+
- pattern: '$BODY = request.$KIND.$ATTR(...)
|
|
3372
|
+
|
|
3373
|
+
...
|
|
3374
|
+
|
|
3375
|
+
EmailMessage($SUBJ, $BODY, ...)
|
|
3376
|
+
|
|
3377
|
+
'
|
|
3378
|
+
- metavariable-regex:
|
|
3379
|
+
metavariable: $KIND
|
|
3380
|
+
regex: ^(GET|POST|data|query_params)$
|
|
3381
|
+
- id: narvy.python.xss.django-httpresponse-echoes-request-data.django-httpresponse-echoes-request-data
|
|
3382
|
+
message: '`HttpResponse()` built by interpolating `request.GET`/`request.POST` directly into the response
|
|
3383
|
+
body instead of going through a Django template (which auto-escapes) or `django.utils.html.escape()`.
|
|
3384
|
+
Reflected XSS (CWE-79).'
|
|
3385
|
+
severity: ERROR
|
|
3386
|
+
languages:
|
|
3387
|
+
- python
|
|
3388
|
+
metadata:
|
|
3389
|
+
cwe:
|
|
3390
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3391
|
+
owasp:
|
|
3392
|
+
- A03:2021 - Injection
|
|
3393
|
+
category: security
|
|
3394
|
+
subcategory:
|
|
3395
|
+
- vuln
|
|
3396
|
+
technology:
|
|
3397
|
+
- django
|
|
3398
|
+
likelihood: HIGH
|
|
3399
|
+
impact: MEDIUM
|
|
3400
|
+
confidence: MEDIUM
|
|
3401
|
+
vulnerability_class:
|
|
3402
|
+
- Cross-Site-Scripting (XSS)
|
|
3403
|
+
patterns:
|
|
3404
|
+
- pattern-either:
|
|
3405
|
+
- pattern: HttpResponse(f"...{request.$KIND.$ATTR(...)}...")
|
|
3406
|
+
- pattern: HttpResponse("..." + request.$KIND.$ATTR(...))
|
|
3407
|
+
- pattern: HttpResponse("...".format(request.$KIND.$ATTR(...)))
|
|
3408
|
+
- pattern: HttpResponse("..." % request.$KIND.$ATTR(...))
|
|
3409
|
+
- metavariable-regex:
|
|
3410
|
+
metavariable: $KIND
|
|
3411
|
+
regex: ^(GET|POST|COOKIES|META)$
|
|
3412
|
+
- id: narvy.python.xss.django-mark-safe-html-email-body.django-mark-safe-html-email-body
|
|
3413
|
+
message: '`mark_safe()` output is passed directly as an HTML email body (`EmailMessage` with `content_subtype
|
|
3414
|
+
= "html"`, or `send_mail(..., html_message=...)`). `mark_safe()` skips Django''s escaping, so whatever
|
|
3415
|
+
went into it reaches the recipient''s mail client verbatim (CWE-79). Only wrap already-sanitized HTML
|
|
3416
|
+
(e.g. from `bleach.clean()`) with `mark_safe()`, never a raw concatenation/format string built from
|
|
3417
|
+
request or database data.'
|
|
3418
|
+
severity: WARNING
|
|
3419
|
+
languages:
|
|
3420
|
+
- python
|
|
3421
|
+
metadata:
|
|
3422
|
+
cwe:
|
|
3423
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3424
|
+
owasp:
|
|
3425
|
+
- A03:2021 - Injection
|
|
3426
|
+
category: security
|
|
3427
|
+
subcategory:
|
|
3428
|
+
- audit
|
|
3429
|
+
technology:
|
|
3430
|
+
- django
|
|
3431
|
+
likelihood: LOW
|
|
3432
|
+
impact: MEDIUM
|
|
3433
|
+
confidence: LOW
|
|
3434
|
+
vulnerability_class:
|
|
3435
|
+
- Cross-Site-Scripting (XSS)
|
|
3436
|
+
patterns:
|
|
3437
|
+
- pattern-either:
|
|
3438
|
+
- patterns:
|
|
3439
|
+
- pattern-inside: "def $FUNC(...):\n ...\n $EMAIL.content_subtype = \"html\"\n ...\n"
|
|
3440
|
+
- pattern: '$BODY = mark_safe(...)
|
|
3441
|
+
|
|
3442
|
+
...
|
|
3443
|
+
|
|
3444
|
+
django.core.mail.EmailMessage($SUBJ, $BODY, ...)
|
|
3445
|
+
|
|
3446
|
+
'
|
|
3447
|
+
- patterns:
|
|
3448
|
+
- pattern-inside: "def $FUNC(...):\n ...\n $EMAIL.content_subtype = \"html\"\n ...\n"
|
|
3449
|
+
- pattern: '$BODY = mark_safe(...)
|
|
3450
|
+
|
|
3451
|
+
...
|
|
3452
|
+
|
|
3453
|
+
EmailMessage($SUBJ, $BODY, ...)
|
|
3454
|
+
|
|
3455
|
+
'
|
|
3456
|
+
- pattern: '$BODY = mark_safe(...)
|
|
3457
|
+
|
|
3458
|
+
...
|
|
3459
|
+
|
|
3460
|
+
send_mail(..., html_message=$BODY, ...)
|
|
3461
|
+
|
|
3462
|
+
'
|
|
3463
|
+
- id: narvy.python.xss.django-mark-safe-tainted.django-mark-safe-tainted
|
|
3464
|
+
message: '`mark_safe()` is applied to a string built by interpolating a variable that is not passed
|
|
3465
|
+
through `escape()`/`conditional_escape()` first. `mark_safe()` tells Django''s template engine to
|
|
3466
|
+
skip auto-escaping entirely - any HTML/JS in the unescaped interpolated value reaches the browser
|
|
3467
|
+
verbatim (CWE-79). If the variable is fully attacker-controlled this is XSS; if it is only developer/admin-controlled,
|
|
3468
|
+
escaping it anyway costs nothing and keeps the call reviewable without having to trace where every
|
|
3469
|
+
value came from. Wrap the interpolated part in `escape(...)`, or build the HTML through a template
|
|
3470
|
+
instead of `mark_safe()`.'
|
|
3471
|
+
severity: WARNING
|
|
3472
|
+
languages:
|
|
3473
|
+
- python
|
|
3474
|
+
metadata:
|
|
3475
|
+
cwe:
|
|
3476
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3477
|
+
owasp:
|
|
3478
|
+
- A03:2021 - Injection
|
|
3479
|
+
category: security
|
|
3480
|
+
subcategory:
|
|
3481
|
+
- audit
|
|
3482
|
+
technology:
|
|
3483
|
+
- django
|
|
3484
|
+
likelihood: MEDIUM
|
|
3485
|
+
impact: MEDIUM
|
|
3486
|
+
confidence: LOW
|
|
3487
|
+
vulnerability_class:
|
|
3488
|
+
- Cross-Site-Scripting (XSS)
|
|
3489
|
+
patterns:
|
|
3490
|
+
- pattern-either:
|
|
3491
|
+
- pattern: mark_safe(f"...{$VAR}...")
|
|
3492
|
+
- pattern: mark_safe("..." + $VAR)
|
|
3493
|
+
- pattern: mark_safe("...".format($VAR))
|
|
3494
|
+
- pattern: mark_safe("..." % $VAR)
|
|
3495
|
+
- pattern-not: mark_safe("...")
|
|
3496
|
+
- pattern-not: mark_safe(f"...{escape($VAR)}...")
|
|
3497
|
+
- pattern-not: mark_safe(f"...{conditional_escape($VAR)}...")
|
|
3498
|
+
- pattern-not: mark_safe("..." + escape($VAR))
|
|
3499
|
+
- pattern-not: mark_safe("...".format(escape($VAR)))
|
|
3500
|
+
- id: narvy.python.xss.django-send-mail-html-message-tainted.django-send-mail-html-message-tainted
|
|
3501
|
+
message: Django's `send_mail(...)` is called with `html_message=` built from request data. `html_message`
|
|
3502
|
+
is rendered as raw HTML by the recipient's mail client with no auto-escaping applied (CWE-79, email-delivered
|
|
3503
|
+
XSS). Escape the value with `django.utils.html.escape()` first, or render it through a Django template.
|
|
3504
|
+
severity: WARNING
|
|
3505
|
+
languages:
|
|
3506
|
+
- python
|
|
3507
|
+
metadata:
|
|
3508
|
+
cwe:
|
|
3509
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3510
|
+
owasp:
|
|
3511
|
+
- A03:2021 - Injection
|
|
3512
|
+
category: security
|
|
3513
|
+
subcategory:
|
|
3514
|
+
- vuln
|
|
3515
|
+
technology:
|
|
3516
|
+
- django
|
|
3517
|
+
likelihood: MEDIUM
|
|
3518
|
+
impact: MEDIUM
|
|
3519
|
+
confidence: MEDIUM
|
|
3520
|
+
vulnerability_class:
|
|
3521
|
+
- Cross-Site-Scripting (XSS)
|
|
3522
|
+
patterns:
|
|
3523
|
+
- pattern-either:
|
|
3524
|
+
- pattern: send_mail(..., html_message=request.$KIND.$ATTR(...), ...)
|
|
3525
|
+
- pattern: django.core.mail.send_mail(..., html_message=request.$KIND.$ATTR(...), ...)
|
|
3526
|
+
- pattern: '$BODY = request.$KIND.$ATTR(...)
|
|
3527
|
+
|
|
3528
|
+
...
|
|
3529
|
+
|
|
3530
|
+
send_mail(..., html_message=$BODY, ...)
|
|
3531
|
+
|
|
3532
|
+
'
|
|
3533
|
+
- metavariable-regex:
|
|
3534
|
+
metavariable: $KIND
|
|
3535
|
+
regex: ^(GET|POST|data|query_params)$
|
|
3536
|
+
- id: narvy.python.xss.django-template-tag-autoescape-off.django-template-tag-autoescape-off
|
|
3537
|
+
message: A Django template file contains `{% autoescape off %}`. Every variable rendered inside this
|
|
3538
|
+
block skips Django's automatic HTML escaping until the matching `{% endautoescape %}` (CWE-79). If
|
|
3539
|
+
any variable rendered in that block can contain attacker-controlled text, this is XSS. Remove the
|
|
3540
|
+
block, or wrap only the specific value that is genuinely pre-sanitized HTML with `|safe` / `mark_safe()`
|
|
3541
|
+
in Python instead of disabling escaping for the whole region.
|
|
3542
|
+
severity: WARNING
|
|
3543
|
+
languages:
|
|
3544
|
+
- generic
|
|
3545
|
+
paths:
|
|
3546
|
+
include:
|
|
3547
|
+
- '*.html'
|
|
3548
|
+
metadata:
|
|
3549
|
+
cwe:
|
|
3550
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3551
|
+
owasp:
|
|
3552
|
+
- A03:2021 - Injection
|
|
3553
|
+
category: security
|
|
3554
|
+
subcategory:
|
|
3555
|
+
- vuln
|
|
3556
|
+
technology:
|
|
3557
|
+
- django
|
|
3558
|
+
likelihood: LOW
|
|
3559
|
+
impact: MEDIUM
|
|
3560
|
+
confidence: LOW
|
|
3561
|
+
vulnerability_class:
|
|
3562
|
+
- Cross-Site-Scripting (XSS)
|
|
3563
|
+
pattern-regex: \{%\s*autoescape\s+off\s*%\}
|
|
3564
|
+
- id: narvy.python.xss.django-templates-setting-autoescape-disabled.django-templates-setting-autoescape-disabled
|
|
3565
|
+
message: 'Django''s `TEMPLATES` setting sets `''autoescape'': False` in a backend''s `OPTIONS`. This
|
|
3566
|
+
turns off automatic HTML escaping for every template rendered through this backend project-wide -
|
|
3567
|
+
any template variable that is not manually wrapped in `escape()` becomes an XSS sink (CWE-79). Remove
|
|
3568
|
+
the `autoescape` override (it defaults to `True`), or escape every interpolated value by hand if you
|
|
3569
|
+
truly need it off.'
|
|
3570
|
+
severity: WARNING
|
|
3571
|
+
languages:
|
|
3572
|
+
- python
|
|
3573
|
+
metadata:
|
|
3574
|
+
cwe:
|
|
3575
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3576
|
+
owasp:
|
|
3577
|
+
- A03:2021 - Injection
|
|
3578
|
+
category: security
|
|
3579
|
+
subcategory:
|
|
3580
|
+
- vuln
|
|
3581
|
+
technology:
|
|
3582
|
+
- django
|
|
3583
|
+
likelihood: LOW
|
|
3584
|
+
impact: HIGH
|
|
3585
|
+
confidence: HIGH
|
|
3586
|
+
vulnerability_class:
|
|
3587
|
+
- Cross-Site-Scripting (XSS)
|
|
3588
|
+
patterns:
|
|
3589
|
+
- pattern-either:
|
|
3590
|
+
- pattern: '{..., ''OPTIONS'': {..., ''autoescape'': False, ...}, ...}
|
|
3591
|
+
|
|
3592
|
+
'
|
|
3593
|
+
- pattern: '{..., "OPTIONS": {..., "autoescape": False, ...}, ...}
|
|
3594
|
+
|
|
3595
|
+
'
|
|
3596
|
+
- id: narvy.python.xss.flask-raw-html-response.flask-raw-html-response
|
|
3597
|
+
message: A Flask response is built from a non-literal, non-template-rendered string with an explicit
|
|
3598
|
+
`text/html` mimetype/content_type. Nothing escapes this body before it reaches the browser. If the
|
|
3599
|
+
string contains attacker-controlled data this is reflected XSS (CWE-79). Render through `render_template`
|
|
3600
|
+
(Jinja2 auto-escapes by default) or explicitly escape with `markupsafe.escape()` before concatenation.
|
|
3601
|
+
severity: WARNING
|
|
3602
|
+
languages:
|
|
3603
|
+
- python
|
|
3604
|
+
metadata:
|
|
3605
|
+
cwe:
|
|
3606
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3607
|
+
owasp:
|
|
3608
|
+
- A03:2021 - Injection
|
|
3609
|
+
category: security
|
|
3610
|
+
subcategory:
|
|
3611
|
+
- audit
|
|
3612
|
+
technology:
|
|
3613
|
+
- python
|
|
3614
|
+
- flask
|
|
3615
|
+
likelihood: LOW
|
|
3616
|
+
impact: HIGH
|
|
3617
|
+
confidence: LOW
|
|
3618
|
+
vulnerability_class:
|
|
3619
|
+
- Cross-Site-Scripting (XSS)
|
|
3620
|
+
patterns:
|
|
3621
|
+
- pattern-either:
|
|
3622
|
+
- pattern: Response($BODY, mimetype="text/html")
|
|
3623
|
+
- pattern: Response($BODY, content_type="text/html")
|
|
3624
|
+
- pattern-not: Response("...", mimetype="text/html")
|
|
3625
|
+
- pattern-not: Response("...", content_type="text/html")
|
|
3626
|
+
- pattern-not: Response(render_template(...), mimetype="text/html")
|
|
3627
|
+
- id: narvy.python.xss.flask-response-echoes-request-data.flask-response-echoes-request-data
|
|
3628
|
+
message: A Flask view returns an HTML/text response built by directly interpolating request data (`request.args`,
|
|
3629
|
+
`request.form`, `request.values`, ...) instead of rendering it through Jinja2's auto-escaping `render_template()`.
|
|
3630
|
+
A browser reflecting this response executes any HTML/JS the attacker put in the request value. Reflected
|
|
3631
|
+
XSS (CWE-79). Use `render_template()` with the value passed as a context variable, or `markupsafe.escape()`
|
|
3632
|
+
if you must build the string manually.
|
|
3633
|
+
severity: ERROR
|
|
3634
|
+
languages:
|
|
3635
|
+
- python
|
|
3636
|
+
metadata:
|
|
3637
|
+
cwe:
|
|
3638
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3639
|
+
owasp:
|
|
3640
|
+
- A03:2021 - Injection
|
|
3641
|
+
category: security
|
|
3642
|
+
subcategory:
|
|
3643
|
+
- vuln
|
|
3644
|
+
technology:
|
|
3645
|
+
- flask
|
|
3646
|
+
likelihood: HIGH
|
|
3647
|
+
impact: MEDIUM
|
|
3648
|
+
confidence: MEDIUM
|
|
3649
|
+
vulnerability_class:
|
|
3650
|
+
- Cross-Site-Scripting (XSS)
|
|
3651
|
+
patterns:
|
|
3652
|
+
- pattern-either:
|
|
3653
|
+
- pattern: return f"...{request.$KIND.$ATTR(...)}..."
|
|
3654
|
+
- pattern: return f"...{request.$KIND.$ATTR}..."
|
|
3655
|
+
- pattern: return f"...{request.$KIND[$X]}..."
|
|
3656
|
+
- pattern: return "..." + request.$KIND.$ATTR(...)
|
|
3657
|
+
- pattern: return "...".format(request.$KIND.$ATTR(...))
|
|
3658
|
+
- pattern: return Response(f"...{request.$KIND.$ATTR(...)}...")
|
|
3659
|
+
- pattern: return make_response(f"...{request.$KIND.$ATTR(...)}...")
|
|
3660
|
+
- metavariable-regex:
|
|
3661
|
+
metavariable: $KIND
|
|
3662
|
+
regex: ^(args|form|values|GET|POST|json|cookies|headers)$
|
|
3663
|
+
- id: narvy.python.xss.jinja2-autoescape-disabled.jinja2-autoescape-disabled
|
|
3664
|
+
message: Jinja2 `Environment(...)` is constructed with `autoescape=False`, or omits `autoescape` entirely
|
|
3665
|
+
- which defaults to False for a plain `jinja2.Environment` (unlike Flask's `app.jinja_env`, which
|
|
3666
|
+
turns it on for you). Every variable rendered through templates in this environment is XSS-vulnerable
|
|
3667
|
+
by default (CWE-79). Pass `autoescape=True`, or `select_autoescape([...])` to scope it to HTML/XML
|
|
3668
|
+
template extensions.
|
|
3669
|
+
severity: WARNING
|
|
3670
|
+
languages:
|
|
3671
|
+
- python
|
|
3672
|
+
metadata:
|
|
3673
|
+
cwe:
|
|
3674
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3675
|
+
owasp:
|
|
3676
|
+
- A03:2021 - Injection
|
|
3677
|
+
category: security
|
|
3678
|
+
subcategory:
|
|
3679
|
+
- vuln
|
|
3680
|
+
technology:
|
|
3681
|
+
- jinja2
|
|
3682
|
+
likelihood: MEDIUM
|
|
3683
|
+
impact: MEDIUM
|
|
3684
|
+
confidence: MEDIUM
|
|
3685
|
+
vulnerability_class:
|
|
3686
|
+
- Cross-Site-Scripting (XSS)
|
|
3687
|
+
patterns:
|
|
3688
|
+
- pattern-either:
|
|
3689
|
+
- pattern: jinja2.Environment(..., autoescape=False, ...)
|
|
3690
|
+
- pattern: Environment(..., autoescape=False, ...)
|
|
3691
|
+
- patterns:
|
|
3692
|
+
- pattern-either:
|
|
3693
|
+
- pattern: jinja2.Environment(loader=..., ...)
|
|
3694
|
+
- pattern: Environment(loader=..., ...)
|
|
3695
|
+
- pattern-not: jinja2.Environment(..., autoescape=$AUTOESCAPE, ...)
|
|
3696
|
+
- pattern-not: Environment(..., autoescape=$AUTOESCAPE, ...)
|
|
3697
|
+
- id: narvy.python.xss.markup-wrap-non-literal.markup-wrap-non-literal
|
|
3698
|
+
message: '`Markup()` wraps a non-literal value, marking it as pre-escaped HTML. Jinja2 will render it
|
|
3699
|
+
unescaped. If the value contains attacker-controlled data this is XSS (CWE-79). Only wrap already-sanitized
|
|
3700
|
+
output (e.g. from `bleach.clean()`) with `Markup`, never raw user input.'
|
|
3701
|
+
severity: ERROR
|
|
3702
|
+
languages:
|
|
3703
|
+
- python
|
|
3704
|
+
metadata:
|
|
3705
|
+
cwe:
|
|
3706
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3707
|
+
owasp:
|
|
3708
|
+
- A03:2021 - Injection
|
|
3709
|
+
category: security
|
|
3710
|
+
subcategory:
|
|
3711
|
+
- vuln
|
|
3712
|
+
technology:
|
|
3713
|
+
- python
|
|
3714
|
+
- flask
|
|
3715
|
+
- jinja2
|
|
3716
|
+
- markupsafe
|
|
3717
|
+
likelihood: MEDIUM
|
|
3718
|
+
impact: HIGH
|
|
3719
|
+
confidence: MEDIUM
|
|
3720
|
+
vulnerability_class:
|
|
3721
|
+
- Cross-Site-Scripting (XSS)
|
|
3722
|
+
patterns:
|
|
3723
|
+
- pattern-either:
|
|
3724
|
+
- pattern: Markup($X)
|
|
3725
|
+
- pattern: markupsafe.Markup($X)
|
|
3726
|
+
- pattern: flask.Markup($X)
|
|
3727
|
+
- pattern-not: Markup("...")
|
|
3728
|
+
- pattern-not: markupsafe.Markup("...")
|
|
3729
|
+
- pattern-not: flask.Markup("...")
|
|
3730
|
+
- pattern-not: Markup(bleach.clean(...))
|
|
3731
|
+
- pattern-not: Markup($SANITIZER.clean(...))
|
|
3732
|
+
- id: narvy.python.xss.raw-html-fragment-built-from-request-data.raw-html-fragment-built-from-request-data
|
|
3733
|
+
message: A value derived from the HTTP request flows into a string that is built to look like an HTML
|
|
3734
|
+
fragment (an f-string/%-format/.format() call/concatenation containing a literal `<tag ...>`), without
|
|
3735
|
+
going through an escaping function first. Whoever eventually renders this fragment - a template `{%
|
|
3736
|
+
autoescape off %}` block, a hand-rolled response, a non-Django rendering layer (widgets, table columns,
|
|
3737
|
+
admin actions) - gets the attacker's HTML/JS verbatim. CWE-79. Escape the interpolated value with
|
|
3738
|
+
`django.utils.html.escape()` / `markupsafe.escape()` / `html.escape()` before it goes into the fragment,
|
|
3739
|
+
or build the fragment through `format_html()` (Django) / Jinja2 auto-escaping instead of a raw string
|
|
3740
|
+
op.
|
|
3741
|
+
severity: WARNING
|
|
3742
|
+
languages:
|
|
3743
|
+
- python
|
|
3744
|
+
metadata:
|
|
3745
|
+
cwe:
|
|
3746
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3747
|
+
owasp:
|
|
3748
|
+
- A03:2021 - Injection
|
|
3749
|
+
category: security
|
|
3750
|
+
subcategory:
|
|
3751
|
+
- vuln
|
|
3752
|
+
technology:
|
|
3753
|
+
- python
|
|
3754
|
+
- django
|
|
3755
|
+
- flask
|
|
3756
|
+
likelihood: MEDIUM
|
|
3757
|
+
impact: MEDIUM
|
|
3758
|
+
confidence: LOW
|
|
3759
|
+
vulnerability_class:
|
|
3760
|
+
- Cross-Site-Scripting (XSS)
|
|
3761
|
+
references:
|
|
3762
|
+
- https://cwe.mitre.org/data/definitions/79.html
|
|
3763
|
+
mode: taint
|
|
3764
|
+
pattern-sources:
|
|
3765
|
+
- patterns:
|
|
3766
|
+
- pattern-either:
|
|
3767
|
+
- pattern: request.$KIND.get(...)
|
|
3768
|
+
- pattern: request.$KIND[...]
|
|
3769
|
+
- metavariable-regex:
|
|
3770
|
+
metavariable: $KIND
|
|
3771
|
+
regex: ^(GET|POST|args|form|values|json|COOKIES|META|data)$
|
|
3772
|
+
pattern-sinks:
|
|
3773
|
+
- patterns:
|
|
3774
|
+
- pattern: f"...{$SINK}..."
|
|
3775
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3776
|
+
- patterns:
|
|
3777
|
+
- pattern: '"...%s..." % $SINK'
|
|
3778
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3779
|
+
- patterns:
|
|
3780
|
+
- pattern: '"...%s..." % ($SINK, ...)'
|
|
3781
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3782
|
+
- patterns:
|
|
3783
|
+
- pattern: '"...{}...".format($SINK)'
|
|
3784
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3785
|
+
- patterns:
|
|
3786
|
+
- pattern: '"...{}...".format(..., $SINK, ...)'
|
|
3787
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3788
|
+
- patterns:
|
|
3789
|
+
- pattern: '"..." + $SINK'
|
|
3790
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3791
|
+
- patterns:
|
|
3792
|
+
- pattern: $SINK + "..."
|
|
3793
|
+
- pattern-not-regex: ^((?!<[a-zA-Z]).)*$
|
|
3794
|
+
pattern-sanitizers:
|
|
3795
|
+
- patterns:
|
|
3796
|
+
- pattern: escape(...)
|
|
3797
|
+
- patterns:
|
|
3798
|
+
- pattern: django.utils.html.escape(...)
|
|
3799
|
+
- patterns:
|
|
3800
|
+
- pattern: markupsafe.escape(...)
|
|
3801
|
+
- patterns:
|
|
3802
|
+
- pattern: html.escape(...)
|
|
3803
|
+
- patterns:
|
|
3804
|
+
- pattern: conditional_escape(...)
|
|
3805
|
+
- patterns:
|
|
3806
|
+
- pattern: format_html(...)
|
|
3807
|
+
- id: narvy.python.xss.template-string-render-tainted.render-template-string-tainted
|
|
3808
|
+
message: '`render_template_string()` is called with a template *text* built from request data, rather
|
|
3809
|
+
than a fixed template file. Since the first argument is compiled as a Jinja2 template, an attacker
|
|
3810
|
+
who controls it can inject template syntax (`{{ 7*7 }}`, or worse, `{{ self.__init__.__globals__ ...
|
|
3811
|
+
}}`), which is Server-Side Template Injection - typically full RCE, not just XSS (CWE-94, CWE-79).
|
|
3812
|
+
Never build the template text itself from user input; pass user data only as context variables to
|
|
3813
|
+
a fixed template.'
|
|
3814
|
+
severity: ERROR
|
|
3815
|
+
languages:
|
|
3816
|
+
- python
|
|
3817
|
+
metadata:
|
|
3818
|
+
cwe:
|
|
3819
|
+
- 'CWE-94: Improper Control of Generation of Code (''Code Injection'')'
|
|
3820
|
+
- 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site Scripting'')'
|
|
3821
|
+
owasp:
|
|
3822
|
+
- A03:2021 - Injection
|
|
3823
|
+
category: security
|
|
3824
|
+
subcategory:
|
|
3825
|
+
- vuln
|
|
3826
|
+
technology:
|
|
3827
|
+
- flask
|
|
3828
|
+
- jinja2
|
|
3829
|
+
likelihood: HIGH
|
|
3830
|
+
impact: HIGH
|
|
3831
|
+
confidence: HIGH
|
|
3832
|
+
vulnerability_class:
|
|
3833
|
+
- Cross-Site-Scripting (XSS)
|
|
3834
|
+
- Code Injection
|
|
3835
|
+
patterns:
|
|
3836
|
+
- pattern-either:
|
|
3837
|
+
- pattern: render_template_string(request.$KIND.$ATTR(...))
|
|
3838
|
+
- pattern: render_template_string(f"...{request.$KIND.$ATTR(...)}...")
|
|
3839
|
+
- pattern: render_template_string("..." + request.$KIND.$ATTR(...))
|
|
3840
|
+
- pattern: '$T = request.$KIND.$ATTR(...)
|
|
3841
|
+
|
|
3842
|
+
...
|
|
3843
|
+
|
|
3844
|
+
render_template_string($T)
|
|
3845
|
+
|
|
3846
|
+
'
|
|
3847
|
+
- metavariable-regex:
|
|
3848
|
+
metavariable: $KIND
|
|
3849
|
+
regex: ^(args|form|values|GET|POST|json)$
|
|
3850
|
+
- id: narvy.python.deserialization.jsonpickle-decode-untrusted.jsonpickle-decode-untrusted
|
|
3851
|
+
message: '`jsonpickle.decode()` deserializes data that is not a fixed local trusted source. Unlike plain
|
|
3852
|
+
`json.loads()`, jsonpickle can reconstruct arbitrary Python objects (including calling constructors)
|
|
3853
|
+
from the `py/object` markers in its payload - applying the same object-instantiation RCE risk as `pickle`
|
|
3854
|
+
to data that looks like harmless JSON (CWE-502). Use `json.loads()` for untrusted input unless you
|
|
3855
|
+
specifically need full object round-tripping from a trusted source only.'
|
|
3856
|
+
severity: ERROR
|
|
3857
|
+
languages:
|
|
3858
|
+
- python
|
|
3859
|
+
metadata:
|
|
3860
|
+
cwe:
|
|
3861
|
+
- 'CWE-502: Deserialization of Untrusted Data'
|
|
3862
|
+
owasp:
|
|
3863
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
3864
|
+
category: security
|
|
3865
|
+
subcategory:
|
|
3866
|
+
- vuln
|
|
3867
|
+
technology:
|
|
3868
|
+
- python
|
|
3869
|
+
- jsonpickle
|
|
3870
|
+
likelihood: LOW
|
|
3871
|
+
impact: HIGH
|
|
3872
|
+
confidence: MEDIUM
|
|
3873
|
+
vulnerability_class:
|
|
3874
|
+
- Insecure Deserialization
|
|
3875
|
+
patterns:
|
|
3876
|
+
- pattern: jsonpickle.decode($DATA)
|
|
3877
|
+
- metavariable-regex:
|
|
3878
|
+
metavariable: $DATA
|
|
3879
|
+
regex: (?i)(request|upload|response|body|payload)
|
|
3880
|
+
- id: narvy.python.deserialization.marshal-loads-untrusted.marshal-loads-untrusted
|
|
3881
|
+
message: '`marshal.loads()` deserializes data that is not a fixed local trusted source. `marshal` is
|
|
3882
|
+
the format used for compiled Python bytecode - loading crafted marshal data from an untrusted source
|
|
3883
|
+
is a code-execution vector, and unlike `pickle`, its format is explicitly documented as unstable/unsafe
|
|
3884
|
+
by CPython itself (CWE-502).'
|
|
3885
|
+
severity: ERROR
|
|
3886
|
+
languages:
|
|
3887
|
+
- python
|
|
3888
|
+
metadata:
|
|
3889
|
+
cwe:
|
|
3890
|
+
- 'CWE-502: Deserialization of Untrusted Data'
|
|
3891
|
+
owasp:
|
|
3892
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
3893
|
+
category: security
|
|
3894
|
+
subcategory:
|
|
3895
|
+
- vuln
|
|
3896
|
+
technology:
|
|
3897
|
+
- python
|
|
3898
|
+
likelihood: LOW
|
|
3899
|
+
impact: HIGH
|
|
3900
|
+
confidence: MEDIUM
|
|
3901
|
+
vulnerability_class:
|
|
3902
|
+
- Insecure Deserialization
|
|
3903
|
+
patterns:
|
|
3904
|
+
- pattern: marshal.loads($DATA)
|
|
3905
|
+
- metavariable-regex:
|
|
3906
|
+
metavariable: $DATA
|
|
3907
|
+
regex: (?i)(request|upload|response|body|payload|socket|message|msg)
|
|
3908
|
+
- id: narvy.python.deserialization.pickle-loads-untrusted.pickle-loads-untrusted
|
|
3909
|
+
message: '`pickle.load()`/`pickle.loads()` deserializes data that is not a fixed local trusted file
|
|
3910
|
+
- a request body, an uploaded file, a cache/queue payload, or a network socket read. Unpickling is
|
|
3911
|
+
equivalent to code execution: a crafted pickle stream can call arbitrary callables via `__reduce__`.
|
|
3912
|
+
This is remote code execution (CWE-502), not just a data-integrity issue. Use JSON (or another data-only
|
|
3913
|
+
format) for anything that crosses a trust boundary; if you must use pickle, sign the payload (HMAC)
|
|
3914
|
+
and verify before unpickling.'
|
|
3915
|
+
severity: ERROR
|
|
3916
|
+
languages:
|
|
3917
|
+
- python
|
|
3918
|
+
metadata:
|
|
3919
|
+
cwe:
|
|
3920
|
+
- 'CWE-502: Deserialization of Untrusted Data'
|
|
3921
|
+
owasp:
|
|
3922
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
3923
|
+
category: security
|
|
3924
|
+
subcategory:
|
|
3925
|
+
- vuln
|
|
3926
|
+
technology:
|
|
3927
|
+
- python
|
|
3928
|
+
likelihood: HIGH
|
|
3929
|
+
impact: HIGH
|
|
3930
|
+
confidence: MEDIUM
|
|
3931
|
+
vulnerability_class:
|
|
3932
|
+
- Insecure Deserialization
|
|
3933
|
+
patterns:
|
|
3934
|
+
- pattern-either:
|
|
3935
|
+
- pattern: pickle.load($F)
|
|
3936
|
+
- pattern: pickle.loads($DATA)
|
|
3937
|
+
- pattern: cPickle.loads($DATA)
|
|
3938
|
+
- metavariable-regex:
|
|
3939
|
+
metavariable: $DATA
|
|
3940
|
+
regex: (?i)(request|upload|response|body|payload|socket|redis|celery|queue|message|msg)
|
|
3941
|
+
- id: narvy.python.deserialization.shelve-open-untrusted-path.shelve-open-untrusted-path
|
|
3942
|
+
message: '`shelve.open()` is backed by `pickle` under the hood - reading a shelve database an attacker
|
|
3943
|
+
can influence the path or contents of is the same RCE risk as calling `pickle.load()` directly (CWE-502).
|
|
3944
|
+
Only open shelve files from a fixed, trusted, non-user-writable path.'
|
|
3945
|
+
severity: WARNING
|
|
3946
|
+
languages:
|
|
3947
|
+
- python
|
|
3948
|
+
metadata:
|
|
3949
|
+
cwe:
|
|
3950
|
+
- 'CWE-502: Deserialization of Untrusted Data'
|
|
3951
|
+
owasp:
|
|
3952
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
3953
|
+
category: security
|
|
3954
|
+
subcategory:
|
|
3955
|
+
- vuln
|
|
3956
|
+
technology:
|
|
3957
|
+
- python
|
|
3958
|
+
likelihood: LOW
|
|
3959
|
+
impact: HIGH
|
|
3960
|
+
confidence: LOW
|
|
3961
|
+
vulnerability_class:
|
|
3962
|
+
- Insecure Deserialization
|
|
3963
|
+
patterns:
|
|
3964
|
+
- pattern: shelve.open($PATH)
|
|
3965
|
+
- metavariable-regex:
|
|
3966
|
+
metavariable: $PATH
|
|
3967
|
+
regex: (?i)(request|upload|user|param)
|