circle-ir 3.1.0 → 3.1.1
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/README.md +3 -0
- package/configs/sinks/javascript_dom_xss.yaml +131 -0
- package/configs/sources/javascript_http.yaml +296 -0
- package/configs/sources/python.json +78 -0
- package/dist/analysis/taint-matcher.js +35 -13
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.js +154 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +490 -16
- package/dist/core/circle-ir-core.cjs +29 -14
- package/dist/core/circle-ir-core.js +29 -14
- package/dist/languages/plugins/javascript.js +333 -1
- package/dist/languages/plugins/javascript.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -194,6 +194,9 @@ sources:
|
|
|
194
194
|
|
|
195
195
|
- [Circle-IR Specification](docs/SPEC.md) - IR format specification
|
|
196
196
|
- [Architecture Guide](docs/ARCHITECTURE.md) - Detailed system architecture
|
|
197
|
+
- [Contributing Guide](CONTRIBUTING.md) - How to contribute
|
|
198
|
+
- [Changelog](CHANGELOG.md) - Version history
|
|
199
|
+
- [TODO](TODO.md) - Pending improvements and roadmap
|
|
197
200
|
|
|
198
201
|
## License
|
|
199
202
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sinks": [
|
|
3
|
+
{
|
|
4
|
+
"property": "innerHTML",
|
|
5
|
+
"type": "xss",
|
|
6
|
+
"cwe": "CWE-79",
|
|
7
|
+
"severity": "critical",
|
|
8
|
+
"note": "DOM XSS - innerHTML assignment with user input"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"property": "outerHTML",
|
|
12
|
+
"type": "xss",
|
|
13
|
+
"cwe": "CWE-79",
|
|
14
|
+
"severity": "critical",
|
|
15
|
+
"note": "DOM XSS - outerHTML assignment with user input"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"method": "write",
|
|
19
|
+
"class": "document",
|
|
20
|
+
"type": "xss",
|
|
21
|
+
"cwe": "CWE-79",
|
|
22
|
+
"severity": "critical",
|
|
23
|
+
"arg_positions": [0],
|
|
24
|
+
"note": "DOM XSS - document.write with user input"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"method": "writeln",
|
|
28
|
+
"class": "document",
|
|
29
|
+
"type": "xss",
|
|
30
|
+
"cwe": "CWE-79",
|
|
31
|
+
"severity": "critical",
|
|
32
|
+
"arg_positions": [0],
|
|
33
|
+
"note": "DOM XSS - document.writeln with user input"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"method": "insertAdjacentHTML",
|
|
37
|
+
"type": "xss",
|
|
38
|
+
"cwe": "CWE-79",
|
|
39
|
+
"severity": "critical",
|
|
40
|
+
"arg_positions": [1],
|
|
41
|
+
"note": "DOM XSS - insertAdjacentHTML with user input"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"property": "src",
|
|
45
|
+
"element": "script",
|
|
46
|
+
"type": "xss",
|
|
47
|
+
"cwe": "CWE-79",
|
|
48
|
+
"severity": "critical",
|
|
49
|
+
"note": "DOM XSS - script src manipulation"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"property": "href",
|
|
53
|
+
"element": "a",
|
|
54
|
+
"type": "xss",
|
|
55
|
+
"cwe": "CWE-79",
|
|
56
|
+
"severity": "high",
|
|
57
|
+
"note": "DOM XSS - anchor href with javascript: protocol"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"property": "action",
|
|
61
|
+
"element": "form",
|
|
62
|
+
"type": "xss",
|
|
63
|
+
"cwe": "CWE-79",
|
|
64
|
+
"severity": "high",
|
|
65
|
+
"note": "DOM XSS - form action manipulation"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"method": "setAttribute",
|
|
69
|
+
"type": "xss",
|
|
70
|
+
"cwe": "CWE-79",
|
|
71
|
+
"severity": "high",
|
|
72
|
+
"arg_positions": [1],
|
|
73
|
+
"note": "DOM XSS - setAttribute with event handler or dangerous attributes"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"method": "setAttributeNS",
|
|
77
|
+
"type": "xss",
|
|
78
|
+
"cwe": "CWE-79",
|
|
79
|
+
"severity": "high",
|
|
80
|
+
"arg_positions": [2],
|
|
81
|
+
"note": "DOM XSS - setAttributeNS"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"method": "createContextualFragment",
|
|
85
|
+
"class": "Range",
|
|
86
|
+
"type": "xss",
|
|
87
|
+
"cwe": "CWE-79",
|
|
88
|
+
"severity": "critical",
|
|
89
|
+
"arg_positions": [0],
|
|
90
|
+
"note": "DOM XSS - Range.createContextualFragment"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"method": "parseFromString",
|
|
94
|
+
"class": "DOMParser",
|
|
95
|
+
"type": "xss",
|
|
96
|
+
"cwe": "CWE-79",
|
|
97
|
+
"severity": "high",
|
|
98
|
+
"arg_positions": [0],
|
|
99
|
+
"note": "DOM parsing - may lead to XSS if content is rendered"
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"sanitizers": [
|
|
103
|
+
{
|
|
104
|
+
"method": "sanitize",
|
|
105
|
+
"class": "DOMPurify",
|
|
106
|
+
"removes": ["xss"],
|
|
107
|
+
"note": "DOMPurify sanitization"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"method": "createTextNode",
|
|
111
|
+
"class": "document",
|
|
112
|
+
"removes": ["xss"],
|
|
113
|
+
"note": "Text node creation - safe from XSS"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"property": "textContent",
|
|
117
|
+
"removes": ["xss"],
|
|
118
|
+
"note": "textContent assignment - safe from XSS"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"property": "innerText",
|
|
122
|
+
"removes": ["xss"],
|
|
123
|
+
"note": "innerText assignment - safe from XSS"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"method": "encodeURIComponent",
|
|
127
|
+
"removes": ["xss"],
|
|
128
|
+
"note": "URL encoding - safe for URL parameters"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sources": [
|
|
3
|
+
{
|
|
4
|
+
"property": "query",
|
|
5
|
+
"object": "req",
|
|
6
|
+
"type": "http_param",
|
|
7
|
+
"severity": "high",
|
|
8
|
+
"return_tainted": true,
|
|
9
|
+
"note": "Express.js query parameters (req.query.x)"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"property": "params",
|
|
13
|
+
"object": "req",
|
|
14
|
+
"type": "http_path",
|
|
15
|
+
"severity": "high",
|
|
16
|
+
"return_tainted": true,
|
|
17
|
+
"note": "Express.js route parameters (req.params.x)"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"property": "body",
|
|
21
|
+
"object": "req",
|
|
22
|
+
"type": "http_body",
|
|
23
|
+
"severity": "high",
|
|
24
|
+
"return_tainted": true,
|
|
25
|
+
"note": "Express.js request body (req.body.x)"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"property": "headers",
|
|
29
|
+
"object": "req",
|
|
30
|
+
"type": "http_header",
|
|
31
|
+
"severity": "high",
|
|
32
|
+
"return_tainted": true,
|
|
33
|
+
"note": "Express.js request headers (req.headers.x)"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"property": "cookies",
|
|
37
|
+
"object": "req",
|
|
38
|
+
"type": "http_cookie",
|
|
39
|
+
"severity": "high",
|
|
40
|
+
"return_tainted": true,
|
|
41
|
+
"note": "Express.js cookies (req.cookies.x)"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"method": "param",
|
|
45
|
+
"object": "req",
|
|
46
|
+
"type": "http_param",
|
|
47
|
+
"severity": "high",
|
|
48
|
+
"return_tainted": true,
|
|
49
|
+
"note": "Express.js req.param() method"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"method": "get",
|
|
53
|
+
"object": "req",
|
|
54
|
+
"type": "http_header",
|
|
55
|
+
"severity": "high",
|
|
56
|
+
"return_tainted": true,
|
|
57
|
+
"note": "Express.js req.get() for headers"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"method": "header",
|
|
61
|
+
"object": "req",
|
|
62
|
+
"type": "http_header",
|
|
63
|
+
"severity": "high",
|
|
64
|
+
"return_tainted": true,
|
|
65
|
+
"note": "Express.js req.header() for headers"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"property": "url",
|
|
69
|
+
"object": "req",
|
|
70
|
+
"type": "http_path",
|
|
71
|
+
"severity": "high",
|
|
72
|
+
"return_tainted": true,
|
|
73
|
+
"note": "Express.js request URL"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"property": "path",
|
|
77
|
+
"object": "req",
|
|
78
|
+
"type": "http_path",
|
|
79
|
+
"severity": "medium",
|
|
80
|
+
"return_tainted": true,
|
|
81
|
+
"note": "Express.js request path"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"property": "originalUrl",
|
|
85
|
+
"object": "req",
|
|
86
|
+
"type": "http_path",
|
|
87
|
+
"severity": "high",
|
|
88
|
+
"return_tainted": true,
|
|
89
|
+
"note": "Express.js original URL"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"property": "baseUrl",
|
|
93
|
+
"object": "req",
|
|
94
|
+
"type": "http_path",
|
|
95
|
+
"severity": "medium",
|
|
96
|
+
"return_tainted": true,
|
|
97
|
+
"note": "Express.js base URL"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"property": "hostname",
|
|
101
|
+
"object": "req",
|
|
102
|
+
"type": "http_header",
|
|
103
|
+
"severity": "medium",
|
|
104
|
+
"return_tainted": true,
|
|
105
|
+
"note": "Express.js hostname from Host header"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"property": "ip",
|
|
109
|
+
"object": "req",
|
|
110
|
+
"type": "http_header",
|
|
111
|
+
"severity": "low",
|
|
112
|
+
"return_tainted": true,
|
|
113
|
+
"note": "Express.js client IP (can be spoofed via X-Forwarded-For)"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"property": "protocol",
|
|
117
|
+
"object": "req",
|
|
118
|
+
"type": "http_header",
|
|
119
|
+
"severity": "low",
|
|
120
|
+
"return_tainted": true,
|
|
121
|
+
"note": "Express.js protocol"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"property": "searchParams",
|
|
125
|
+
"object": "URL",
|
|
126
|
+
"type": "http_param",
|
|
127
|
+
"severity": "high",
|
|
128
|
+
"return_tainted": true,
|
|
129
|
+
"note": "URL searchParams"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"method": "searchParams.get",
|
|
133
|
+
"type": "http_param",
|
|
134
|
+
"severity": "high",
|
|
135
|
+
"return_tainted": true,
|
|
136
|
+
"note": "URL searchParams.get()"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"property": "search",
|
|
140
|
+
"object": "location",
|
|
141
|
+
"type": "http_param",
|
|
142
|
+
"severity": "high",
|
|
143
|
+
"return_tainted": true,
|
|
144
|
+
"note": "Browser location.search"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"property": "hash",
|
|
148
|
+
"object": "location",
|
|
149
|
+
"type": "http_param",
|
|
150
|
+
"severity": "high",
|
|
151
|
+
"return_tainted": true,
|
|
152
|
+
"note": "Browser location.hash"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"property": "href",
|
|
156
|
+
"object": "location",
|
|
157
|
+
"type": "http_path",
|
|
158
|
+
"severity": "high",
|
|
159
|
+
"return_tainted": true,
|
|
160
|
+
"note": "Browser location.href"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"property": "pathname",
|
|
164
|
+
"object": "location",
|
|
165
|
+
"type": "http_path",
|
|
166
|
+
"severity": "medium",
|
|
167
|
+
"return_tainted": true,
|
|
168
|
+
"note": "Browser location.pathname"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"method": "getElementById",
|
|
172
|
+
"object": "document",
|
|
173
|
+
"type": "dom_input",
|
|
174
|
+
"severity": "high",
|
|
175
|
+
"return_tainted": true,
|
|
176
|
+
"note": "DOM element (may contain user input)"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"method": "querySelector",
|
|
180
|
+
"object": "document",
|
|
181
|
+
"type": "dom_input",
|
|
182
|
+
"severity": "high",
|
|
183
|
+
"return_tainted": true,
|
|
184
|
+
"note": "DOM element (may contain user input)"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"method": "querySelectorAll",
|
|
188
|
+
"object": "document",
|
|
189
|
+
"type": "dom_input",
|
|
190
|
+
"severity": "high",
|
|
191
|
+
"return_tainted": true,
|
|
192
|
+
"note": "DOM elements (may contain user input)"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"property": "value",
|
|
196
|
+
"type": "dom_input",
|
|
197
|
+
"severity": "high",
|
|
198
|
+
"return_tainted": true,
|
|
199
|
+
"note": "Form input value"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"property": "innerHTML",
|
|
203
|
+
"type": "dom_input",
|
|
204
|
+
"severity": "high",
|
|
205
|
+
"return_tainted": true,
|
|
206
|
+
"note": "DOM innerHTML (can be tainted)"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"property": "innerText",
|
|
210
|
+
"type": "dom_input",
|
|
211
|
+
"severity": "medium",
|
|
212
|
+
"return_tainted": true,
|
|
213
|
+
"note": "DOM innerText (can be tainted)"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"property": "textContent",
|
|
217
|
+
"type": "dom_input",
|
|
218
|
+
"severity": "medium",
|
|
219
|
+
"return_tainted": true,
|
|
220
|
+
"note": "DOM textContent (can be tainted)"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"method": "prompt",
|
|
224
|
+
"type": "user_input",
|
|
225
|
+
"severity": "high",
|
|
226
|
+
"return_tainted": true,
|
|
227
|
+
"note": "Browser prompt() dialog - user input"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"property": "request.query",
|
|
231
|
+
"type": "http_param",
|
|
232
|
+
"severity": "high",
|
|
233
|
+
"return_tainted": true,
|
|
234
|
+
"note": "Koa/Hapi request query"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"property": "request.params",
|
|
238
|
+
"type": "http_path",
|
|
239
|
+
"severity": "high",
|
|
240
|
+
"return_tainted": true,
|
|
241
|
+
"note": "Koa/Hapi request params"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"property": "request.body",
|
|
245
|
+
"type": "http_body",
|
|
246
|
+
"severity": "high",
|
|
247
|
+
"return_tainted": true,
|
|
248
|
+
"note": "Koa/Hapi request body"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"property": "ctx.query",
|
|
252
|
+
"type": "http_param",
|
|
253
|
+
"severity": "high",
|
|
254
|
+
"return_tainted": true,
|
|
255
|
+
"note": "Koa context query"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"property": "ctx.params",
|
|
259
|
+
"type": "http_path",
|
|
260
|
+
"severity": "high",
|
|
261
|
+
"return_tainted": true,
|
|
262
|
+
"note": "Koa context params"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"property": "ctx.request.body",
|
|
266
|
+
"type": "http_body",
|
|
267
|
+
"severity": "high",
|
|
268
|
+
"return_tainted": true,
|
|
269
|
+
"note": "Koa context request body"
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"property": "input",
|
|
273
|
+
"object": "event",
|
|
274
|
+
"type": "user_input",
|
|
275
|
+
"severity": "high",
|
|
276
|
+
"return_tainted": true,
|
|
277
|
+
"note": "Event input (keyboard, etc.)"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"property": "data",
|
|
281
|
+
"object": "event",
|
|
282
|
+
"type": "user_input",
|
|
283
|
+
"severity": "high",
|
|
284
|
+
"return_tainted": true,
|
|
285
|
+
"note": "Event data (paste, drag, etc.)"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"property": "target.value",
|
|
289
|
+
"object": "event",
|
|
290
|
+
"type": "user_input",
|
|
291
|
+
"severity": "high",
|
|
292
|
+
"return_tainted": true,
|
|
293
|
+
"note": "Event target value"
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
@@ -199,6 +199,48 @@
|
|
|
199
199
|
"severity": "high",
|
|
200
200
|
"return_tainted": true,
|
|
201
201
|
"note": "socket.recvfrom() - network data with address"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"annotation": "Query",
|
|
205
|
+
"type": "http_param",
|
|
206
|
+
"severity": "high",
|
|
207
|
+
"note": "FastAPI Query() parameter"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"annotation": "Path",
|
|
211
|
+
"type": "http_param",
|
|
212
|
+
"severity": "high",
|
|
213
|
+
"note": "FastAPI Path() parameter"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"annotation": "Body",
|
|
217
|
+
"type": "http_body",
|
|
218
|
+
"severity": "high",
|
|
219
|
+
"note": "FastAPI Body() parameter"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"annotation": "Header",
|
|
223
|
+
"type": "http_header",
|
|
224
|
+
"severity": "high",
|
|
225
|
+
"note": "FastAPI Header() parameter"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"annotation": "Cookie",
|
|
229
|
+
"type": "http_cookie",
|
|
230
|
+
"severity": "high",
|
|
231
|
+
"note": "FastAPI Cookie() parameter"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"annotation": "Form",
|
|
235
|
+
"type": "http_param",
|
|
236
|
+
"severity": "high",
|
|
237
|
+
"note": "FastAPI Form() parameter"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"annotation": "File",
|
|
241
|
+
"type": "file_input",
|
|
242
|
+
"severity": "high",
|
|
243
|
+
"note": "FastAPI File() upload"
|
|
202
244
|
}
|
|
203
245
|
],
|
|
204
246
|
"annotations": [
|
|
@@ -225,6 +267,42 @@
|
|
|
225
267
|
"type": "http_param",
|
|
226
268
|
"severity": "high",
|
|
227
269
|
"note": "Django REST framework @api_view() decorated function"
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"annotation": "app.get",
|
|
273
|
+
"type": "http_param",
|
|
274
|
+
"severity": "high",
|
|
275
|
+
"note": "FastAPI @app.get() decorated function parameters"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"annotation": "app.post",
|
|
279
|
+
"type": "http_body",
|
|
280
|
+
"severity": "high",
|
|
281
|
+
"note": "FastAPI @app.post() decorated function parameters"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"annotation": "app.put",
|
|
285
|
+
"type": "http_body",
|
|
286
|
+
"severity": "high",
|
|
287
|
+
"note": "FastAPI @app.put() decorated function parameters"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"annotation": "app.delete",
|
|
291
|
+
"type": "http_param",
|
|
292
|
+
"severity": "high",
|
|
293
|
+
"note": "FastAPI @app.delete() decorated function parameters"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"annotation": "router.get",
|
|
297
|
+
"type": "http_param",
|
|
298
|
+
"severity": "high",
|
|
299
|
+
"note": "FastAPI APIRouter @router.get() decorated function"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"annotation": "router.post",
|
|
303
|
+
"type": "http_body",
|
|
304
|
+
"severity": "high",
|
|
305
|
+
"note": "FastAPI APIRouter @router.post() decorated function"
|
|
228
306
|
}
|
|
229
307
|
]
|
|
230
308
|
}
|
|
@@ -67,15 +67,20 @@ function findSources(calls, types, patterns) {
|
|
|
67
67
|
continue;
|
|
68
68
|
for (const param of method.parameters) {
|
|
69
69
|
// Check if parameter type could carry tainted data
|
|
70
|
-
|
|
70
|
+
// For typed languages (Java), check the type
|
|
71
|
+
// For untyped languages (JavaScript), treat all params as potentially tainted
|
|
72
|
+
const isTaintable = param.type
|
|
73
|
+
? isInterproceduralTaintableType(param.type)
|
|
74
|
+
: true; // JavaScript/Python - no type means any value
|
|
75
|
+
if (isTaintable) {
|
|
71
76
|
// Use parameter line if available, fallback to method start line
|
|
72
77
|
const paramLine = param.line ?? method.start_line;
|
|
73
78
|
sources.push({
|
|
74
79
|
type: 'interprocedural_param',
|
|
75
|
-
location: `${param.type} ${param.name} in ${method.name}`,
|
|
80
|
+
location: `${param.type || 'any'} ${param.name} in ${method.name}`,
|
|
76
81
|
severity: 'medium',
|
|
77
82
|
line: paramLine,
|
|
78
|
-
confidence: 0.7, // Lower confidence
|
|
83
|
+
confidence: param.type ? 0.7 : 0.5, // Lower confidence for untyped params
|
|
79
84
|
});
|
|
80
85
|
}
|
|
81
86
|
}
|
|
@@ -105,7 +110,16 @@ function findSources(calls, types, patterns) {
|
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
|
-
|
|
113
|
+
// Deduplicate sources by line+type, keeping highest confidence
|
|
114
|
+
const sourceMap = new Map();
|
|
115
|
+
for (const source of sources) {
|
|
116
|
+
const key = `${source.line}:${source.type}`;
|
|
117
|
+
const existing = sourceMap.get(key);
|
|
118
|
+
if (!existing || source.confidence > existing.confidence) {
|
|
119
|
+
sourceMap.set(key, source);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return Array.from(sourceMap.values());
|
|
109
123
|
}
|
|
110
124
|
/**
|
|
111
125
|
* Check if a parameter type could carry tainted data in inter-procedural analysis.
|
|
@@ -167,23 +181,31 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
167
181
|
}
|
|
168
182
|
/**
|
|
169
183
|
* Find taint sinks in method calls.
|
|
184
|
+
* Deduplicates sinks at the same location+line+cwe, keeping highest confidence.
|
|
170
185
|
*/
|
|
171
186
|
function findSinks(calls, patterns) {
|
|
172
|
-
|
|
187
|
+
// Use a map to deduplicate by location+line+cwe
|
|
188
|
+
const sinkMap = new Map();
|
|
173
189
|
for (const call of calls) {
|
|
174
190
|
for (const pattern of patterns) {
|
|
175
191
|
if (matchesSinkPattern(call, pattern)) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
const location = formatCallLocation(call);
|
|
193
|
+
const key = `${location}:${call.location.line}:${pattern.cwe}`;
|
|
194
|
+
const confidence = calculateSinkConfidence(call, pattern);
|
|
195
|
+
const existing = sinkMap.get(key);
|
|
196
|
+
if (!existing || confidence > existing.confidence) {
|
|
197
|
+
sinkMap.set(key, {
|
|
198
|
+
type: pattern.type,
|
|
199
|
+
cwe: pattern.cwe,
|
|
200
|
+
location,
|
|
201
|
+
line: call.location.line,
|
|
202
|
+
confidence,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
183
205
|
}
|
|
184
206
|
}
|
|
185
207
|
}
|
|
186
|
-
return
|
|
208
|
+
return Array.from(sinkMap.values());
|
|
187
209
|
}
|
|
188
210
|
/**
|
|
189
211
|
* Check if a call matches a source pattern.
|