seaworthycode 1.1.1 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +269 -59
- package/dist/index.js.map +1 -1
- package/package.json +16 -12
- package/rules/external/configuration/insecure-cookie-flags.test.yaml +114 -0
- package/rules/external/configuration/insecure-cookie-flags.yaml +77 -0
- package/rules/external/configuration/missing-ssl-verification.test.yaml +130 -0
- package/rules/external/configuration/missing-ssl-verification.yaml +123 -0
- package/rules/external/data-exposure/debug-artifacts.test.yaml +143 -0
- package/rules/external/data-exposure/debug-artifacts.yaml +57 -0
- package/rules/external/data-exposure/hardcoded-ip.test.yaml +131 -0
- package/rules/external/data-exposure/hardcoded-ip.yaml +67 -0
- package/rules/external/data-exposure/localstorage-sensitive-data.test.yaml +58 -0
- package/rules/external/data-exposure/localstorage-sensitive-data.yaml +60 -0
- package/rules/external/dependencies/deprecated-functions.test.yaml +92 -0
- package/rules/external/dependencies/deprecated-functions.yaml +66 -0
- package/rules/external/ops/hardcoded-port.test.yaml +141 -0
- package/rules/external/ops/hardcoded-port.yaml +91 -0
- package/rules/external/ops/insecure-file-permissions.test.yaml +111 -0
- package/rules/external/ops/insecure-file-permissions.yaml +91 -0
- package/rules/external/resilience/missing-memory-limit.test.yaml +54 -0
- package/rules/external/resilience/missing-memory-limit.yaml +59 -0
- package/rules/external/resilience/missing-timeout-config.test.yaml +179 -0
- package/rules/external/resilience/missing-timeout-config.yaml +124 -0
- package/rules/external/security/broken-crypto-algorithm.test.yaml +104 -0
- package/rules/external/security/broken-crypto-algorithm.yaml +79 -0
- package/rules/external/security/http-method-override.test.yaml +40 -0
- package/rules/external/security/http-method-override.yaml +35 -0
- package/rules/external/security/http-no-tls.test.yaml +115 -0
- package/rules/external/security/http-no-tls.yaml +59 -0
- package/rules/external/security/path-traversal.test.yaml +188 -0
- package/rules/external/security/path-traversal.yaml +210 -0
- package/rules/external/security/unsafe-innerhtml.test.yaml +72 -0
- package/rules/external/security/unsafe-innerhtml.yaml +46 -0
- package/rules/external/security/untrusted-deserialization.test.yaml +79 -0
- package/rules/external/security/untrusted-deserialization.yaml +48 -0
- package/rules/external/security/weak-encryption-mode.test.yaml +126 -0
- package/rules/external/security/weak-encryption-mode.yaml +104 -0
- package/rules/external/security/weak-hash-algorithm.test.yaml +121 -0
- package/rules/external/security/weak-hash-algorithm.yaml +87 -0
- package/rules/external/security/xxe-parsing.test.yaml +76 -0
- package/rules/external/security/xxe-parsing.yaml +63 -0
- package/rules/internal/security/dangerous-eval.test.yaml +126 -0
- package/rules/internal/security/dangerous-eval.yaml +134 -0
- package/rules/internal/security/no-document-write.test.yaml +33 -0
- package/rules/internal/security/no-document-write.yaml +40 -0
- package/rules/internal/taint/MATRIX.md +16 -0
- package/rules/internal/taint/bash.test.yaml +84 -0
- package/rules/internal/taint/bash.yaml +166 -0
- package/rules/internal/taint/dvwa.test.yaml +217 -0
- package/rules/internal/taint/go.test.yaml +207 -0
- package/rules/internal/taint/go.yaml +325 -0
- package/rules/internal/taint/java.test.yaml +158 -0
- package/rules/internal/taint/java.yaml +318 -0
- package/rules/internal/taint/javascript.test.yaml +374 -0
- package/rules/internal/taint/javascript.yaml +439 -0
- package/rules/internal/taint/php.test.yaml +134 -0
- package/rules/internal/taint/php.yaml +331 -0
- package/rules/internal/taint/python.test.yaml +427 -0
- package/rules/internal/taint/python.yaml +636 -0
- package/rules/internal/taint/ruby.test.yaml +148 -0
- package/rules/internal/taint/ruby.yaml +233 -0
- package/rules/internal/taint/rust.test.yaml +585 -0
- package/rules/internal/taint/rust.yaml +483 -0
- package/rules/internal/taint/tsx.test.yaml +140 -0
- package/rules/internal/taint/tsx.yaml +51 -0
- package/rules/internal/taint/typescript.test.yaml +393 -0
- package/rules/internal/taint/typescript.yaml +660 -0
- package/wasm/tree-sitter-sql.wasm +0 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
language: python
|
|
2
|
+
cases:
|
|
3
|
+
- name: "AC-4.6: XSS flow in Python — request.args to flask.render_template_string"
|
|
4
|
+
language: python
|
|
5
|
+
code: |
|
|
6
|
+
flask.render_template_string(f"Hello {request.args.get('name')}")
|
|
7
|
+
expect:
|
|
8
|
+
count: 1
|
|
9
|
+
flows:
|
|
10
|
+
- sourceId: request.args
|
|
11
|
+
sinkId: flask.render_template_string
|
|
12
|
+
|
|
13
|
+
- name: "AC-4.7: XSS sanitised by html.escape in Python — no finding"
|
|
14
|
+
language: python
|
|
15
|
+
code: |
|
|
16
|
+
flask.render_template_string(html.escape(request.args.get("name")))
|
|
17
|
+
expect:
|
|
18
|
+
count: 0
|
|
19
|
+
|
|
20
|
+
- name: "AC-4.8: html.escape sanitiser does not cover sql-injection"
|
|
21
|
+
language: python
|
|
22
|
+
code: |
|
|
23
|
+
cursor.execute(html.escape(request.args.get("name")))
|
|
24
|
+
expect:
|
|
25
|
+
count: 1
|
|
26
|
+
flows:
|
|
27
|
+
- sourceId: request.args
|
|
28
|
+
sinkId: cursor.execute
|
|
29
|
+
|
|
30
|
+
- name: "SQL injection in Python — request.args to cursor.execute"
|
|
31
|
+
language: python
|
|
32
|
+
code: |
|
|
33
|
+
cursor.execute("SELECT * FROM users WHERE name = " + request.args.get("name"))
|
|
34
|
+
expect:
|
|
35
|
+
count: 1
|
|
36
|
+
flows:
|
|
37
|
+
- sourceId: request.args
|
|
38
|
+
sinkId: cursor.execute
|
|
39
|
+
|
|
40
|
+
- name: "SQL injection sanitised by parameterised query in Python"
|
|
41
|
+
language: python
|
|
42
|
+
code: |
|
|
43
|
+
cursor.execute("SELECT * FROM users WHERE name = ?", [request.args.get("name")])
|
|
44
|
+
expect:
|
|
45
|
+
count: 0
|
|
46
|
+
|
|
47
|
+
- name: "Command injection in Python — sys.argv to os.system"
|
|
48
|
+
language: python
|
|
49
|
+
code: |
|
|
50
|
+
os.system(sys.argv[1])
|
|
51
|
+
expect:
|
|
52
|
+
count: 1
|
|
53
|
+
flows:
|
|
54
|
+
- sourceId: sys.argv
|
|
55
|
+
sinkId: os.system
|
|
56
|
+
|
|
57
|
+
- name: "Path traversal in Python — request.args to open"
|
|
58
|
+
language: python
|
|
59
|
+
code: |
|
|
60
|
+
open(request.args.get("file"))
|
|
61
|
+
expect:
|
|
62
|
+
count: 1
|
|
63
|
+
flows:
|
|
64
|
+
- sourceId: request.args
|
|
65
|
+
sinkId: open-write
|
|
66
|
+
|
|
67
|
+
- name: "Path traversal sanitised by os.path.realpath in Python"
|
|
68
|
+
language: python
|
|
69
|
+
code: |
|
|
70
|
+
open(os.path.realpath(request.args.get("file")))
|
|
71
|
+
expect:
|
|
72
|
+
count: 0
|
|
73
|
+
|
|
74
|
+
- name: "URL injection in Python — request.values to flask.redirect"
|
|
75
|
+
language: python
|
|
76
|
+
code: |
|
|
77
|
+
flask.redirect(request.values.get("next"))
|
|
78
|
+
expect:
|
|
79
|
+
count: 1
|
|
80
|
+
flows:
|
|
81
|
+
- sourceId: request.values
|
|
82
|
+
sinkId: flask.redirect
|
|
83
|
+
|
|
84
|
+
- name: "URL injection sanitised by urllib.parse.quote in Python"
|
|
85
|
+
language: python
|
|
86
|
+
code: |
|
|
87
|
+
flask.redirect(urllib.parse.quote(request.args.get("next")))
|
|
88
|
+
expect:
|
|
89
|
+
count: 0
|
|
90
|
+
|
|
91
|
+
- name: "Prototype pollution via dict.update in Python"
|
|
92
|
+
language: python
|
|
93
|
+
code: |
|
|
94
|
+
config.update(request.json)
|
|
95
|
+
expect:
|
|
96
|
+
count: 1
|
|
97
|
+
flows:
|
|
98
|
+
- sourceId: flask.request.json
|
|
99
|
+
sinkId: dict.update
|
|
100
|
+
|
|
101
|
+
- name: "Prototype pollution sanitised by copy.deepcopy"
|
|
102
|
+
language: python
|
|
103
|
+
code: |
|
|
104
|
+
config.update(copy.deepcopy(request.json))
|
|
105
|
+
expect:
|
|
106
|
+
count: 0
|
|
107
|
+
|
|
108
|
+
- name: "Python — no source match returns zero flows"
|
|
109
|
+
language: python
|
|
110
|
+
code: |
|
|
111
|
+
print("hello world")
|
|
112
|
+
expect:
|
|
113
|
+
count: 0
|
|
114
|
+
|
|
115
|
+
- name: "Python — network response from requests.get to eval"
|
|
116
|
+
language: python
|
|
117
|
+
code: |
|
|
118
|
+
eval(requests.get("https://api.example.com/data").text)
|
|
119
|
+
expect:
|
|
120
|
+
count: 1
|
|
121
|
+
flows:
|
|
122
|
+
- sourceId: requests.response.text
|
|
123
|
+
sinkId: eval
|
|
124
|
+
|
|
125
|
+
- name: "Python — environment variable to subprocess.call"
|
|
126
|
+
language: python
|
|
127
|
+
code: |
|
|
128
|
+
subprocess.call(os.environ.get("CMD"))
|
|
129
|
+
expect:
|
|
130
|
+
count: 1
|
|
131
|
+
flows:
|
|
132
|
+
- sourceId: os.environ.get
|
|
133
|
+
sinkId: subprocess.call
|
|
134
|
+
|
|
135
|
+
- name: "Python — input() to exec is code execution"
|
|
136
|
+
language: python
|
|
137
|
+
code: |
|
|
138
|
+
exec(input())
|
|
139
|
+
expect:
|
|
140
|
+
count: 1
|
|
141
|
+
flows:
|
|
142
|
+
- sourceId: input
|
|
143
|
+
sinkId: exec
|
|
144
|
+
|
|
145
|
+
- name: "Python — django request GET to HttpResponse"
|
|
146
|
+
language: python
|
|
147
|
+
code: |
|
|
148
|
+
HttpResponse("Hello " + request.GET.get("name"))
|
|
149
|
+
expect:
|
|
150
|
+
count: 1
|
|
151
|
+
flows:
|
|
152
|
+
- sourceId: django.request.GET
|
|
153
|
+
sinkId: django.http.HttpResponse
|
|
154
|
+
|
|
155
|
+
- name: "FastAPI: query_params to cursor.execute"
|
|
156
|
+
language: python
|
|
157
|
+
code: |
|
|
158
|
+
cursor.execute("SELECT * FROM users WHERE id = " + request.query_params.get("id"))
|
|
159
|
+
expect:
|
|
160
|
+
count: 1
|
|
161
|
+
flows:
|
|
162
|
+
- sourceId: starlette.request.query_params
|
|
163
|
+
sinkId: cursor.execute
|
|
164
|
+
|
|
165
|
+
- name: "FastAPI: parameterised execute blocks SQLi"
|
|
166
|
+
language: python
|
|
167
|
+
code: |
|
|
168
|
+
cursor.execute("SELECT * FROM users WHERE id = ?", [request.query_params.get("id")])
|
|
169
|
+
expect:
|
|
170
|
+
count: 0
|
|
171
|
+
|
|
172
|
+
- name: "Python safe anti-fixture: escape and parameterised query"
|
|
173
|
+
language: python
|
|
174
|
+
code: |
|
|
175
|
+
safe = html.escape(request.args.get("name"))
|
|
176
|
+
cursor.execute("SELECT * FROM users WHERE name = ?", [request.args.get("name")])
|
|
177
|
+
flask.render_template_string(safe)
|
|
178
|
+
expect:
|
|
179
|
+
count: 0
|
|
180
|
+
|
|
181
|
+
- name: "Python — request cookies to pickle.loads (code execution)"
|
|
182
|
+
language: python
|
|
183
|
+
code: |
|
|
184
|
+
pickle.loads(request.cookies.get("session"))
|
|
185
|
+
expect:
|
|
186
|
+
count: 1
|
|
187
|
+
flows:
|
|
188
|
+
- sourceId: request.cookies
|
|
189
|
+
sinkId: pickle.loads
|
|
190
|
+
|
|
191
|
+
- name: "Python — Django cookies to yaml.unsafe_load (code execution)"
|
|
192
|
+
language: python
|
|
193
|
+
code: |
|
|
194
|
+
yaml.unsafe_load(request.COOKIES.get("prefs"))
|
|
195
|
+
expect:
|
|
196
|
+
count: 2
|
|
197
|
+
flows:
|
|
198
|
+
- sourceId: request.cookies
|
|
199
|
+
sinkId: yaml.unsafe_load
|
|
200
|
+
- sourceId: django.request.COOKIES
|
|
201
|
+
sinkId: yaml.unsafe_load
|
|
202
|
+
|
|
203
|
+
- name: "Python — request headers to starlette.RedirectResponse (open redirect)"
|
|
204
|
+
language: python
|
|
205
|
+
code: |
|
|
206
|
+
RedirectResponse(request.headers.get("referrer"))
|
|
207
|
+
expect:
|
|
208
|
+
count: 1
|
|
209
|
+
flows:
|
|
210
|
+
- sourceId: request.headers
|
|
211
|
+
sinkId: starlette.RedirectResponse
|
|
212
|
+
|
|
213
|
+
- name: "Python — Flask headers to Starlette HTMLResponse (XSS)"
|
|
214
|
+
language: python
|
|
215
|
+
code: |
|
|
216
|
+
HTMLResponse("Hello " + request.headers.get("name"))
|
|
217
|
+
expect:
|
|
218
|
+
count: 1
|
|
219
|
+
flows:
|
|
220
|
+
- sourceId: request.headers
|
|
221
|
+
sinkId: starlette.HTMLResponse
|
|
222
|
+
|
|
223
|
+
- name: "Python — Flask args to Jinja2 Template render (XSS)"
|
|
224
|
+
language: python
|
|
225
|
+
code: |
|
|
226
|
+
Template("Hello " + request.args.get("name")).render()
|
|
227
|
+
expect:
|
|
228
|
+
count: 1
|
|
229
|
+
flows:
|
|
230
|
+
- sourceId: request.args
|
|
231
|
+
sinkId: jinja2.Template
|
|
232
|
+
|
|
233
|
+
- name: "Python — Django request GET to Django .raw() SQL query"
|
|
234
|
+
language: python
|
|
235
|
+
code: |
|
|
236
|
+
User.objects.raw("SELECT * FROM users WHERE name = '" + request.GET.get("name") + "'")
|
|
237
|
+
expect:
|
|
238
|
+
count: 1
|
|
239
|
+
flows:
|
|
240
|
+
- sourceId: django.request.GET
|
|
241
|
+
sinkId: django.db.models.raw
|
|
242
|
+
|
|
243
|
+
- name: "Python — Django request POST to RawSQL expression"
|
|
244
|
+
language: python
|
|
245
|
+
code: |
|
|
246
|
+
User.objects.annotate(val=RawSQL("SELECT '" + request.POST.get("role") + "'", []))
|
|
247
|
+
expect:
|
|
248
|
+
count: 1
|
|
249
|
+
flows:
|
|
250
|
+
- sourceId: django.request.POST
|
|
251
|
+
sinkId: django.db.models.expressions.RawSQL
|
|
252
|
+
|
|
253
|
+
- name: "Python BFS: f-string interpolation propagates taint to sink"
|
|
254
|
+
language: python
|
|
255
|
+
code: |
|
|
256
|
+
cursor.execute(f"SELECT * FROM users WHERE name = {request.args.get('name')}")
|
|
257
|
+
expect:
|
|
258
|
+
count: 1
|
|
259
|
+
flows:
|
|
260
|
+
- sourceId: request.args
|
|
261
|
+
sinkId: cursor.execute
|
|
262
|
+
|
|
263
|
+
- name: "Python BFS: f-string interpolation sanitised by parameterised query"
|
|
264
|
+
language: python
|
|
265
|
+
code: |
|
|
266
|
+
name = html.escape(request.args.get('name'))
|
|
267
|
+
cursor.execute(f"SELECT * FROM users WHERE name = {name}")
|
|
268
|
+
expect:
|
|
269
|
+
count: 1
|
|
270
|
+
flows:
|
|
271
|
+
- sourceId: request.args
|
|
272
|
+
sinkId: cursor.execute
|
|
273
|
+
|
|
274
|
+
- name: "Python BFS: list comprehension propagates taint from source collection"
|
|
275
|
+
language: python
|
|
276
|
+
code: |
|
|
277
|
+
os.system([x for x in request.args.getlist('name')][0])
|
|
278
|
+
expect:
|
|
279
|
+
count: 1
|
|
280
|
+
flows:
|
|
281
|
+
- sourceId: request.args
|
|
282
|
+
sinkId: os.system
|
|
283
|
+
|
|
284
|
+
- name: "Python BFS: dict comprehension propagates taint"
|
|
285
|
+
language: python
|
|
286
|
+
code: |
|
|
287
|
+
cursor.execute({k: v for k, v in request.args.items()}.get('q'))
|
|
288
|
+
expect:
|
|
289
|
+
count: 1
|
|
290
|
+
flows:
|
|
291
|
+
- sourceId: request.args
|
|
292
|
+
sinkId: cursor.execute
|
|
293
|
+
|
|
294
|
+
- name: "Python BFS: starred expression propagates taint through *args"
|
|
295
|
+
language: python
|
|
296
|
+
code: |
|
|
297
|
+
os.system(*request.args.getlist('cmd'))
|
|
298
|
+
expect:
|
|
299
|
+
count: 1
|
|
300
|
+
flows:
|
|
301
|
+
- sourceId: request.args
|
|
302
|
+
sinkId: os.system
|
|
303
|
+
|
|
304
|
+
- name: "Python BFS: **kwargs splat propagates taint"
|
|
305
|
+
language: python
|
|
306
|
+
code: |
|
|
307
|
+
config.update(**request.json)
|
|
308
|
+
expect:
|
|
309
|
+
count: 1
|
|
310
|
+
flows:
|
|
311
|
+
- sourceId: flask.request.json
|
|
312
|
+
sinkId: dict.update
|
|
313
|
+
|
|
314
|
+
- name: "Python BFS: subscript assignment taints container object"
|
|
315
|
+
language: python
|
|
316
|
+
code: |
|
|
317
|
+
data = {}
|
|
318
|
+
data['key'] = request.args.get('name')
|
|
319
|
+
cursor.execute(data['key'])
|
|
320
|
+
expect:
|
|
321
|
+
count: 0
|
|
322
|
+
|
|
323
|
+
- name: "Python BFS: attribute assignment taints container object"
|
|
324
|
+
language: python
|
|
325
|
+
code: |
|
|
326
|
+
class Obj:
|
|
327
|
+
pass
|
|
328
|
+
obj = Obj()
|
|
329
|
+
obj.name = request.args.get('name')
|
|
330
|
+
cursor.execute(obj.name)
|
|
331
|
+
expect:
|
|
332
|
+
count: 1
|
|
333
|
+
flows:
|
|
334
|
+
- sourceId: request.args
|
|
335
|
+
sinkId: cursor.execute
|
|
336
|
+
|
|
337
|
+
- name: "Set comprehension BFS: taint propagates through set comprehension"
|
|
338
|
+
language: python
|
|
339
|
+
code: |
|
|
340
|
+
os.system({x for x in request.args.getlist('name')}.pop())
|
|
341
|
+
expect:
|
|
342
|
+
count: 1
|
|
343
|
+
flows:
|
|
344
|
+
- sourceId: request.args
|
|
345
|
+
sinkId: os.system
|
|
346
|
+
|
|
347
|
+
- name: "Augmented assignment BFS: taint via += propagates to sink"
|
|
348
|
+
language: python
|
|
349
|
+
code: |
|
|
350
|
+
name = ""
|
|
351
|
+
name += request.args.get("name")
|
|
352
|
+
cursor.execute(name)
|
|
353
|
+
expect:
|
|
354
|
+
count: 1
|
|
355
|
+
flows:
|
|
356
|
+
- sourceId: request.args
|
|
357
|
+
sinkId: cursor.execute
|
|
358
|
+
|
|
359
|
+
- name: "Mutator method BFS: taint via .append() to list then sink"
|
|
360
|
+
language: python
|
|
361
|
+
code: |
|
|
362
|
+
data = []
|
|
363
|
+
data.append(request.args.get("name"))
|
|
364
|
+
cursor.execute(data[0])
|
|
365
|
+
expect:
|
|
366
|
+
count: 1
|
|
367
|
+
flows:
|
|
368
|
+
- sourceId: request.args
|
|
369
|
+
sinkId: cursor.execute
|
|
370
|
+
|
|
371
|
+
- name: "Inter-function propagation: tainted arg -> return -> sink"
|
|
372
|
+
language: python
|
|
373
|
+
code: |
|
|
374
|
+
def process(value):
|
|
375
|
+
return value
|
|
376
|
+
|
|
377
|
+
name = process(request.args.get("name"))
|
|
378
|
+
cursor.execute(name)
|
|
379
|
+
expect:
|
|
380
|
+
count: 1
|
|
381
|
+
flows:
|
|
382
|
+
- sourceId: request.args
|
|
383
|
+
sinkId: cursor.execute
|
|
384
|
+
|
|
385
|
+
- name: "Return-value propagation: function returns tainted value to sink"
|
|
386
|
+
language: python
|
|
387
|
+
code: |
|
|
388
|
+
def get_name():
|
|
389
|
+
return request.args.get("name")
|
|
390
|
+
|
|
391
|
+
cursor.execute(get_name())
|
|
392
|
+
expect:
|
|
393
|
+
count: 1
|
|
394
|
+
flows:
|
|
395
|
+
- sourceId: request.args
|
|
396
|
+
sinkId: cursor.execute
|
|
397
|
+
|
|
398
|
+
- name: "Conditional branch merging: taint in if/else merges before sink"
|
|
399
|
+
language: python
|
|
400
|
+
code: |
|
|
401
|
+
if True:
|
|
402
|
+
name = request.args.get("name")
|
|
403
|
+
else:
|
|
404
|
+
name = "safe"
|
|
405
|
+
|
|
406
|
+
cursor.execute(name)
|
|
407
|
+
expect:
|
|
408
|
+
count: 1
|
|
409
|
+
flows:
|
|
410
|
+
- sourceId: request.args
|
|
411
|
+
sinkId: cursor.execute
|
|
412
|
+
|
|
413
|
+
- name: "Loop fixpoint: taint introduced inside loop body reaches sink after loop"
|
|
414
|
+
language: python
|
|
415
|
+
code: |
|
|
416
|
+
result = ""
|
|
417
|
+
for x in request.args.getlist("id"):
|
|
418
|
+
result = x
|
|
419
|
+
|
|
420
|
+
cursor.execute(result)
|
|
421
|
+
expect:
|
|
422
|
+
count: 1
|
|
423
|
+
flows:
|
|
424
|
+
- sourceId: request.args
|
|
425
|
+
sinkId: cursor.execute
|
|
426
|
+
|
|
427
|
+
|