circle-ir 3.194.0 → 3.197.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/configs/README.md +39 -0
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +43 -3
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +624 -2
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +68 -1
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-matcher-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-matcher-pass.js.map +1 -1
- package/dist/analysis/rules.d.ts.map +1 -1
- package/dist/analysis/rules.js +42 -0
- package/dist/analysis/rules.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +24 -8
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +582 -604
- package/dist/core/circle-ir-core.cjs +59 -13
- package/dist/core/circle-ir-core.js +59 -13
- package/dist/languages/plugins/bash.d.ts.map +1 -1
- package/dist/languages/plugins/bash.js +12 -16
- package/dist/languages/plugins/bash.js.map +1 -1
- package/dist/languages/plugins/go.d.ts +4 -0
- package/dist/languages/plugins/go.d.ts.map +1 -1
- package/dist/languages/plugins/go.js +16 -32
- package/dist/languages/plugins/go.js.map +1 -1
- package/dist/languages/plugins/java.d.ts +9 -1
- package/dist/languages/plugins/java.d.ts.map +1 -1
- package/dist/languages/plugins/java.js +16 -127
- package/dist/languages/plugins/java.js.map +1 -1
- package/dist/languages/plugins/javascript.d.ts +7 -0
- package/dist/languages/plugins/javascript.d.ts.map +1 -1
- package/dist/languages/plugins/javascript.js +10 -117
- package/dist/languages/plugins/javascript.js.map +1 -1
- package/dist/languages/plugins/python.d.ts +7 -0
- package/dist/languages/plugins/python.d.ts.map +1 -1
- package/dist/languages/plugins/python.js +24 -177
- package/dist/languages/plugins/python.js.map +1 -1
- package/dist/languages/plugins/rust.d.ts +9 -0
- package/dist/languages/plugins/rust.d.ts.map +1 -1
- package/dist/languages/plugins/rust.js +22 -110
- package/dist/languages/plugins/rust.js.map +1 -1
- package/dist/languages/types.d.ts +3 -3
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/types/config.d.ts +15 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +13 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +37 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# `configs/` — documentation and export surface (not runtime)
|
|
2
|
+
|
|
3
|
+
**Nothing in `src/` reads these files.** circle-ir must run in the browser and
|
|
4
|
+
in Cloudflare Workers, so the library cannot touch the filesystem at all. The
|
|
5
|
+
taint patterns that actually drive analysis live in TypeScript:
|
|
6
|
+
|
|
7
|
+
| Surface | Location | Role |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `DEFAULT_SOURCES` / `DEFAULT_SINKS` / `DEFAULT_SANITIZERS` / `DEFAULT_SINK_SEMANTICS` | `src/analysis/config-loader.ts` | **Canonical.** Language-agnostic by default; scope a pattern with `languages: ['python']` when its method name collides across ecosystems. |
|
|
10
|
+
| `LanguagePlugin.getBuiltinSources()` / `getBuiltinSinks()` | `src/languages/plugins/<lang>.ts` | Supplement for language-specific patterns that have **no** `DEFAULT_*` counterpart. `TaintMatcherPass` merges them after the canonical registry. |
|
|
11
|
+
| `configs/**` (this directory) | — | Documentation, and an export for downstream tools that want a machine-readable view of the ruleset. Editing a file here changes nothing about how code is analysed. |
|
|
12
|
+
|
|
13
|
+
See ADR-004 in `.specifica/mvp/design.md` for the rationale.
|
|
14
|
+
|
|
15
|
+
## Adding or fixing a pattern
|
|
16
|
+
|
|
17
|
+
Add it to `DEFAULT_SINKS` / `DEFAULT_SOURCES` in `src/analysis/config-loader.ts`
|
|
18
|
+
(scoped with `languages` if it is language-specific). Only reach for a plugin
|
|
19
|
+
builtin when the pattern genuinely has no place in the canonical registry.
|
|
20
|
+
|
|
21
|
+
Never register the same `(class, method, cwe)` in both surfaces:
|
|
22
|
+
`findSinks` dedupes by `location:line:cwe` and keeps the higher-confidence
|
|
23
|
+
match, so the duplicate is invisible — you can "fix" the copy that loses and
|
|
24
|
+
see no change in behaviour. That failure mode cost an extra investigation round
|
|
25
|
+
on Issue #4 (`yaml.safe_load`). It is now locked by
|
|
26
|
+
`tests/languages/sink-registry-contract.test.ts`.
|
|
27
|
+
|
|
28
|
+
## Drift
|
|
29
|
+
|
|
30
|
+
Because these files are not executable, they lag the runtime registries. To see
|
|
31
|
+
by how much:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm run config:drift
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The report lists documented sinks that no longer exist at runtime, and entries
|
|
38
|
+
whose vulnerability type disagrees with the code. It is informational and never
|
|
39
|
+
fails the build — treat a large delta as a documentation debt signal, not a bug.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/analysis/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAEjD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAiB1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG;IACtD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAcA;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,mBAAmB,EAAE,GAC7B,kBAAkB,EAAE,CAQtB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EAAE,EACxB,YAAY,EAAE,MAAM,EAAE,EACtB,qBAAqB,GAAE,MAAM,EAAO,GACnC,WAAW,CAYb;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/analysis/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAEjD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAiB1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG;IACtD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAcA;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,mBAAmB,EAAE,GAC7B,kBAAkB,EAAE,CAQtB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EAAE,EACxB,YAAY,EAAE,MAAM,EAAE,EACtB,qBAAqB,GAAE,MAAM,EAAO,GACnC,WAAW,CAYb;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,EAivB1C,CAAC;AA+KF,eAAO,MAAM,aAAa,EAAE,WAAW,EA+yDtC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,gBAAgB,EA+hBhD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,EAiDtD,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAO9C;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,UAAU,EA8F5C,CAAC"}
|
|
@@ -500,7 +500,11 @@ export const DEFAULT_SOURCES = [
|
|
|
500
500
|
{ method: 'Json', type: 'http_body', severity: 'high', return_tainted: true, languages: ['rust'] },
|
|
501
501
|
{ method: 'Query', type: 'http_param', severity: 'high', return_tainted: true, languages: ['rust'] },
|
|
502
502
|
{ method: 'Path', type: 'http_path', severity: 'high', return_tainted: true, languages: ['rust'] },
|
|
503
|
-
|
|
503
|
+
// `Form<T>` decodes the urlencoded request *body* (`Query<T>` above is the
|
|
504
|
+
// query-string extractor), so it is http_body — retyped from http_param
|
|
505
|
+
// when the RustPlugin's http_body duplicate was removed, so the two
|
|
506
|
+
// surfaces no longer disagree. Matches Gin's `Context.PostForm`.
|
|
507
|
+
{ method: 'Form', type: 'http_body', severity: 'high', return_tainted: true, languages: ['rust'] },
|
|
504
508
|
// Rust std library
|
|
505
509
|
{ method: 'var', class: 'env', type: 'env_input', severity: 'medium', return_tainted: true },
|
|
506
510
|
{ method: 'var_os', class: 'env', type: 'env_input', severity: 'medium', return_tainted: true },
|
|
@@ -1823,7 +1827,11 @@ export const DEFAULT_SINKS = [
|
|
|
1823
1827
|
// Also match without receiver (destructured imports: const { exec } = require('child_process'))
|
|
1824
1828
|
// `exec` is intentionally classless: catches Node.js child_process.exec AND
|
|
1825
1829
|
// Java Runtime.exec (via `r.exec()` where heuristic can't resolve r → Runtime).
|
|
1826
|
-
|
|
1830
|
+
// Excluded from Python: there, bare `exec(code)` is the code-execution
|
|
1831
|
+
// builtin, registered below as code_injection / CWE-94. Before the exclusion
|
|
1832
|
+
// a Python `exec(user_code)` matched both patterns and emitted two findings
|
|
1833
|
+
// for one call — CWE-78 (wrong: no shell is involved) and CWE-94 (right).
|
|
1834
|
+
{ method: 'exec', type: 'command_injection', cwe: 'CWE-78', severity: 'high', arg_positions: [0], exclude_languages: ['python'] },
|
|
1827
1835
|
// `execSync`/`spawn`/`spawnSync`/`execFile` are Node-specific — language-scope them.
|
|
1828
1836
|
{ method: 'execSync', type: 'command_injection', cwe: 'CWE-78', severity: 'high', arg_positions: [0], languages: ['javascript', 'typescript'] },
|
|
1829
1837
|
{ method: 'spawn', type: 'command_injection', cwe: 'CWE-78', severity: 'high', arg_positions: [0], languages: ['javascript', 'typescript'] },
|
|
@@ -1851,7 +1859,11 @@ export const DEFAULT_SINKS = [
|
|
|
1851
1859
|
// Note: classless { method: 'query' } removed — too many FPs (UriComponentsBuilder.query(), etc.)
|
|
1852
1860
|
// SQL query calls are covered by class-specific patterns above (Connection, Pool, Client, JdbcTemplate)
|
|
1853
1861
|
// Note: `raw` is shared with Python (Django ORM) — scoped to JS+TS to avoid leaking.
|
|
1854
|
-
|
|
1862
|
+
// Severity `critical` (not `high`) preserves what the JS plugin's duplicate
|
|
1863
|
+
// builtin emitted before the #4 follow-up consolidation removed it: the
|
|
1864
|
+
// plugin copy carried `critical`, which won the `findSinks` dedup slot via
|
|
1865
|
+
// the +0.1 critical-severity confidence boost.
|
|
1866
|
+
{ method: 'raw', type: 'sql_injection', cwe: 'CWE-89', severity: 'critical', arg_positions: [0], languages: ['javascript', 'typescript'] },
|
|
1855
1867
|
// sqlite3 (npm) — Database/Statement methods. The JS plugin resolves
|
|
1856
1868
|
// `const db = new sqlite3.Database(...); db.all(sql)` to the resolution
|
|
1857
1869
|
// target `Connection.all`, so class-scoped patterns matching `Connection`
|
|
@@ -2410,10 +2422,30 @@ export const DEFAULT_SINKS = [
|
|
|
2410
2422
|
{ method: 'Sprintf', class: 'fmt', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [0], languages: ['go'] },
|
|
2411
2423
|
{ method: 'Printf', class: 'fmt', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [0], languages: ['go'] },
|
|
2412
2424
|
{ method: 'Errorf', class: 'fmt', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [0], languages: ['go'] },
|
|
2425
|
+
// `fmt.Fprintf` is deliberately dual-classified: this CWE-134 entry and the
|
|
2426
|
+
// Go plugin's xss (CWE-79) entry both cover arg[1], so a tainted format
|
|
2427
|
+
// string emits two findings. Unlike the logger case above, neither is
|
|
2428
|
+
// redundant — CWE-134 holds for any writer, while CWE-79 is the actionable
|
|
2429
|
+
// finding when the writer is an http.ResponseWriter (`fmt.Fprintf(w, taint)`
|
|
2430
|
+
// is the standard Go reflected-XSS shape). Collapsing it requires deciding
|
|
2431
|
+
// by the *type of arg[0]*, which sink patterns cannot express today: the
|
|
2432
|
+
// options are a Go-specific gate (mirroring isSafeGoJsonUnmarshalCall) that
|
|
2433
|
+
// emits xss only for a resolved ResponseWriter, or dropping one CWE and
|
|
2434
|
+
// accepting either lost XSS recall or a mis-classified stderr write.
|
|
2435
|
+
// Left as-is pending the Go corpus run — see tasks.md.
|
|
2413
2436
|
{ method: 'Fprintf', class: 'fmt', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [1], languages: ['go'] },
|
|
2414
2437
|
// cognium-dev #264 — Go stdlib `log` package format-string entry points.
|
|
2415
2438
|
// log.Printf / Fatalf / Panicf take the format string at arg[0]; tainted
|
|
2416
2439
|
// format string reaches the same fmt.Sprintf machinery internally.
|
|
2440
|
+
//
|
|
2441
|
+
// These deliberately coexist with the Go plugin's log_injection (CWE-117)
|
|
2442
|
+
// entries for the same methods, so such a call emits both. The Logger
|
|
2443
|
+
// decision note in the Java log_injection section above does NOT apply
|
|
2444
|
+
// here: it reasons about SLF4J-style APIs where the format string is a
|
|
2445
|
+
// literal with `{}` placeholders, whereas Go's `log.Printf(fmt, ...)` takes
|
|
2446
|
+
// a real format string that an attacker can control. CWE-117 covers the
|
|
2447
|
+
// forged log line, CWE-134 the format string itself. Locked by
|
|
2448
|
+
// tests/analysis/passes/format-string-additions.test.ts.
|
|
2417
2449
|
{ method: 'Printf', class: 'log', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [0], languages: ['go'] },
|
|
2418
2450
|
{ method: 'Fatalf', class: 'log', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [0], languages: ['go'] },
|
|
2419
2451
|
{ method: 'Panicf', class: 'log', type: 'format_string', cwe: 'CWE-134', severity: 'medium', arg_positions: [0], languages: ['go'] },
|
|
@@ -2427,6 +2459,14 @@ export const DEFAULT_SINKS = [
|
|
|
2427
2459
|
{ method: 'cookie', type: 'crlf', cwe: 'CWE-113', severity: 'medium', arg_positions: [1], languages: ['javascript', 'typescript'] },
|
|
2428
2460
|
// Express: res.location(url) and res.redirect(url) — Location header.
|
|
2429
2461
|
{ method: 'location', type: 'crlf', cwe: 'CWE-113', severity: 'medium', arg_positions: [0], languages: ['javascript', 'typescript'] },
|
|
2462
|
+
// `redirect` is deliberately dual-classified: this crlf (CWE-113) entry
|
|
2463
|
+
// coexists with the open_redirect (CWE-601) entry for js/ts, so a tainted
|
|
2464
|
+
// redirect target emits two flows. That is intentional, not duplication —
|
|
2465
|
+
// #189 Sprint 82 shipped a sink-type-aware flow dedup specifically so both
|
|
2466
|
+
// survive ("res.redirect is both open_redirect AND crlf"), and #132 added a
|
|
2467
|
+
// crlf recall test for the bare `res.redirect(req.query.url)` shape. Locked
|
|
2468
|
+
// by issue-189-sprint82-open-redirect-cluster.test.ts and
|
|
2469
|
+
// crlf-stage8-fp.test.ts — do not collapse without revisiting both tickets.
|
|
2430
2470
|
{ method: 'redirect', type: 'crlf', cwe: 'CWE-113', severity: 'medium', arg_positions: [0], languages: ['javascript', 'typescript'] },
|
|
2431
2471
|
// Go net/http: w.Header().Set(k, v) / Add(k, v) — first arg is the value
|
|
2432
2472
|
// (Header is a map; the actual `value` is arg 1 of the call). We flag the
|