@kevinrabun/judges 3.20.14 → 3.22.0
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/CHANGELOG.md +68 -0
- package/dist/api.d.ts +42 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +49 -1
- package/dist/api.js.map +1 -1
- package/dist/ast/taint-tracker.d.ts +3 -1
- package/dist/ast/taint-tracker.d.ts.map +1 -1
- package/dist/ast/taint-tracker.js +523 -12
- package/dist/ast/taint-tracker.js.map +1 -1
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +124 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/benchmark.d.ts +28 -0
- package/dist/commands/benchmark.d.ts.map +1 -1
- package/dist/commands/benchmark.js +1058 -1
- package/dist/commands/benchmark.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +88 -0
- package/dist/config.js.map +1 -1
- package/dist/dedup.d.ts +23 -0
- package/dist/dedup.d.ts.map +1 -1
- package/dist/dedup.js +123 -0
- package/dist/dedup.js.map +1 -1
- package/dist/evaluators/authentication.d.ts +2 -2
- package/dist/evaluators/authentication.d.ts.map +1 -1
- package/dist/evaluators/authentication.js +26 -2
- package/dist/evaluators/authentication.js.map +1 -1
- package/dist/evaluators/cybersecurity.d.ts +2 -2
- package/dist/evaluators/cybersecurity.d.ts.map +1 -1
- package/dist/evaluators/cybersecurity.js +58 -5
- package/dist/evaluators/cybersecurity.js.map +1 -1
- package/dist/evaluators/framework-safety.d.ts.map +1 -1
- package/dist/evaluators/framework-safety.js +855 -365
- package/dist/evaluators/framework-safety.js.map +1 -1
- package/dist/evaluators/index.d.ts +1 -1
- package/dist/evaluators/index.d.ts.map +1 -1
- package/dist/evaluators/index.js +6 -2
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/performance.d.ts +2 -2
- package/dist/evaluators/performance.d.ts.map +1 -1
- package/dist/evaluators/performance.js +33 -4
- package/dist/evaluators/performance.js.map +1 -1
- package/dist/evaluators/project.d.ts.map +1 -1
- package/dist/evaluators/project.js +223 -13
- package/dist/evaluators/project.js.map +1 -1
- package/dist/evaluators/shared.d.ts +31 -3
- package/dist/evaluators/shared.d.ts.map +1 -1
- package/dist/evaluators/shared.js +145 -11
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/v2.d.ts.map +1 -1
- package/dist/evaluators/v2.js +8 -0
- package/dist/evaluators/v2.js.map +1 -1
- package/dist/formatters/csv.d.ts +17 -0
- package/dist/formatters/csv.d.ts.map +1 -0
- package/dist/formatters/csv.js +54 -0
- package/dist/formatters/csv.js.map +1 -0
- package/dist/language-patterns.d.ts +136 -0
- package/dist/language-patterns.d.ts.map +1 -1
- package/dist/language-patterns.js +155 -1
- package/dist/language-patterns.js.map +1 -1
- package/dist/patches/index.d.ts.map +1 -1
- package/dist/patches/index.js +210 -0
- package/dist/patches/index.js.map +1 -1
- package/dist/presets.d.ts +14 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +72 -0
- package/dist/presets.js.map +1 -1
- package/dist/scoring.d.ts.map +1 -1
- package/dist/scoring.js +43 -4
- package/dist/scoring.js.map +1 -1
- package/dist/tools/register-fix.d.ts +6 -0
- package/dist/tools/register-fix.d.ts.map +1 -0
- package/dist/tools/register-fix.js +153 -0
- package/dist/tools/register-fix.js.map +1 -0
- package/dist/tools/register-workflow.d.ts.map +1 -1
- package/dist/tools/register-workflow.js +79 -0
- package/dist/tools/register-workflow.js.map +1 -1
- package/dist/tools/register-workspace.d.ts +3 -0
- package/dist/tools/register-workspace.d.ts.map +1 -0
- package/dist/tools/register-workspace.js +215 -0
- package/dist/tools/register-workspace.js.map +1 -0
- package/dist/tools/register.d.ts +1 -1
- package/dist/tools/register.d.ts.map +1 -1
- package/dist/tools/register.js +5 -1
- package/dist/tools/register.js.map +1 -1
- package/dist/tools/schemas.d.ts +2 -2
- package/dist/types.d.ts +24 -2
- package/dist/types.d.ts.map +1 -1
- package/judgesrc.schema.json +17 -2
- package/package.json +1 -1
- package/server.json +30 -2
|
@@ -208,13 +208,480 @@ function getFnName(node) {
|
|
|
208
208
|
}
|
|
209
209
|
return undefined;
|
|
210
210
|
}
|
|
211
|
+
const PYTHON_PATTERNS = {
|
|
212
|
+
sources: [
|
|
213
|
+
{
|
|
214
|
+
pattern: /\brequest\.(?:form|args|json|data|values|files|cookies|headers)\b(?:\[|\.get\s*\()/i,
|
|
215
|
+
kind: "http-param",
|
|
216
|
+
},
|
|
217
|
+
{ pattern: /\brequest\.GET\b(?:\[|\.get\s*\()/i, kind: "http-param" },
|
|
218
|
+
{ pattern: /\brequest\.POST\b(?:\[|\.get\s*\()/i, kind: "http-param" },
|
|
219
|
+
{ pattern: /\brequest\.(?:query_params|query_string)\b/i, kind: "http-param" },
|
|
220
|
+
{ pattern: /\bflask\.request\.\w+/i, kind: "http-param" },
|
|
221
|
+
{ pattern: /\binput\s*\(/i, kind: "user-input" },
|
|
222
|
+
{ pattern: /\bsys\.stdin\b/i, kind: "user-input" },
|
|
223
|
+
{ pattern: /\bos\.environ\b(?:\[|\.get\s*\()/i, kind: "environment" },
|
|
224
|
+
{ pattern: /\burlparse\s*\(|parse_qs\s*\(/i, kind: "url-param" },
|
|
225
|
+
{ pattern: /\bopen\s*\(.*\)\.read/i, kind: "external-data" },
|
|
226
|
+
{ pattern: /\brequests\.(?:get|post|put|delete)\s*\(/i, kind: "external-data" },
|
|
227
|
+
{ pattern: /\bjson\.loads?\s*\(/i, kind: "external-data" },
|
|
228
|
+
],
|
|
229
|
+
sinks: [
|
|
230
|
+
{ pattern: /\bexec\s*\(/i, kind: "code-execution" },
|
|
231
|
+
{ pattern: /\beval\s*\(/i, kind: "code-execution" },
|
|
232
|
+
{ pattern: /\bcompile\s*\(.*\).*\bexec\b/i, kind: "code-execution" },
|
|
233
|
+
{ pattern: /\bos\.system\s*\(/i, kind: "command-exec" },
|
|
234
|
+
{ pattern: /\bos\.popen\s*\(/i, kind: "command-exec" },
|
|
235
|
+
{
|
|
236
|
+
pattern: /\bsubprocess\.(?:Popen|run|call|check_output|check_call|getoutput|getstatusoutput)\s*\(/i,
|
|
237
|
+
kind: "command-exec",
|
|
238
|
+
},
|
|
239
|
+
{ pattern: /\bcursor\.execute\s*\(/i, kind: "sql-query" },
|
|
240
|
+
{ pattern: /\b(?:connection|conn|db)\.execute\s*\(/i, kind: "sql-query" },
|
|
241
|
+
{ pattern: /\braw\s*\(\s*["'`]?\s*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
242
|
+
{ pattern: /\.(?:extra|raw)\s*\(/i, kind: "sql-query" },
|
|
243
|
+
{ pattern: /\brender_template_string\s*\(/i, kind: "template" },
|
|
244
|
+
{ pattern: /\bTemplate\s*\(.*\)\.render\s*\(/i, kind: "template" },
|
|
245
|
+
{ pattern: /\bJinja2\.\w*\.from_string\s*\(/i, kind: "template" },
|
|
246
|
+
{ pattern: /\bmarkup\s*\(.*\+/i, kind: "xss" },
|
|
247
|
+
{ pattern: /\bopen\s*\(.*user|path|file|name/i, kind: "path-traversal" },
|
|
248
|
+
{ pattern: /\bredirect\s*\(/i, kind: "redirect" },
|
|
249
|
+
{ pattern: /\bpickle\.loads?\s*\(/i, kind: "deserialization" },
|
|
250
|
+
{ pattern: /\byaml\.(?:load|unsafe_load)\s*\(/i, kind: "deserialization" },
|
|
251
|
+
{ pattern: /\bmarshal\.loads?\s*\(/i, kind: "deserialization" },
|
|
252
|
+
],
|
|
253
|
+
sanitizers: [
|
|
254
|
+
/\bbleach\.clean\s*\(/i,
|
|
255
|
+
/\bmarkup_safe\b/i,
|
|
256
|
+
/\bescape\s*\(/i,
|
|
257
|
+
/\bMarkup\b/i,
|
|
258
|
+
/\bquote\s*\(/i,
|
|
259
|
+
/\bshlex\.quote\s*\(/i,
|
|
260
|
+
/\bshellescape\s*\(/i,
|
|
261
|
+
/\bsanitize\w*\s*\(/i,
|
|
262
|
+
/\bvalidator\.\w+\s*\(/i,
|
|
263
|
+
/\bpydantic\b/i,
|
|
264
|
+
/\b%s\b.*\bexecute\s*\(/i, // parameterized query
|
|
265
|
+
/\bparamstyle\b/i,
|
|
266
|
+
/\bsqlalchemy\.text\s*\(/i,
|
|
267
|
+
],
|
|
268
|
+
assignPattern: /^\s*(\w+)\s*(?::\s*\w[\w[\], |]*\s*)?=\s*(.+)/,
|
|
269
|
+
guards: [
|
|
270
|
+
/if[ \t]+(?:not[ \t]+)?isinstance\s*\(/i,
|
|
271
|
+
/if[ \t]+(?:not[ \t]+)?\w+\s*(?:is|==|!=)\s*None/i,
|
|
272
|
+
/raise[ \t]+(?:ValueError|TypeError|ValidationError)/i,
|
|
273
|
+
/assert[ \t]+isinstance\s*\(/i,
|
|
274
|
+
/\.validate\s*\(|\.is_valid\s*\(/i,
|
|
275
|
+
],
|
|
276
|
+
};
|
|
277
|
+
const JAVA_PATTERNS = {
|
|
278
|
+
sources: [
|
|
279
|
+
{ pattern: /\b(?:request|req|httpRequest)\.getParameter\s*\(/i, kind: "http-param" },
|
|
280
|
+
{ pattern: /\brequest\.getAttribute\s*\(/i, kind: "http-param" },
|
|
281
|
+
{ pattern: /\brequest\.getHeader\s*\(/i, kind: "http-param" },
|
|
282
|
+
{ pattern: /\brequest\.getQueryString\s*\(/i, kind: "http-param" },
|
|
283
|
+
{ pattern: /\brequest\.getInputStream\s*\(/i, kind: "http-param" },
|
|
284
|
+
{ pattern: /\brequest\.getReader\s*\(/i, kind: "http-param" },
|
|
285
|
+
{ pattern: /\brequest\.getCookies\s*\(/i, kind: "http-param" },
|
|
286
|
+
{ pattern: /\b@RequestParam\b/i, kind: "http-param" },
|
|
287
|
+
{ pattern: /\b@PathVariable\b/i, kind: "url-param" },
|
|
288
|
+
{ pattern: /\b@RequestBody\b/i, kind: "http-param" },
|
|
289
|
+
{ pattern: /\b@RequestHeader\b/i, kind: "http-param" },
|
|
290
|
+
{ pattern: /\bSystem\.getenv\s*\(/i, kind: "environment" },
|
|
291
|
+
{ pattern: /\bScanner\s*\(\s*System\.in\b/i, kind: "user-input" },
|
|
292
|
+
{ pattern: /\bBufferedReader\b.*\bInputStreamReader\b.*\bSystem\.in\b/i, kind: "user-input" },
|
|
293
|
+
{ pattern: /\bargs\[/i, kind: "user-input" },
|
|
294
|
+
{ pattern: /\bnew\s+ObjectMapper\b.*\.read/i, kind: "external-data" },
|
|
295
|
+
{ pattern: /\bURL\s*\(.*\)\.openStream\s*\(/i, kind: "external-data" },
|
|
296
|
+
],
|
|
297
|
+
sinks: [
|
|
298
|
+
{ pattern: /\bRuntime\.getRuntime\s*\(\)\.exec\s*\(/i, kind: "command-exec" },
|
|
299
|
+
{ pattern: /\bProcessBuilder\b/i, kind: "command-exec" },
|
|
300
|
+
{ pattern: /\bStatement\b.*\.(?:execute|executeQuery|executeUpdate)\s*\(/i, kind: "sql-query" },
|
|
301
|
+
{ pattern: /\.(?:createQuery|createNativeQuery)\s*\(/i, kind: "sql-query" },
|
|
302
|
+
{ pattern: /\bString\.format\s*\(.*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
303
|
+
{ pattern: /\bScriptEngine\b.*\.eval\s*\(/i, kind: "code-execution" },
|
|
304
|
+
{ pattern: /\bClass\.forName\s*\(/i, kind: "code-execution" },
|
|
305
|
+
{ pattern: /\.newInstance\s*\(/i, kind: "code-execution" },
|
|
306
|
+
{ pattern: /\bXStream\b.*\.fromXML\s*\(/i, kind: "deserialization" },
|
|
307
|
+
{ pattern: /\bObjectInputStream\b.*\.readObject\s*\(/i, kind: "deserialization" },
|
|
308
|
+
{ pattern: /\bnew\s+File\s*\(/i, kind: "path-traversal" },
|
|
309
|
+
{ pattern: /\bFiles\.(?:read|write|copy|move|newInputStream)\s*\(/i, kind: "path-traversal" },
|
|
310
|
+
{ pattern: /\bresponse\.sendRedirect\s*\(/i, kind: "redirect" },
|
|
311
|
+
{ pattern: /\.(?:forward|include)\s*\(/i, kind: "redirect" },
|
|
312
|
+
{ pattern: /\bVelocity\b.*\.evaluate\s*\(/i, kind: "template" },
|
|
313
|
+
{ pattern: /\bFreemarkerConfiguration\b/i, kind: "template" },
|
|
314
|
+
],
|
|
315
|
+
sanitizers: [
|
|
316
|
+
/\bPreparedStatement\b/i,
|
|
317
|
+
/\bEncoder\.encode\s*\(/i,
|
|
318
|
+
/\bOWASP\.\w+\.encode\s*\(/i,
|
|
319
|
+
/\bHtmlUtils\.htmlEscape\s*\(/i,
|
|
320
|
+
/\bStringEscapeUtils\.escape\w+\s*\(/i,
|
|
321
|
+
/\bPattern\.matches\s*\(/i,
|
|
322
|
+
/\b@Valid\b/i,
|
|
323
|
+
/\b@Validated\b/i,
|
|
324
|
+
/\bBindingResult\b/i,
|
|
325
|
+
/\bInputValidator\b/i,
|
|
326
|
+
/\bwhitelist\s*\(/i,
|
|
327
|
+
/\bSanitizers\.\w+\s*\(/i,
|
|
328
|
+
],
|
|
329
|
+
assignPattern: /^\s*(?:(?:final|var|String|int|long|double|boolean|byte|short|float|char|Object|List|Map|Set|Integer|Long|Double|Boolean|Optional|HttpServletRequest)\s+)*(\w+)\s*=\s*(.+);/,
|
|
330
|
+
guards: [
|
|
331
|
+
/if[ \t]*\([ \t]*\w+[ \t]*==[ \t]*null/i,
|
|
332
|
+
/\bObjects\.requireNonNull\s*\(/i,
|
|
333
|
+
/\bOptional\.ofNullable\s*\(/i,
|
|
334
|
+
/\bif[ \t]*\([ \t]*!?\w+\.(?:isEmpty|isBlank|matches|startsWith)\s*\(/i,
|
|
335
|
+
/throw[ \t]+new[ \t]+(?:IllegalArgumentException|ValidationException)/i,
|
|
336
|
+
],
|
|
337
|
+
};
|
|
338
|
+
const GO_PATTERNS = {
|
|
339
|
+
sources: [
|
|
340
|
+
{ pattern: /\br\.(?:FormValue|PostFormValue)\s*\(/i, kind: "http-param" },
|
|
341
|
+
{ pattern: /\br\.URL\.Query\s*\(\)/i, kind: "http-param" },
|
|
342
|
+
{ pattern: /\br\.Header\.Get\s*\(/i, kind: "http-param" },
|
|
343
|
+
{ pattern: /\br\.Body\b/i, kind: "http-param" },
|
|
344
|
+
{ pattern: /\bc\.(?:Query|Param|PostForm|FormValue|GetHeader)\s*\(/i, kind: "http-param" },
|
|
345
|
+
{ pattern: /\bc\.(?:BindJSON|ShouldBindJSON|Bind)\s*\(/i, kind: "http-param" },
|
|
346
|
+
{ pattern: /\bos\.Getenv\s*\(/i, kind: "environment" },
|
|
347
|
+
{ pattern: /\bos\.Args\b/i, kind: "user-input" },
|
|
348
|
+
{ pattern: /\bflag\.(?:String|Int|Bool|Arg)\s*\(/i, kind: "user-input" },
|
|
349
|
+
{ pattern: /\bbufio\.NewReader\s*\(\s*os\.Stdin\b/i, kind: "user-input" },
|
|
350
|
+
{ pattern: /\bjson\.(?:Unmarshal|NewDecoder)\s*\(/i, kind: "external-data" },
|
|
351
|
+
{ pattern: /\bhttp\.Get\s*\(/i, kind: "external-data" },
|
|
352
|
+
{ pattern: /\bioutil\.ReadAll\s*\(/i, kind: "external-data" },
|
|
353
|
+
{ pattern: /\bio\.ReadAll\s*\(/i, kind: "external-data" },
|
|
354
|
+
],
|
|
355
|
+
sinks: [
|
|
356
|
+
{ pattern: /\bexec\.Command\s*\(/i, kind: "command-exec" },
|
|
357
|
+
{ pattern: /\bexec\.CommandContext\s*\(/i, kind: "command-exec" },
|
|
358
|
+
{ pattern: /\bos\.(?:StartProcess|Exec)\s*\(/i, kind: "command-exec" },
|
|
359
|
+
{ pattern: /\bdb\.(?:Query|Exec|QueryRow|QueryContext|ExecContext)\s*\(/i, kind: "sql-query" },
|
|
360
|
+
{ pattern: /\bsql\.(?:Open|Query)\s*\(/i, kind: "sql-query" },
|
|
361
|
+
{ pattern: /\bfmt\.Sprintf\s*\(.*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
362
|
+
{ pattern: /\btemplate\.(?:New|Must)\s*\(.*\.Parse\s*\(/i, kind: "template" },
|
|
363
|
+
{ pattern: /\bhtml\/template\b.*\.Execute\s*\(/i, kind: "template" },
|
|
364
|
+
{ pattern: /\btext\/template\b.*\.Execute\s*\(/i, kind: "template" },
|
|
365
|
+
{ pattern: /\bos\.(?:Open|Create|OpenFile|ReadFile|WriteFile)\s*\(/i, kind: "path-traversal" },
|
|
366
|
+
{ pattern: /\bfilepath\.Join\s*\(.*\+/i, kind: "path-traversal" },
|
|
367
|
+
{ pattern: /\bhttp\.Redirect\s*\(/i, kind: "redirect" },
|
|
368
|
+
{ pattern: /\bgob\.NewDecoder\b.*\.Decode\s*\(/i, kind: "deserialization" },
|
|
369
|
+
{ pattern: /\bencoding\/gob\b/i, kind: "deserialization" },
|
|
370
|
+
{ pattern: /\byaml\.Unmarshal\s*\(/i, kind: "deserialization" },
|
|
371
|
+
],
|
|
372
|
+
sanitizers: [
|
|
373
|
+
/\bhtml\.EscapeString\s*\(/i,
|
|
374
|
+
/\burl\.QueryEscape\s*\(/i,
|
|
375
|
+
/\burl\.PathEscape\s*\(/i,
|
|
376
|
+
/\btemplate\.HTMLEscapeString\s*\(/i,
|
|
377
|
+
/\bstrconv\.(?:Atoi|ParseInt|ParseFloat|ParseBool)\s*\(/i,
|
|
378
|
+
/\bregexp\.MustCompile\b.*\.(?:MatchString|FindString)\s*\(/i,
|
|
379
|
+
/\bfilepath\.Clean\s*\(/i,
|
|
380
|
+
/\bpath\.Clean\s*\(/i,
|
|
381
|
+
/\bsqlx?\.\w*Prepared\b/i,
|
|
382
|
+
/\bValidate\.\w+\s*\(/i,
|
|
383
|
+
],
|
|
384
|
+
assignPattern: /^\s*(?:var\s+)?(\w+)\s*(?::=|=)\s*(.+)/,
|
|
385
|
+
guards: [
|
|
386
|
+
/if[ \t]+\w+[ \t]*(?:==|!=)[ \t]*nil/i,
|
|
387
|
+
/if[ \t]+err[ \t]*!=[ \t]*nil/i,
|
|
388
|
+
/if[ \t]+!?(?:strings\.Contains|strings\.HasPrefix|regexp)\b/i,
|
|
389
|
+
/if[ \t]+len\s*\(\w+\)[ \t]*(?:==|!=|<|>|<=|>=)/i,
|
|
390
|
+
],
|
|
391
|
+
};
|
|
392
|
+
const CSHARP_PATTERNS = {
|
|
393
|
+
sources: [
|
|
394
|
+
{ pattern: /\bRequest\.(?:Form|QueryString|Query|Params|Headers|Cookies)\b/i, kind: "http-param" },
|
|
395
|
+
{ pattern: /\bRequest\.(?:Body|InputStream)\b/i, kind: "http-param" },
|
|
396
|
+
{ pattern: /\b\[FromQuery\]/i, kind: "http-param" },
|
|
397
|
+
{ pattern: /\b\[FromBody\]/i, kind: "http-param" },
|
|
398
|
+
{ pattern: /\b\[FromForm\]/i, kind: "http-param" },
|
|
399
|
+
{ pattern: /\b\[FromHeader\]/i, kind: "http-param" },
|
|
400
|
+
{ pattern: /\b\[FromRoute\]/i, kind: "url-param" },
|
|
401
|
+
{ pattern: /\bHttpContext\.Request\b/i, kind: "http-param" },
|
|
402
|
+
{ pattern: /\bEnvironment\.GetEnvironmentVariable\s*\(/i, kind: "environment" },
|
|
403
|
+
{ pattern: /\bConsole\.ReadLine\s*\(/i, kind: "user-input" },
|
|
404
|
+
{ pattern: /\bargs\[/i, kind: "user-input" },
|
|
405
|
+
{ pattern: /\bHttpClient\b.*\.(?:GetAsync|PostAsync|GetStringAsync)\s*\(/i, kind: "external-data" },
|
|
406
|
+
{ pattern: /\bJsonSerializer\.Deserialize\s*\(/i, kind: "external-data" },
|
|
407
|
+
{ pattern: /\bJsonConvert\.DeserializeObject\s*\(/i, kind: "external-data" },
|
|
408
|
+
],
|
|
409
|
+
sinks: [
|
|
410
|
+
{ pattern: /\bProcess\.Start\s*\(/i, kind: "command-exec" },
|
|
411
|
+
{ pattern: /\bProcessStartInfo\b/i, kind: "command-exec" },
|
|
412
|
+
{ pattern: /\bSqlCommand\b.*\.(?:ExecuteReader|ExecuteNonQuery|ExecuteScalar)\s*\(/i, kind: "sql-query" },
|
|
413
|
+
{ pattern: /\bnew\s+SqlCommand\s*\(\s*(?:\$"|".*\+)/i, kind: "sql-query" },
|
|
414
|
+
{ pattern: /\.(?:FromSqlRaw|ExecuteSqlRaw|SqlQuery)\s*\(/i, kind: "sql-query" },
|
|
415
|
+
{ pattern: /\bstring\.Format\s*\(.*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
416
|
+
{ pattern: /\bCSharpScript\.EvaluateAsync\s*\(/i, kind: "code-execution" },
|
|
417
|
+
{ pattern: /\bAssembly\.Load\s*\(/i, kind: "code-execution" },
|
|
418
|
+
{ pattern: /\bActivator\.CreateInstance\s*\(/i, kind: "code-execution" },
|
|
419
|
+
{ pattern: /\bBinaryFormatter\b.*\.Deserialize\s*\(/i, kind: "deserialization" },
|
|
420
|
+
{ pattern: /\bXmlSerializer\b.*\.Deserialize\s*\(/i, kind: "deserialization" },
|
|
421
|
+
{ pattern: /\bFile\.(?:ReadAllText|ReadAllBytes|ReadAllLines|Open|OpenRead)\s*\(/i, kind: "path-traversal" },
|
|
422
|
+
{ pattern: /\bPath\.Combine\s*\(.*\+/i, kind: "path-traversal" },
|
|
423
|
+
{ pattern: /\bResponse\.Redirect\s*\(/i, kind: "redirect" },
|
|
424
|
+
{ pattern: /\bRedirectToAction\s*\(/i, kind: "redirect" },
|
|
425
|
+
{ pattern: /\b@Html\.Raw\s*\(/i, kind: "xss" },
|
|
426
|
+
{ pattern: /\bHtmlHelper\b.*\.Raw\s*\(/i, kind: "xss" },
|
|
427
|
+
],
|
|
428
|
+
sanitizers: [
|
|
429
|
+
/\bHtmlEncoder\.Default\.Encode\s*\(/i,
|
|
430
|
+
/\bWebUtility\.HtmlEncode\s*\(/i,
|
|
431
|
+
/\bUrlEncoder\.Default\.Encode\s*\(/i,
|
|
432
|
+
/\bAntiXssEncoder\.\w+\s*\(/i,
|
|
433
|
+
/\b\[ValidateAntiForgeryToken\]/i,
|
|
434
|
+
/\bModelState\.IsValid\b/i,
|
|
435
|
+
/\b\[Required\]/i,
|
|
436
|
+
/\b\[StringLength\b/i,
|
|
437
|
+
/\b\[RegularExpression\b/i,
|
|
438
|
+
/\bSqlParameter\b/i,
|
|
439
|
+
/\bParameterized\b/i,
|
|
440
|
+
/\bAddWithValue\s*\(/i,
|
|
441
|
+
/\bInputValidator\b/i,
|
|
442
|
+
],
|
|
443
|
+
assignPattern: /^\s*(?:(?:var|string|int|long|double|bool|float|decimal|object|dynamic|char|byte|List|Dictionary|IEnumerable|Task)\s*(?:<[^>]+>\s*)?)?(\w+)\s*=\s*(.+);/,
|
|
444
|
+
guards: [
|
|
445
|
+
/if[ \t]*\([ \t]*\w+[ \t]*(?:==|!=)[ \t]*null/i,
|
|
446
|
+
/\bif[ \t]*\([ \t]*!?string\.IsNullOrEmpty\s*\(/i,
|
|
447
|
+
/\bif[ \t]*\([ \t]*!?string\.IsNullOrWhiteSpace\s*\(/i,
|
|
448
|
+
/\?\?[ \t]+throw\b/i,
|
|
449
|
+
/\bargument\w*Exception\b/i,
|
|
450
|
+
/\bModelState\.IsValid\b/i,
|
|
451
|
+
],
|
|
452
|
+
};
|
|
453
|
+
const RUST_PATTERNS = {
|
|
454
|
+
sources: [
|
|
455
|
+
{ pattern: /\b(?:web|actix_web)::(?:Query|Form|Json|Path)\b/i, kind: "http-param" },
|
|
456
|
+
{ pattern: /\breq\.(?:body|param|query|header)\s*\(/i, kind: "http-param" },
|
|
457
|
+
{ pattern: /\baxum::extract::(?:Query|Form|Json|Path)\b/i, kind: "http-param" },
|
|
458
|
+
{ pattern: /\bstd::env::(?:var|args)\b/i, kind: "environment" },
|
|
459
|
+
{ pattern: /\bstd::io::stdin\b/i, kind: "user-input" },
|
|
460
|
+
{ pattern: /\bserde_json::from_str\s*\(/i, kind: "external-data" },
|
|
461
|
+
{ pattern: /\breqwest::(?:get|Client)\b/i, kind: "external-data" },
|
|
462
|
+
],
|
|
463
|
+
sinks: [
|
|
464
|
+
{ pattern: /\bCommand::new\s*\(/i, kind: "command-exec" },
|
|
465
|
+
{ pattern: /\bstd::process::Command\b/i, kind: "command-exec" },
|
|
466
|
+
{ pattern: /\.(?:query|execute|query_as|query_scalar)\s*\(/i, kind: "sql-query" },
|
|
467
|
+
{ pattern: /\bformat!\s*\(.*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
468
|
+
{ pattern: /\bstd::fs::(?:read_to_string|read|write|File::open)\s*\(/i, kind: "path-traversal" },
|
|
469
|
+
{ pattern: /\bFile::open\s*\(/i, kind: "path-traversal" },
|
|
470
|
+
{ pattern: /\bserde_json::from_value\s*\(/i, kind: "deserialization" },
|
|
471
|
+
{ pattern: /\bbincode::deserialize\s*\(/i, kind: "deserialization" },
|
|
472
|
+
{ pattern: /\bRedirect::to\s*\(/i, kind: "redirect" },
|
|
473
|
+
],
|
|
474
|
+
sanitizers: [
|
|
475
|
+
/\bhtml_escape\s*\(/i,
|
|
476
|
+
/\bammonia::clean\s*\(/i,
|
|
477
|
+
/\bencode_safe\s*\(/i,
|
|
478
|
+
/\bsqlx::query!\s*\(/i,
|
|
479
|
+
/\b\.bind\s*\(/i,
|
|
480
|
+
/\.parse::<(?:i32|i64|u32|u64|f64|usize|bool)>/i,
|
|
481
|
+
/\bvalidate\s*\(\)/i,
|
|
482
|
+
/\bPath::new\s*\(.*\)\.canonicalize\s*\(/i,
|
|
483
|
+
],
|
|
484
|
+
assignPattern: /^\s*(?:let\s+(?:mut\s+)?)?(\w+)\s*(?::\s*[\w<>&, [\]]+\s*)?=\s*(.+);/,
|
|
485
|
+
guards: [
|
|
486
|
+
/\bmatch\s+\w+\s*\{/i,
|
|
487
|
+
/if[ \t]+let[ \t]+Some\b/i,
|
|
488
|
+
/\.(?:unwrap_or|unwrap_or_else|unwrap_or_default)\s*\(/i,
|
|
489
|
+
/\.is_(?:some|none|ok|err)\s*\(\)/i,
|
|
490
|
+
/\bensure!\s*\(/i,
|
|
491
|
+
/\banyhow::ensure!\s*\(/i,
|
|
492
|
+
],
|
|
493
|
+
};
|
|
494
|
+
const PHP_PATTERNS = {
|
|
495
|
+
sources: [
|
|
496
|
+
{ pattern: /\$_(?:GET|POST|REQUEST|COOKIE|SERVER|FILES)\[/i, kind: "http-param" },
|
|
497
|
+
{ pattern: /\$request->(?:input|get|post|query|all)\s*\(/i, kind: "http-param" },
|
|
498
|
+
{ pattern: /\$_ENV\[|getenv\s*\(/i, kind: "environment" },
|
|
499
|
+
{ pattern: /\$argv\b|fgets\s*\(\s*STDIN\b/i, kind: "user-input" },
|
|
500
|
+
{ pattern: /file_get_contents\s*\(\s*['"]php:\/\/input/i, kind: "http-param" },
|
|
501
|
+
{ pattern: /json_decode\s*\(\s*file_get_contents/i, kind: "external-data" },
|
|
502
|
+
{ pattern: /\$_SESSION\[/i, kind: "external-data" },
|
|
503
|
+
],
|
|
504
|
+
sinks: [
|
|
505
|
+
{ pattern: /\b(?:exec|system|passthru|shell_exec|popen|proc_open)\s*\(/i, kind: "command-exec" },
|
|
506
|
+
{ pattern: /\beval\s*\(|preg_replace\b.*\/e/i, kind: "code-execution" },
|
|
507
|
+
{ pattern: /\bmysqli?_query\s*\(/i, kind: "sql-query" },
|
|
508
|
+
{ pattern: /\$(?:pdo|db|conn)->(?:query|exec)\s*\(/i, kind: "sql-query" },
|
|
509
|
+
{ pattern: /->(?:where|whereRaw|selectRaw|orderByRaw)\s*\(/i, kind: "sql-query" },
|
|
510
|
+
{ pattern: /\binclude\s*\(|\brequire\s*\(|include_once\s*\(|require_once\s*\(/i, kind: "path-traversal" },
|
|
511
|
+
{ pattern: /\bfile_(?:get_contents|put_contents)\s*\(/i, kind: "path-traversal" },
|
|
512
|
+
{ pattern: /\bfopen\s*\(/i, kind: "path-traversal" },
|
|
513
|
+
{ pattern: /\bheader\s*\(\s*['"]Location:/i, kind: "redirect" },
|
|
514
|
+
{ pattern: /\bunserialize\s*\(/i, kind: "deserialization" },
|
|
515
|
+
{ pattern: /\becho\b|\bprint\b/i, kind: "xss" },
|
|
516
|
+
],
|
|
517
|
+
sanitizers: [
|
|
518
|
+
/\bhtmlspecialchars\s*\(/i,
|
|
519
|
+
/\bhtmlentities\s*\(/i,
|
|
520
|
+
/\bstrip_tags\s*\(/i,
|
|
521
|
+
/\baddslashes\s*\(/i,
|
|
522
|
+
/\bmysqli?_real_escape_string\s*\(/i,
|
|
523
|
+
/\bPDO::quote\s*\(/i,
|
|
524
|
+
/->(?:prepare|bindParam|bindValue)\s*\(/i,
|
|
525
|
+
/\bintval\s*\(|\bfloatval\s*\(|\b\(int\)|\b\(float\)/i,
|
|
526
|
+
/\bfilter_(?:var|input)\s*\(/i,
|
|
527
|
+
/\bpreg_match\s*\(/i,
|
|
528
|
+
/\brealpath\s*\(|basename\s*\(/i,
|
|
529
|
+
],
|
|
530
|
+
assignPattern: /^\s*\$(\w+)\s*=\s*(.+);/,
|
|
531
|
+
guards: [
|
|
532
|
+
/if[ \t]*\([ \t]*!?(?:isset|empty|is_null|is_numeric|is_string|is_array)\s*\(/i,
|
|
533
|
+
/if[ \t]*\([ \t]*!?\$\w+\s*(?:===?|!==?)\s*(?:null|false|''|"")\b/i,
|
|
534
|
+
/\bvalidate\s*\(/i,
|
|
535
|
+
/\bpreg_match\s*\(/i,
|
|
536
|
+
/\bfilter_(?:var|input)\s*\(/i,
|
|
537
|
+
],
|
|
538
|
+
};
|
|
539
|
+
const RUBY_PATTERNS = {
|
|
540
|
+
sources: [
|
|
541
|
+
{ pattern: /\bparams\[/i, kind: "http-param" },
|
|
542
|
+
{ pattern: /\bparams\.(?:require|permit|fetch)\s*\(/i, kind: "http-param" },
|
|
543
|
+
{ pattern: /\brequest\.(?:body|env|headers|params)\b/i, kind: "http-param" },
|
|
544
|
+
{ pattern: /\bENV\[|ENV\.fetch\s*\(/i, kind: "environment" },
|
|
545
|
+
{ pattern: /\bARGV\b|\bgets\b|\breadline\b/i, kind: "user-input" },
|
|
546
|
+
{ pattern: /\bJSON\.parse\s*\(/i, kind: "external-data" },
|
|
547
|
+
{ pattern: /\bNet::HTTP\b.*\.(?:get|post)\s*\(/i, kind: "external-data" },
|
|
548
|
+
{ pattern: /\bsession\[/i, kind: "external-data" },
|
|
549
|
+
{ pattern: /\bcookies\[/i, kind: "http-param" },
|
|
550
|
+
],
|
|
551
|
+
sinks: [
|
|
552
|
+
{ pattern: /\bsystem\s*\(|\bexec\s*\(|\b`[^`]*#\{/i, kind: "command-exec" },
|
|
553
|
+
{ pattern: /\b%x\{|Kernel\.system\s*\(/i, kind: "command-exec" },
|
|
554
|
+
{ pattern: /\beval\s*\(|instance_eval\s*\(|class_eval\s*\(/i, kind: "code-execution" },
|
|
555
|
+
{ pattern: /\bsend\s*\(|public_send\s*\(/i, kind: "code-execution" },
|
|
556
|
+
{ pattern: /\.(?:where|find_by_sql|execute|select)\s*\(\s*(?:"|'|%|#)/i, kind: "sql-query" },
|
|
557
|
+
{ pattern: /\.connection\.execute\s*\(/i, kind: "sql-query" },
|
|
558
|
+
{ pattern: /\bFile\.(?:open|read|write|delete)\s*\(/i, kind: "path-traversal" },
|
|
559
|
+
{ pattern: /\bredirect_to\s*\(/i, kind: "redirect" },
|
|
560
|
+
{ pattern: /\bMarshal\.load\s*\(|YAML\.load\s*\(/i, kind: "deserialization" },
|
|
561
|
+
{ pattern: /\b\.html_safe\b/i, kind: "xss" },
|
|
562
|
+
{ pattern: /\braw\s*\(/i, kind: "xss" },
|
|
563
|
+
],
|
|
564
|
+
sanitizers: [
|
|
565
|
+
/\bERB::Util\.html_escape\s*\(/i,
|
|
566
|
+
/\bCGI\.escapeHTML\s*\(/i,
|
|
567
|
+
/\bsanitize\s*\(/i,
|
|
568
|
+
/\bparams\.(?:require|permit)\s*\(/i,
|
|
569
|
+
/\.to_i\b|\.to_f\b/i,
|
|
570
|
+
/\bActiveRecord::Base\.connection\.quote\s*\(/i,
|
|
571
|
+
/\.(?:where|find_by)\s*\(\s*\w+\s*:\s/i,
|
|
572
|
+
/\bMarshal\.safe_load\b|YAML\.safe_load\s*\(/i,
|
|
573
|
+
/\bRegexp\.match\s*\(/i,
|
|
574
|
+
/\bFile\.expand_path\b.*\.start_with\?\s*\(/i,
|
|
575
|
+
],
|
|
576
|
+
assignPattern: /^\s*(\w+)\s*=\s*(.+)/,
|
|
577
|
+
guards: [
|
|
578
|
+
/\bunless\s+\w+\.(?:nil\?|blank\?|empty\?)\b/i,
|
|
579
|
+
/if[ \t]+\w+\.(?:present\?|valid\?)\b/i,
|
|
580
|
+
/\braise\s+\w+Error\b/i,
|
|
581
|
+
/\.(?:validates?|validate!)\s/i,
|
|
582
|
+
],
|
|
583
|
+
};
|
|
584
|
+
const KOTLIN_PATTERNS = {
|
|
585
|
+
sources: [
|
|
586
|
+
{ pattern: /\brequest\.(?:getParameter|getAttribute|getHeader)\s*\(/i, kind: "http-param" },
|
|
587
|
+
{ pattern: /\b@RequestParam\b|\b@PathVariable\b|\b@RequestBody\b/i, kind: "http-param" },
|
|
588
|
+
{ pattern: /\bcall\.receive\b/i, kind: "http-param" },
|
|
589
|
+
{ pattern: /\bcall\.parameters\[/i, kind: "http-param" },
|
|
590
|
+
{ pattern: /\bSystem\.getenv\s*\(/i, kind: "environment" },
|
|
591
|
+
{ pattern: /\breadLine\s*\(\)|Scanner\s*\(\s*System\.`in`\)/i, kind: "user-input" },
|
|
592
|
+
{ pattern: /\bargs\[/i, kind: "user-input" },
|
|
593
|
+
{ pattern: /\bGson\(\)\.fromJson\s*\(/i, kind: "external-data" },
|
|
594
|
+
{ pattern: /\bJson\.decodeFromString\s*\(/i, kind: "external-data" },
|
|
595
|
+
],
|
|
596
|
+
sinks: [
|
|
597
|
+
{ pattern: /\bRuntime\.getRuntime\(\)\.exec\s*\(/i, kind: "command-exec" },
|
|
598
|
+
{ pattern: /\bProcessBuilder\s*\(/i, kind: "command-exec" },
|
|
599
|
+
{ pattern: /\.(?:executeQuery|executeUpdate|createQuery|nativeQuery)\s*\(/i, kind: "sql-query" },
|
|
600
|
+
{ pattern: /\bString\.format\s*\(.*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
601
|
+
{ pattern: /\"\$\{?\w+\}?.*(?:SELECT|INSERT|UPDATE|DELETE)\b/i, kind: "sql-query" },
|
|
602
|
+
{ pattern: /\bFile\s*\(\s*(?:\$|[^")]+\+)/i, kind: "path-traversal" },
|
|
603
|
+
{ pattern: /\bScriptEngine\b.*\.eval\s*\(/i, kind: "code-execution" },
|
|
604
|
+
{ pattern: /\bObjectInputStream\b.*\.readObject\s*\(/i, kind: "deserialization" },
|
|
605
|
+
],
|
|
606
|
+
sanitizers: [
|
|
607
|
+
/\bPreparedStatement\b/i,
|
|
608
|
+
/\bEncoder\.encode\s*\(/i,
|
|
609
|
+
/\bHtmlUtils\.htmlEscape\s*\(/i,
|
|
610
|
+
/\bStringEscapeUtils\.escape\w+\s*\(/i,
|
|
611
|
+
/\b@Valid\b|\b@Validated\b/i,
|
|
612
|
+
/\brequire\s*\{|check\s*\{/i,
|
|
613
|
+
/\.(?:toIntOrNull|toLongOrNull|toDoubleOrNull)\s*\(/i,
|
|
614
|
+
/\bRegex\s*\(.*\)\.matches\s*\(/i,
|
|
615
|
+
],
|
|
616
|
+
assignPattern: /^\s*(?:(?:val|var|private|internal)\s+)?(\w+)\s*(?::\s*[\w<>?, [\]]+\s*)?=\s*(.+)/,
|
|
617
|
+
guards: [
|
|
618
|
+
/if[ \t]*\([ \t]*\w+[ \t]*(?:==|!=)[ \t]*null\b/i,
|
|
619
|
+
/\?\.\s*let\s*\{/i,
|
|
620
|
+
/\brequire\s*\(/i,
|
|
621
|
+
/\bcheck\s*\(/i,
|
|
622
|
+
/if[ \t]*\([ \t]*!?\w+\.(?:isBlank|isEmpty|isNullOrBlank|isNullOrEmpty)\s*\(/i,
|
|
623
|
+
],
|
|
624
|
+
};
|
|
625
|
+
const SWIFT_PATTERNS = {
|
|
626
|
+
sources: [
|
|
627
|
+
{ pattern: /\breq\.(?:content|query|parameters)\b/i, kind: "http-param" },
|
|
628
|
+
{ pattern: /\brequest\.(?:content|query|body)\b/i, kind: "http-param" },
|
|
629
|
+
{ pattern: /\bURLComponents\b.*\.queryItems\b/i, kind: "url-param" },
|
|
630
|
+
{ pattern: /\bProcessInfo\.processInfo\.environment\[/i, kind: "environment" },
|
|
631
|
+
{ pattern: /\bCommandLine\.arguments\b/i, kind: "user-input" },
|
|
632
|
+
{ pattern: /\breadLine\s*\(/i, kind: "user-input" },
|
|
633
|
+
{ pattern: /\bJSONDecoder\(\)\.decode\s*\(/i, kind: "external-data" },
|
|
634
|
+
{ pattern: /\bURLSession\b.*\.data\s*\(/i, kind: "external-data" },
|
|
635
|
+
],
|
|
636
|
+
sinks: [
|
|
637
|
+
{ pattern: /\bProcess\(\)\s*.*arguments/i, kind: "command-exec" },
|
|
638
|
+
{ pattern: /\bNSTask\b/i, kind: "command-exec" },
|
|
639
|
+
{ pattern: /\.(?:execute|prepare)\s*\(\s*(?:".*\\|".*\+)/i, kind: "sql-query" },
|
|
640
|
+
{ pattern: /\bFileManager\b.*\.(?:contentsOfFile|createFile)\s*\(/i, kind: "path-traversal" },
|
|
641
|
+
{ pattern: /\bURL\s*\(\s*fileURLWithPath:\s*(?:\w+\s*\+|"\\)/i, kind: "path-traversal" },
|
|
642
|
+
{ pattern: /\bJSContext\b.*\.evaluateScript\s*\(/i, kind: "code-execution" },
|
|
643
|
+
{ pattern: /\bNSExpression\s*\(/i, kind: "code-execution" },
|
|
644
|
+
{ pattern: /\bNSKeyedUnarchiver\b.*\.unarchiveObject\s*\(/i, kind: "deserialization" },
|
|
645
|
+
{ pattern: /\bResponse\.redirect\s*\(/i, kind: "redirect" },
|
|
646
|
+
],
|
|
647
|
+
sanitizers: [
|
|
648
|
+
/\baddingPercentEncoding\s*\(/i,
|
|
649
|
+
/\.replacingOccurrences\s*\(of:.*with:/i,
|
|
650
|
+
/\bInt\s*\(|Double\s*\(|Float\s*\(/i,
|
|
651
|
+
/\bNSRegularExpression\b/i,
|
|
652
|
+
/\bguard\s+let\b/i,
|
|
653
|
+
/\b\.standardizedFileURL\b|\.resolvingSymlinksInPath\b/i,
|
|
654
|
+
],
|
|
655
|
+
assignPattern: /^\s*(?:(?:let|var)\s+)?(\w+)\s*(?::\s*[\w<>?, [\]?!]+\s*)?=\s*(.+)/,
|
|
656
|
+
guards: [
|
|
657
|
+
/guard[ \t]+let\b/i,
|
|
658
|
+
/if[ \t]+let\b/i,
|
|
659
|
+
/guard[ \t]+!?\w+\.(?:isEmpty|isNil)\b/i,
|
|
660
|
+
/\bprecondition\s*\(/i,
|
|
661
|
+
/\bassert\s*\(/i,
|
|
662
|
+
],
|
|
663
|
+
};
|
|
664
|
+
// Map normalized languages to their pattern sets
|
|
665
|
+
const LANGUAGE_PATTERN_MAP = {
|
|
666
|
+
python: PYTHON_PATTERNS,
|
|
667
|
+
java: JAVA_PATTERNS,
|
|
668
|
+
go: GO_PATTERNS,
|
|
669
|
+
csharp: CSHARP_PATTERNS,
|
|
670
|
+
rust: RUST_PATTERNS,
|
|
671
|
+
php: PHP_PATTERNS,
|
|
672
|
+
ruby: RUBY_PATTERNS,
|
|
673
|
+
kotlin: KOTLIN_PATTERNS,
|
|
674
|
+
swift: SWIFT_PATTERNS,
|
|
675
|
+
};
|
|
211
676
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
212
677
|
/**
|
|
213
678
|
* Analyze a source file for taint flows: paths from untrusted input to
|
|
214
679
|
* dangerous sinks through variable assignments and string concatenation.
|
|
215
680
|
*
|
|
216
681
|
* For JS/TS, uses the TypeScript compiler AST for precise variable tracking.
|
|
217
|
-
* For
|
|
682
|
+
* For Python, Java, Go, C#, and Rust: uses language-specific source/sink/
|
|
683
|
+
* sanitizer patterns for deeper analysis.
|
|
684
|
+
* For other languages, falls back to generic regex-based analysis.
|
|
218
685
|
*/
|
|
219
686
|
export function analyzeTaintFlows(code, language) {
|
|
220
687
|
const lang = normalizeLanguage(language);
|
|
@@ -222,8 +689,10 @@ export function analyzeTaintFlows(code, language) {
|
|
|
222
689
|
case "javascript":
|
|
223
690
|
case "typescript":
|
|
224
691
|
return analyzeTypeScriptTaint(code, lang);
|
|
225
|
-
default:
|
|
226
|
-
|
|
692
|
+
default: {
|
|
693
|
+
const langPatterns = LANGUAGE_PATTERN_MAP[lang];
|
|
694
|
+
return analyzeRegexTaint(code, langPatterns);
|
|
695
|
+
}
|
|
227
696
|
}
|
|
228
697
|
}
|
|
229
698
|
// ─── TypeScript / JavaScript Taint Analysis ──────────────────────────────────
|
|
@@ -423,13 +892,55 @@ function analyzeTypeScriptTaint(code, language) {
|
|
|
423
892
|
return deduplicateFlows(flows);
|
|
424
893
|
}
|
|
425
894
|
// ─── Regex-based Taint Analysis (non-JS/TS languages) ────────────────────────
|
|
426
|
-
|
|
895
|
+
/**
|
|
896
|
+
* Language-aware sanitizer check: combines global sanitizers with
|
|
897
|
+
* language-specific ones when available.
|
|
898
|
+
*/
|
|
899
|
+
function isLangSanitized(expression, langPatterns) {
|
|
900
|
+
if (isSanitized(expression))
|
|
901
|
+
return true;
|
|
902
|
+
if (langPatterns) {
|
|
903
|
+
for (const p of langPatterns.sanitizers) {
|
|
904
|
+
if (p.test(expression))
|
|
905
|
+
return true;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Language-aware guard clause detection: combines global guards with
|
|
912
|
+
* language-specific guard patterns.
|
|
913
|
+
*/
|
|
914
|
+
function detectLangGuardClauses(varName, sourceLine, sinkLine, codeLines, langPatterns) {
|
|
915
|
+
const baseReduction = detectGuardClauses(varName, sourceLine, sinkLine, codeLines);
|
|
916
|
+
if (!langPatterns)
|
|
917
|
+
return baseReduction;
|
|
918
|
+
const start = Math.min(sourceLine, sinkLine) - 1;
|
|
919
|
+
const end = Math.max(sourceLine, sinkLine);
|
|
920
|
+
let extraGuards = 0;
|
|
921
|
+
for (let i = start; i < end && i < codeLines.length; i++) {
|
|
922
|
+
const line = codeLines[i];
|
|
923
|
+
if (!containsWordBoundary(line, varName))
|
|
924
|
+
continue;
|
|
925
|
+
for (const guard of langPatterns.guards) {
|
|
926
|
+
if (guard.test(line)) {
|
|
927
|
+
extraGuards++;
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return Math.min(baseReduction + extraGuards * 0.1, 0.35);
|
|
933
|
+
}
|
|
934
|
+
function analyzeRegexTaint(code, langPatterns) {
|
|
427
935
|
const codeLines = code.split("\n");
|
|
428
936
|
const flows = [];
|
|
429
937
|
// Track tainted variable names
|
|
430
938
|
const tainted = new Map();
|
|
431
|
-
//
|
|
432
|
-
const
|
|
939
|
+
// Merge source and sink patterns: language-specific + global
|
|
940
|
+
const allSources = langPatterns ? [...langPatterns.sources, ...SOURCE_PATTERNS] : SOURCE_PATTERNS;
|
|
941
|
+
const allSinks = langPatterns ? [...langPatterns.sinks, ...SINK_PATTERNS] : SINK_PATTERNS;
|
|
942
|
+
// Use language-specific assignment pattern if available
|
|
943
|
+
const assignPattern = langPatterns?.assignPattern ?? /^\s*(?:(?:let|const|var|val|auto)\s+)?(\w+)\s*[:=]\s*(.+)/;
|
|
433
944
|
for (let i = 0; i < codeLines.length; i++) {
|
|
434
945
|
const line = codeLines[i];
|
|
435
946
|
const lineNum = i + 1;
|
|
@@ -438,10 +949,10 @@ function analyzeRegexTaint(code) {
|
|
|
438
949
|
if (assignMatch) {
|
|
439
950
|
const [, varName, rhs] = assignMatch;
|
|
440
951
|
// Skip sanitized assignments
|
|
441
|
-
if (
|
|
952
|
+
if (isLangSanitized(rhs, langPatterns))
|
|
442
953
|
continue;
|
|
443
954
|
// Direct source
|
|
444
|
-
for (const src of
|
|
955
|
+
for (const src of allSources) {
|
|
445
956
|
if (src.pattern.test(rhs)) {
|
|
446
957
|
tainted.set(varName, {
|
|
447
958
|
sourceExpr: rhs.trim(),
|
|
@@ -462,16 +973,16 @@ function analyzeRegexTaint(code) {
|
|
|
462
973
|
}
|
|
463
974
|
}
|
|
464
975
|
// Skip lines with sanitizers for sink checking
|
|
465
|
-
if (
|
|
976
|
+
if (isLangSanitized(line, langPatterns))
|
|
466
977
|
continue;
|
|
467
978
|
// Check for sinks using tainted data
|
|
468
|
-
for (const sink of
|
|
979
|
+
for (const sink of allSinks) {
|
|
469
980
|
if (!sink.pattern.test(line))
|
|
470
981
|
continue;
|
|
471
982
|
// Check tainted variables (word-boundary aware)
|
|
472
983
|
for (const [varName, info] of tainted) {
|
|
473
984
|
if (containsWordBoundary(line, varName) && lineNum !== info.sourceLine) {
|
|
474
|
-
const guardReduction =
|
|
985
|
+
const guardReduction = detectLangGuardClauses(varName, info.sourceLine, lineNum, codeLines, langPatterns);
|
|
475
986
|
flows.push({
|
|
476
987
|
source: {
|
|
477
988
|
line: info.sourceLine,
|
|
@@ -486,7 +997,7 @@ function analyzeRegexTaint(code) {
|
|
|
486
997
|
}
|
|
487
998
|
}
|
|
488
999
|
// Inline source→sink
|
|
489
|
-
for (const src of
|
|
1000
|
+
for (const src of allSources) {
|
|
490
1001
|
if (src.pattern.test(line)) {
|
|
491
1002
|
const alreadyCaptured = flows.some((f) => f.sink.line === lineNum);
|
|
492
1003
|
if (!alreadyCaptured) {
|