@kevinrabun/judges 3.20.14 → 3.21.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 +35 -0
- 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/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.map +1 -1
- package/dist/evaluators/index.js +5 -1
- 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 +200 -0
- 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/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/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-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 +18 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/server.json +18 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to **@kevinrabun/judges** are documented here.
|
|
4
4
|
|
|
5
|
+
## [3.21.0] — 2026-03-05
|
|
6
|
+
|
|
7
|
+
### Added — P0: GitHub Action CI/CD
|
|
8
|
+
- **PR inline review comments** — New `pr-review` input in `action.yml` posts findings as inline PR review comments with severity badges, auto-fix hints, and judge attribution
|
|
9
|
+
- **Diff-only mode** — New `diff-only` input restricts analysis to changed files using `git diff`, dramatically reducing CI noise on large repos
|
|
10
|
+
- **Baseline filtering** — New `baseline-file` input suppresses known findings via a baseline JSON, surfacing only new issues in PRs
|
|
11
|
+
- **Improved step summary** — GitHub Actions summary now includes findings table, score badge, and must-fix gate status
|
|
12
|
+
|
|
13
|
+
### Added — P1: Core Engine Enhancements
|
|
14
|
+
- **AST context in more evaluators** — `AnalyzeContext` interface pipes tree-sitter AST data into cybersecurity (scope-aware taint), performance (async/complexity detection), and authentication (decorator/import awareness) evaluators
|
|
15
|
+
- **`fix_code` MCP tool** — New tool evaluates code and auto-applies all available patches, returning fixed code + summary of remaining findings
|
|
16
|
+
- **Multi-language framework evaluators** — Extended `framework-safety.ts` from JS/TS-only to 8 frameworks: Django (6 rules), Flask (4), FastAPI (1), Spring Boot (6), ASP.NET Core (6), Go/Gin/Echo/Fiber (5)
|
|
17
|
+
|
|
18
|
+
### Added — P2: Depth & Tooling
|
|
19
|
+
- **20+ new auto-fix patches** — Added patches for Python (7), Go (2), Java (5), C# (4), Rust (2) covering SQL injection, command injection, weak hashing, empty catch, and more
|
|
20
|
+
- **VS Code findings panel** — TreeView-based panel with sort-by-severity/judge, filter controls, go-to-line navigation, and 7 new commands (`judges.showFindingsPanel`, `judges.sortBySeverity`, etc.)
|
|
21
|
+
- **Cross-file type/state tracking** — Three new project-level detectors: `detectSharedMutableState()`, `detectTypeSafetyGaps()`, `detectScatteredEnvAccess()` in `project.ts`
|
|
22
|
+
- **Taint tracker language depth** — Expanded from 5 to 9 language-specific pattern sets with `LanguagePatternSet` interface; each set defines sources, sinks, sanitizers, assign patterns, and guard conditions
|
|
23
|
+
|
|
24
|
+
### Added — P3: Breadth & Polish
|
|
25
|
+
- **PHP/Ruby/Kotlin/Swift language support** — Added 4 new languages to `LangFamily`, expanded all ~35 pattern constants in `language-patterns.ts`, added 4 complete taint tracker pattern sets (PHP: 7 sources/11 sinks/11 sanitizers, Ruby: 9/11/10, Kotlin: 9/8/8, Swift: 8/9/6)
|
|
26
|
+
- **Performance & snapshot tests** — 3 new test suites: performance budgets (tribunal <5s, per-judge <500ms, evaluateDiff <3s, large-block <15s), rule coverage stability (≥30 judges, 100-600 findings, required families, severity distribution), multi-language pattern coverage (8 tests for PHP/Ruby/Kotlin/Swift)
|
|
27
|
+
- **Framework version awareness** — `detectFrameworkVersions()` extracts versions from 14 manifest/config patterns; `getVersionConfidenceAdjustment()` applies version-specific confidence rules for Django 4+, Spring 3+, Next.js 13+/14+, Express 5+, Rails 6+/7+, Laravel 9+, ASP.NET 8+; integrated into `applyFrameworkAwareness()`
|
|
28
|
+
- **MCP workspace & streaming tools** — 3 new MCP tools: `list_files` (recursive directory listing with skip-dirs), `read_file` (content reading with line-range slicing), `evaluate_with_progress` (progressive judge-by-judge reporting with count updates)
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- **MCP tool count** — 10 → 13 tools registered in `server.json`
|
|
32
|
+
- **`applyFrameworkAwareness()` rewritten** — Now combines framework mitigation with version-aware confidence adjustments and stacked provenance notes
|
|
33
|
+
- **`register.ts` modular architecture** — Now orchestrates 4 registration modules: evaluation, workflow, fix, workspace
|
|
34
|
+
|
|
35
|
+
### Tests
|
|
36
|
+
- 19 new performance/snapshot/multi-language tests in `judges.test.ts`
|
|
37
|
+
- 19 new framework version awareness tests in `subsystems.test.ts`
|
|
38
|
+
- 1006 tests in judges.test.ts, 392 tests in subsystems.test.ts — all passing
|
|
39
|
+
|
|
5
40
|
## [3.20.14] — 2026-03-04
|
|
6
41
|
|
|
7
42
|
### Added
|
|
@@ -29,7 +29,9 @@ export type TaintSinkKind = "code-execution" | "command-exec" | "sql-query" | "x
|
|
|
29
29
|
* dangerous sinks through variable assignments and string concatenation.
|
|
30
30
|
*
|
|
31
31
|
* For JS/TS, uses the TypeScript compiler AST for precise variable tracking.
|
|
32
|
-
* For
|
|
32
|
+
* For Python, Java, Go, C#, and Rust: uses language-specific source/sink/
|
|
33
|
+
* sanitizer patterns for deeper analysis.
|
|
34
|
+
* For other languages, falls back to generic regex-based analysis.
|
|
33
35
|
*/
|
|
34
36
|
export declare function analyzeTaintFlows(code: string, language: string): TaintFlow[];
|
|
35
37
|
//# sourceMappingURL=taint-tracker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taint-tracker.d.ts","sourceRoot":"","sources":["../../src/ast/taint-tracker.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,0CAA0C;IAC1C,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,eAAe,CAAC;KACvB,CAAC;IACF,kDAAkD;IAClD,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,aAAa,CAAC;KACrB,CAAC;IACF,qDAAqD;IACrD,aAAa,EAAE,KAAK,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,WAAW,GACX,eAAe,CAAC;AAEpB,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,cAAc,GACd,WAAW,GACX,KAAK,GACL,gBAAgB,GAChB,UAAU,GACV,UAAU,GACV,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"taint-tracker.d.ts","sourceRoot":"","sources":["../../src/ast/taint-tracker.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,0CAA0C;IAC1C,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,eAAe,CAAC;KACvB,CAAC;IACF,kDAAkD;IAClD,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,aAAa,CAAC;KACrB,CAAC;IACF,qDAAqD;IACrD,aAAa,EAAE,KAAK,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,WAAW,GACX,eAAe,CAAC;AAEpB,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,cAAc,GACd,WAAW,GACX,KAAK,GACL,gBAAgB,GAChB,UAAU,GACV,UAAU,GACV,iBAAiB,CAAC;AAguBtB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CAY7E"}
|
|
@@ -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) {
|