seaworthycode 1.1.2 → 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.
Files changed (68) hide show
  1. package/dist/index.js +30 -26
  2. package/dist/index.js.map +1 -1
  3. package/package.json +14 -12
  4. package/rules/external/configuration/insecure-cookie-flags.test.yaml +114 -0
  5. package/rules/external/configuration/insecure-cookie-flags.yaml +77 -0
  6. package/rules/external/configuration/missing-ssl-verification.test.yaml +130 -0
  7. package/rules/external/configuration/missing-ssl-verification.yaml +123 -0
  8. package/rules/external/data-exposure/debug-artifacts.test.yaml +143 -0
  9. package/rules/external/data-exposure/debug-artifacts.yaml +57 -0
  10. package/rules/external/data-exposure/hardcoded-ip.test.yaml +131 -0
  11. package/rules/external/data-exposure/hardcoded-ip.yaml +67 -0
  12. package/rules/external/data-exposure/localstorage-sensitive-data.test.yaml +58 -0
  13. package/rules/external/data-exposure/localstorage-sensitive-data.yaml +60 -0
  14. package/rules/external/dependencies/deprecated-functions.test.yaml +92 -0
  15. package/rules/external/dependencies/deprecated-functions.yaml +66 -0
  16. package/rules/external/ops/hardcoded-port.test.yaml +141 -0
  17. package/rules/external/ops/hardcoded-port.yaml +91 -0
  18. package/rules/external/ops/insecure-file-permissions.test.yaml +111 -0
  19. package/rules/external/ops/insecure-file-permissions.yaml +91 -0
  20. package/rules/external/resilience/missing-memory-limit.test.yaml +54 -0
  21. package/rules/external/resilience/missing-memory-limit.yaml +59 -0
  22. package/rules/external/resilience/missing-timeout-config.test.yaml +179 -0
  23. package/rules/external/resilience/missing-timeout-config.yaml +124 -0
  24. package/rules/external/security/broken-crypto-algorithm.test.yaml +104 -0
  25. package/rules/external/security/broken-crypto-algorithm.yaml +79 -0
  26. package/rules/external/security/http-method-override.test.yaml +40 -0
  27. package/rules/external/security/http-method-override.yaml +35 -0
  28. package/rules/external/security/http-no-tls.test.yaml +115 -0
  29. package/rules/external/security/http-no-tls.yaml +59 -0
  30. package/rules/external/security/path-traversal.test.yaml +188 -0
  31. package/rules/external/security/path-traversal.yaml +210 -0
  32. package/rules/external/security/unsafe-innerhtml.test.yaml +72 -0
  33. package/rules/external/security/unsafe-innerhtml.yaml +46 -0
  34. package/rules/external/security/untrusted-deserialization.test.yaml +79 -0
  35. package/rules/external/security/untrusted-deserialization.yaml +48 -0
  36. package/rules/external/security/weak-encryption-mode.test.yaml +126 -0
  37. package/rules/external/security/weak-encryption-mode.yaml +104 -0
  38. package/rules/external/security/weak-hash-algorithm.test.yaml +121 -0
  39. package/rules/external/security/weak-hash-algorithm.yaml +87 -0
  40. package/rules/external/security/xxe-parsing.test.yaml +76 -0
  41. package/rules/external/security/xxe-parsing.yaml +63 -0
  42. package/rules/internal/security/dangerous-eval.test.yaml +126 -0
  43. package/rules/internal/security/dangerous-eval.yaml +134 -0
  44. package/rules/internal/security/no-document-write.test.yaml +33 -0
  45. package/rules/internal/security/no-document-write.yaml +40 -0
  46. package/rules/internal/taint/MATRIX.md +16 -0
  47. package/rules/internal/taint/bash.test.yaml +84 -0
  48. package/rules/internal/taint/bash.yaml +166 -0
  49. package/rules/internal/taint/dvwa.test.yaml +217 -0
  50. package/rules/internal/taint/go.test.yaml +207 -0
  51. package/rules/internal/taint/go.yaml +325 -0
  52. package/rules/internal/taint/java.test.yaml +158 -0
  53. package/rules/internal/taint/java.yaml +318 -0
  54. package/rules/internal/taint/javascript.test.yaml +374 -0
  55. package/rules/internal/taint/javascript.yaml +439 -0
  56. package/rules/internal/taint/php.test.yaml +134 -0
  57. package/rules/internal/taint/php.yaml +331 -0
  58. package/rules/internal/taint/python.test.yaml +427 -0
  59. package/rules/internal/taint/python.yaml +636 -0
  60. package/rules/internal/taint/ruby.test.yaml +148 -0
  61. package/rules/internal/taint/ruby.yaml +233 -0
  62. package/rules/internal/taint/rust.test.yaml +585 -0
  63. package/rules/internal/taint/rust.yaml +483 -0
  64. package/rules/internal/taint/tsx.test.yaml +140 -0
  65. package/rules/internal/taint/tsx.yaml +51 -0
  66. package/rules/internal/taint/typescript.test.yaml +393 -0
  67. package/rules/internal/taint/typescript.yaml +660 -0
  68. 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
+