seaworthycode 1.1.2 → 1.2.3

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 +739 -42
  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,318 @@
1
+ # Prototype-pollution is N/A — Java has no prototype chain.
2
+ # attribution: CWE-79
3
+ attribution: CWE-79
4
+ language: java
5
+ applies-to: []
6
+
7
+ sources:
8
+ - id: servlet.request.getParameter
9
+ class: http-params
10
+ query: |
11
+ (method_invocation
12
+ name: (identifier) @method
13
+ (#match? @method "^(getParameter|getParameterValues|getQueryString)$")
14
+ arguments: (argument_list)) @source
15
+
16
+ - id: servlet.request.getHeader
17
+ class: header
18
+ query: |
19
+ (method_invocation
20
+ name: (identifier) @method
21
+ (#match? @method "^(getHeader|getHeaders)$")
22
+ arguments: (argument_list)) @source
23
+
24
+ - id: servlet.request.body
25
+ class: http-body
26
+ query: |
27
+ (method_invocation
28
+ name: (identifier) @method
29
+ (#match? @method "^(getInputStream|getReader)$")
30
+ arguments: (argument_list)) @source
31
+
32
+ - id: servlet.request.getCookies
33
+ class: cookie
34
+ query: |
35
+ (method_invocation
36
+ name: (identifier) @method
37
+ (#eq? @method "getCookies")
38
+ arguments: (argument_list)) @source
39
+
40
+ - id: spring.requestParam
41
+ class: http-params
42
+ query: |
43
+ (formal_parameter
44
+ (modifiers
45
+ [
46
+ (marker_annotation name: (identifier) @ann)
47
+ (annotation name: (identifier) @ann)
48
+ ]
49
+ (#match? @ann "^(RequestParam|PathVariable)$"))
50
+ name: (identifier) @source)
51
+
52
+ - id: spring.requestBody
53
+ class: http-body
54
+ query: |
55
+ (formal_parameter
56
+ (modifiers
57
+ [
58
+ (marker_annotation name: (identifier) @ann)
59
+ (annotation name: (identifier) @ann)
60
+ ]
61
+ (#eq? @ann "RequestBody"))
62
+ name: (identifier) @source)
63
+
64
+ - id: spring.requestHeader
65
+ class: header
66
+ query: |
67
+ (formal_parameter
68
+ (modifiers
69
+ [
70
+ (marker_annotation name: (identifier) @ann)
71
+ (annotation name: (identifier) @ann)
72
+ ]
73
+ (#eq? @ann "RequestHeader"))
74
+ name: (identifier) @source)
75
+
76
+ - id: spring.cookieValue
77
+ class: cookie
78
+ query: |
79
+ (formal_parameter
80
+ (modifiers
81
+ [
82
+ (marker_annotation name: (identifier) @ann)
83
+ (annotation name: (identifier) @ann)
84
+ ]
85
+ (#eq? @ann "CookieValue"))
86
+ name: (identifier) @source)
87
+
88
+ - id: java.System.getenv
89
+ class: env-var
90
+ query: |
91
+ (method_invocation
92
+ object: (identifier) @obj
93
+ name: (identifier) @method
94
+ (#eq? @obj "System")
95
+ (#eq? @method "getenv")
96
+ arguments: (argument_list)) @source
97
+
98
+ - id: java.System.getProperty
99
+ class: env-var
100
+ query: |
101
+ (method_invocation
102
+ object: (identifier) @obj
103
+ name: (identifier) @method
104
+ (#eq? @obj "System")
105
+ (#eq? @method "getProperty")
106
+ arguments: (argument_list)) @source
107
+
108
+ - id: java.main.args
109
+ class: process-argv
110
+ query: |
111
+ (formal_parameter
112
+ type: (array_type (type_identifier) @t (#eq? @t "String"))
113
+ name: (identifier) @source
114
+ (#eq? @source "args"))
115
+
116
+ - id: spring.value
117
+ class: env-var
118
+ query: |
119
+ (formal_parameter
120
+ (modifiers
121
+ [
122
+ (marker_annotation name: (identifier) @ann)
123
+ (annotation name: (identifier) @ann)
124
+ ]
125
+ (#eq? @ann "Value"))
126
+ name: (identifier) @source)
127
+
128
+ - id: spring.multipartFile
129
+ class: http-body
130
+ query: |
131
+ (formal_parameter
132
+ type: (type_identifier) @t
133
+ name: (identifier) @source
134
+ (#eq? @t "MultipartFile"))
135
+
136
+ sinks:
137
+ - id: java.Statement.execute
138
+ sink-class: sql-injection
139
+ query: |
140
+ (method_invocation
141
+ name: (identifier) @method
142
+ (#match? @method "^execute.*$")
143
+ arguments: (argument_list) @sink) @match
144
+
145
+ - id: java.Connection.prepareStatement
146
+ sink-class: sql-injection
147
+ query: |
148
+ (method_invocation
149
+ name: (identifier) @method
150
+ (#eq? @method "prepareStatement")
151
+ arguments: (argument_list) @sink) @match
152
+
153
+ - id: java.JdbcTemplate.query
154
+ sink-class: sql-injection
155
+ query: |
156
+ (method_invocation
157
+ name: (identifier) @method
158
+ (#match? @method "^(query|update|execute)$")
159
+ arguments: (argument_list) @sink) @match
160
+
161
+ - id: java.hibernate.createQuery
162
+ sink-class: sql-injection
163
+ query: |
164
+ (method_invocation
165
+ name: (identifier) @method
166
+ (#match? @method "^(createQuery|createNativeQuery)$")
167
+ arguments: (argument_list) @sink) @match
168
+
169
+ - id: java.Runtime.exec
170
+ sink-class: command-injection
171
+ query: |
172
+ (method_invocation
173
+ object: (method_invocation
174
+ object: (identifier) @rt
175
+ name: (identifier) @gr
176
+ (#eq? @rt "Runtime")
177
+ (#eq? @gr "getRuntime"))
178
+ name: (identifier) @method
179
+ (#eq? @method "exec")
180
+ arguments: (argument_list) @sink) @match
181
+
182
+ - id: java.ProcessBuilder
183
+ sink-class: command-injection
184
+ query: |
185
+ [
186
+ (object_creation_expression
187
+ type: (type_identifier) @t
188
+ (#eq? @t "ProcessBuilder")
189
+ arguments: (argument_list) @sink)
190
+ (object_creation_expression
191
+ type: (scoped_type_identifier) @type
192
+ (#match? @type "ProcessBuilder$")
193
+ arguments: (argument_list) @sink)
194
+ (method_invocation
195
+ object: (identifier) @obj
196
+ name: (identifier) @method
197
+ (#eq? @obj "ProcessBuilder")
198
+ (#eq? @method "command")
199
+ arguments: (argument_list) @sink)
200
+ ] @match
201
+
202
+ - id: java.ScriptEngine.eval
203
+ sink-class: code-execution
204
+ query: |
205
+ (method_invocation
206
+ name: (identifier) @method
207
+ (#eq? @method "eval")
208
+ arguments: (argument_list) @sink) @match
209
+
210
+ - id: java.response.print
211
+ sink-class: xss
212
+ query: |
213
+ (method_invocation
214
+ name: (identifier) @method
215
+ (#match? @method "^(print|println|printf|write|append)$")
216
+ arguments: (argument_list) @sink) @match
217
+
218
+ - id: java.File.constructor
219
+ sink-class: path-traversal
220
+ query: |
221
+ (object_creation_expression
222
+ type: (type_identifier) @t
223
+ (#match? @t "^(File|FileInputStream)$")
224
+ arguments: (argument_list) @sink) @match
225
+
226
+ - id: java.File.constructorScoped
227
+ sink-class: path-traversal
228
+ query: |
229
+ (object_creation_expression
230
+ type: (scoped_type_identifier) @type
231
+ (#match? @type "(File|FileInputStream)$")
232
+ arguments: (argument_list) @sink) @match
233
+
234
+ - id: java.Paths.get
235
+ sink-class: path-traversal
236
+ query: |
237
+ (method_invocation
238
+ object: (identifier) @obj
239
+ name: (identifier) @method
240
+ (#eq? @obj "Paths")
241
+ (#eq? @method "get")
242
+ arguments: (argument_list) @sink) @match
243
+
244
+ - id: java.Files.read
245
+ sink-class: path-traversal
246
+ query: |
247
+ (method_invocation
248
+ object: (identifier) @obj
249
+ name: (identifier) @method
250
+ (#eq? @obj "Files")
251
+ (#match? @method "^(readAllBytes|readString|readAllLines|newInputStream|newBufferedReader)$")
252
+ arguments: (argument_list) @sink) @match
253
+
254
+ - id: java.response.sendRedirect
255
+ sink-class: url-injection
256
+ query: |
257
+ (method_invocation
258
+ name: (identifier) @method
259
+ (#eq? @method "sendRedirect")
260
+ arguments: (argument_list) @sink) @match
261
+
262
+ - id: java.response.locationHeader
263
+ sink-class: url-injection
264
+ query: |
265
+ (method_invocation
266
+ name: (identifier) @method
267
+ (#eq? @method "setHeader")
268
+ arguments: (argument_list
269
+ (string_literal (string_fragment) @h)
270
+ (#eq? @h "Location")
271
+ (_) @sink)) @match
272
+
273
+ sanitisers:
274
+ - id: java.PreparedStatement.bind
275
+ sanitises: [sql-injection]
276
+ # Note: A full parameter binding dataflow (from conn.prepareStatement(tainted)
277
+ # to ps.setXxx(receiver, index, value) to ps.execute()) is not tracked at a variable
278
+ # level in this simple pattern matcher. We assume setXxx/setObject/setNull calls
279
+ # are safe and don't propagate taint to sinks. This sanitiser is intentionally
280
+ # matching Statement-style execute calls where the query is a string literal,
281
+ # as execute(string_literal) is safe from SQL injection.
282
+ query: |
283
+ (method_invocation
284
+ name: (identifier) @method
285
+ (#match? @method "^execute.*$")
286
+ arguments: (argument_list
287
+ (string_literal)
288
+ (_))) @sanitiser
289
+
290
+ - id: java.HtmlUtils.escape
291
+ sanitises: [xss]
292
+ autofix-safety: requires-import
293
+ query: |
294
+ (method_invocation
295
+ name: (identifier) @method
296
+ (#match? @method "^(htmlEscape|escapeHtml)$")
297
+ arguments: (argument_list (_) @arg)) @sanitiser
298
+
299
+ - id: java.Integer.parseInt
300
+ sanitises: [xss, sql-injection, command-injection, path-traversal, url-injection]
301
+ query: |
302
+ (method_invocation
303
+ object: (identifier) @obj
304
+ name: (identifier) @method
305
+ (#match? @obj "^(Integer|Long)$")
306
+ (#match? @method "^(parseInt|parseLong)$")
307
+ arguments: (argument_list (_) @arg)) @sanitiser
308
+
309
+
310
+
311
+ - id: java.UriUtils.encode
312
+ sanitises: [url-injection]
313
+ autofix-safety: requires-import
314
+ query: |
315
+ (method_invocation
316
+ name: (identifier) @method
317
+ (#match? @method "^(encode|encodePath|encodePathSegment)$")
318
+ arguments: (argument_list (_) @arg)) @sanitiser
@@ -0,0 +1,374 @@
1
+ language: javascript
2
+ cases:
3
+ - name: "AC-4.6: direct XSS flow from req.body to res.send"
4
+ language: javascript
5
+ code: |
6
+ const x = req.body;
7
+ res.send(x);
8
+ expect:
9
+ count: 1
10
+ flows:
11
+ - sourceId: req.body
12
+ sinkId: res.send
13
+
14
+ - name: "AC-4.7: XSS sanitised by encodeURIComponent — no finding"
15
+ language: javascript
16
+ code: |
17
+ res.send(encodeURIComponent(req.body));
18
+ expect:
19
+ count: 0
20
+
21
+ - name: "AC-4.8: encodeURIComponent sanitiser does not cover sql-injection"
22
+ language: javascript
23
+ code: |
24
+ db.query(encodeURIComponent(req.body));
25
+ expect:
26
+ count: 1
27
+ flows:
28
+ - sourceId: req.body
29
+ sinkId: db.query
30
+
31
+ - name: "SQL injection from req.query to db.query"
32
+ language: javascript
33
+ code: |
34
+ const q = req.query.id;
35
+ db.query("SELECT * FROM users WHERE id = " + q);
36
+ expect:
37
+ count: 1
38
+ flows:
39
+ - sourceId: req.query
40
+ sinkId: db.query
41
+
42
+ - name: "SQL injection sanitised by parameterised query"
43
+ language: javascript
44
+ code: |
45
+ db.query("SELECT * FROM users WHERE id = ?", [req.query.id]);
46
+ expect:
47
+ count: 0
48
+
49
+ - name: "Sequelize query with req.query criteria"
50
+ language: javascript
51
+ code: |
52
+ const criteria = req.query.q;
53
+ models.sequelize.query(`SELECT * FROM Products WHERE name LIKE '%${criteria}%'`);
54
+ expect:
55
+ count: 2
56
+ flows:
57
+ - sourceId: req.query
58
+ sinkId: sequelize.query
59
+ - sourceId: req.query
60
+ sinkId: template-literal-sql
61
+
62
+ - name: "Safe parameterised Sequelize-style query"
63
+ language: javascript
64
+ code: |
65
+ models.sequelize.query("SELECT * FROM Products WHERE id = ?", [req.query.id]);
66
+ expect:
67
+ count: 0
68
+
69
+ - name: "URL injection from req.query to res.redirect"
70
+ language: javascript
71
+ code: |
72
+ res.redirect(req.query.next);
73
+ expect:
74
+ count: 1
75
+ flows:
76
+ - sourceId: req.query
77
+ sinkId: res.redirect
78
+
79
+ - name: "Command injection from process.argv to child_process.exec"
80
+ language: javascript
81
+ code: |
82
+ child_process.exec(process.argv[2]);
83
+ expect:
84
+ count: 1
85
+ flows:
86
+ - sourceId: process.argv
87
+ sinkId: child_process.exec
88
+
89
+ - name: "Path traversal from req.params to fs.readFile"
90
+ language: javascript
91
+ code: |
92
+ fs.readFile(req.params.file);
93
+ expect:
94
+ count: 1
95
+ flows:
96
+ - sourceId: req.params
97
+ sinkId: fs.readFile-path
98
+
99
+ - name: "Path traversal sanitised by path.resolve"
100
+ language: javascript
101
+ code: |
102
+ fs.readFile(path.resolve("/safe/root", req.params.file));
103
+ expect:
104
+ count: 0
105
+
106
+ - name: "Safe fs.writeFile with literal path — zero flows"
107
+ language: javascript
108
+ code: |
109
+ fs.writeFile('./data/app-config.json', 'static');
110
+ expect:
111
+ count: 0
112
+
113
+ - name: "Juice Shop-style codingChallenges read — zero flows"
114
+ language: javascript
115
+ code: |
116
+ const fs = require('fs');
117
+ fs.readFile('./data/codingchallenges/readme.md');
118
+ expect:
119
+ count: 0
120
+
121
+ - name: "Path traversal from req.params to res.sendFile"
122
+ language: javascript
123
+ code: |
124
+ res.sendFile(req.params.file);
125
+ expect:
126
+ count: 1
127
+ flows:
128
+ - sourceId: req.params
129
+ sinkId: res.sendFile
130
+
131
+ - name: "No source match — zero flows"
132
+ language: javascript
133
+ code: |
134
+ console.log("hello world");
135
+ expect:
136
+ count: 0
137
+
138
+ - name: "Function call stops propagation"
139
+ language: javascript
140
+ code: |
141
+ const x = req.body;
142
+ helper(x);
143
+ eval(x);
144
+ expect:
145
+ count: 1
146
+ flows:
147
+ - sourceId: req.body
148
+ sinkId: eval
149
+
150
+ - name: "Template literal propagation to eval"
151
+ language: javascript
152
+ code: |
153
+ eval(`const data = ${req.body}; process(data)`);
154
+ expect:
155
+ count: 1
156
+ flows:
157
+ - sourceId: req.body
158
+ sinkId: eval
159
+
160
+ - name: "Array mutation propagation"
161
+ language: javascript
162
+ code: |
163
+ const arr = [];
164
+ arr.push(req.body);
165
+ eval(arr[0]);
166
+ expect:
167
+ count: 1
168
+ flows:
169
+ - sourceId: req.body
170
+ sinkId: eval
171
+
172
+ - name: "Object mutation propagation"
173
+ language: javascript
174
+ code: |
175
+ const obj = {};
176
+ obj.data = req.body;
177
+ eval(obj.data);
178
+ expect:
179
+ count: 1
180
+ flows:
181
+ - sourceId: req.body
182
+ sinkId: eval
183
+
184
+ - name: "InnerHTML XSS from env var"
185
+ language: javascript
186
+ code: |
187
+ document.getElementById("output").innerHTML = process.env.USER_INPUT;
188
+ expect:
189
+ count: 1
190
+ flows:
191
+ - sourceId: process.env
192
+ sinkId: innerHTML
193
+
194
+ - name: "JSON.stringify sanitises XSS"
195
+ language: javascript
196
+ code: |
197
+ res.send(JSON.stringify(req.body));
198
+ expect:
199
+ count: 0
200
+
201
+ - name: "Multiple flows from same source to different sinks"
202
+ language: javascript
203
+ code: |
204
+ const x = req.body;
205
+ res.send(x);
206
+ db.query(x);
207
+ expect:
208
+ count: 2
209
+ flows:
210
+ - sourceId: req.body
211
+ sinkId: res.send
212
+ - sourceId: req.body
213
+ sinkId: db.query
214
+
215
+ - name: "URL injection from process.env to res.redirect"
216
+ language: javascript
217
+ code: |
218
+ res.redirect(process.env.REDIRECT_URL);
219
+ expect:
220
+ count: 1
221
+ flows:
222
+ - sourceId: process.env
223
+ sinkId: res.redirect
224
+
225
+ - name: "URL injection sanitised by encodeURIComponent"
226
+ language: javascript
227
+ code: |
228
+ res.redirect(encodeURIComponent(req.query.redirect));
229
+ expect:
230
+ count: 0
231
+
232
+ - name: "Prototype pollution with Object.assign"
233
+ language: javascript
234
+ code: |
235
+ const target = {};
236
+ Object.assign(target, req.body);
237
+ expect:
238
+ count: 1
239
+ flows:
240
+ - sourceId: req.body
241
+ sinkId: Object.assign-pollution
242
+
243
+ - name: "Prototype pollution sanitised by Object.freeze"
244
+ language: javascript
245
+ code: |
246
+ const target = {};
247
+ Object.assign(target, Object.freeze(req.body));
248
+ expect:
249
+ count: 0
250
+
251
+ - name: "Branch join — taint on one branch"
252
+ language: javascript
253
+ code: |
254
+ let y;
255
+ if (cond) { y = req.body; } else { y = "safe"; }
256
+ eval(y);
257
+ expect:
258
+ count: 1
259
+ flows:
260
+ - sourceId: req.body
261
+ sinkId: eval
262
+
263
+ - name: "Loop handling — assignment inside loop"
264
+ language: javascript
265
+ code: |
266
+ let x;
267
+ for (let i = 0; i < 10; i++) { x = req.body; }
268
+ eval(x);
269
+ expect:
270
+ count: 1
271
+ flows:
272
+ - sourceId: req.body
273
+ sinkId: eval
274
+
275
+ - name: "AC-10.5: inter-function taint — helper with sink"
276
+ language: javascript
277
+ code: |
278
+ let x = req.body;
279
+ helper(x);
280
+ function helper(data) { eval(data); }
281
+ expect:
282
+ count: 1
283
+ flows:
284
+ - sourceId: req.body
285
+ sinkId: eval
286
+
287
+ - name: "AC-10.5: inter-function taint — return value propagation"
288
+ language: javascript
289
+ code: |
290
+ let x = req.body;
291
+ let y = wrapper(x);
292
+ eval(y);
293
+ function wrapper(data) { return data; }
294
+ expect:
295
+ count: 1
296
+ flows:
297
+ - sourceId: req.body
298
+ sinkId: eval
299
+
300
+ - name: "API XSS from req.body to res.json"
301
+ language: javascript
302
+ code: |
303
+ res.json(req.body.label);
304
+ expect:
305
+ count: 1
306
+ flows:
307
+ - sourceId: req.body
308
+ sinkId: res.json
309
+
310
+ - name: "API XSS safe — res.json with literal object"
311
+ language: javascript
312
+ code: |
313
+ res.json({ ok: true });
314
+ expect:
315
+ count: 0
316
+
317
+ - name: "Destructuring SQLi from req.query to db.query"
318
+ language: javascript
319
+ code: |
320
+ const { id } = req.query;
321
+ db.query("SELECT * FROM users WHERE id = " + id);
322
+ expect:
323
+ count: 1
324
+ flows:
325
+ - sourceId: req.query
326
+ sinkId: db.query
327
+
328
+ - name: "MongoDB $where injection from req.query"
329
+ language: javascript
330
+ code: |
331
+ Product.find({ $where: req.query.q });
332
+ expect:
333
+ count: 1
334
+ flows:
335
+ - sourceId: req.query
336
+ sinkId: Model.find-$where
337
+
338
+ - name: "Upload path traversal from req.body to fs.writeFile"
339
+ language: javascript
340
+ code: |
341
+ fs.writeFile(req.body.path, req.body.data);
342
+ expect:
343
+ count: 1
344
+ flows:
345
+ - sourceId: req.body
346
+ sinkId: fs.writeFile-path
347
+
348
+ - name: "DOMPurify sanitises API XSS via res.json"
349
+ language: javascript
350
+ code: |
351
+ res.json(DOMPurify.sanitize(req.body.x));
352
+ expect:
353
+ count: 0
354
+
355
+ - name: "Optional chaining propagates req?.body to res.send"
356
+ language: javascript
357
+ code: |
358
+ const x = req?.body;
359
+ res.send(x);
360
+ expect:
361
+ count: 1
362
+ flows:
363
+ - sourceId: req.body
364
+ sinkId: res.send
365
+
366
+ - name: "Multer upload path from req.file.path to fs.writeFile"
367
+ language: javascript
368
+ code: |
369
+ fs.writeFile(req.file.path, buf);
370
+ expect:
371
+ count: 1
372
+ flows:
373
+ - sourceId: req.file.path
374
+ sinkId: fs.writeFile-path