circle-ir 3.74.0 → 3.77.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.
@@ -12413,6 +12413,18 @@ var DEFAULT_SINKS = [
12413
12413
  // value position so a tainted variable is detected.
12414
12414
  { method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
12415
12415
  { method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
12416
+ // Python: Flask/Werkzeug/FastAPI/Django response header sinks (CWE-113).
12417
+ // Subscript assignment (`resp.headers['X-A'] = name`) is NOT covered because
12418
+ // the IR does not emit subscript writes as calls — a known limitation, see
12419
+ // cognium-dev #111. The method-call forms below ARE captured (receiver
12420
+ // suffix-match on `.headers` via receiverMightBeClass).
12421
+ { method: "set", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
12422
+ { method: "add", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
12423
+ { method: "setdefault", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
12424
+ { method: "extend", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["python"] },
12425
+ { method: "__setitem__", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
12426
+ // Flask/Werkzeug response.set_cookie(name, value, ...) — value is CRLF-sensitive.
12427
+ { method: "set_cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
12416
12428
  // Mass-assignment (CWE-915 / CWE-1321) — Sprint 6, #86; cognium-dev #68 Sprint 10.
12417
12429
  // JS Object.assign(target, ...sources), `_.merge`, `_.extend`, `$.extend`,
12418
12430
  // `Object.defineProperty` — when fed an attacker-controlled bag, they write
@@ -13539,6 +13551,10 @@ function receiverMightBeClass(receiver, className) {
13539
13551
  }
13540
13552
  }
13541
13553
  }
13554
+ const chainedCallSuffix = `.${className}()`;
13555
+ if (receiver.endsWith(chainedCallSuffix) || receiver.toLowerCase().endsWith(chainedCallSuffix.toLowerCase())) {
13556
+ return true;
13557
+ }
13542
13558
  if (receiver.includes("::")) {
13543
13559
  const scopePrefix = receiver.match(/^(\w+)::/);
13544
13560
  if (scopePrefix) {
@@ -24641,6 +24657,43 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
24641
24657
  }
24642
24658
  }
24643
24659
  }
24660
+ const aliasChains = [];
24661
+ {
24662
+ const codeLines2 = code.split("\n");
24663
+ for (let i2 = 0; i2 < codeLines2.length; i2++) {
24664
+ const ln = codeLines2[i2];
24665
+ if (ln.trimStart().startsWith("#")) continue;
24666
+ const m = ln.match(/^\s*([\p{L}\p{N}_]+)\s*=\s*([\p{L}\p{N}_]+)\s*$/u);
24667
+ if (!m) continue;
24668
+ const lineNum = i2 + 1;
24669
+ const lhs = m[1];
24670
+ if (derived.get(lhs) !== lineNum) continue;
24671
+ aliasChains.push({ lhs, upstream: m[2], line: lineNum });
24672
+ }
24673
+ }
24674
+ if (aliasChains.length > 0) {
24675
+ let changed = true;
24676
+ let guard = 0;
24677
+ while (changed && guard < aliasChains.length + 2) {
24678
+ changed = false;
24679
+ guard++;
24680
+ for (const { lhs, upstream } of aliasChains) {
24681
+ const upCov = aliasSanitizedFor.get(upstream);
24682
+ if (!upCov || upCov.size === 0) continue;
24683
+ let downCov = aliasSanitizedFor.get(lhs);
24684
+ if (!downCov) {
24685
+ downCov = /* @__PURE__ */ new Set();
24686
+ aliasSanitizedFor.set(lhs, downCov);
24687
+ }
24688
+ for (const t of upCov) {
24689
+ if (!downCov.has(t)) {
24690
+ downCov.add(t);
24691
+ changed = true;
24692
+ }
24693
+ }
24694
+ }
24695
+ }
24696
+ }
24644
24697
  }
24645
24698
  }
24646
24699
  if (language === "rust" && typeof code === "string" && sourcesWithVar.length > 0) {
@@ -29758,6 +29811,10 @@ var WeakRandomPass = class {
29758
29811
  return `${rt}.${method}`;
29759
29812
  }
29760
29813
  }
29814
+ if (JAVA_RANDOM_METHODS.has(method)) {
29815
+ if (/^new\s+Random\s*\(/.test(receiver)) return `new Random.${method}`;
29816
+ if (/^new\s+SplittableRandom\s*\(/.test(receiver)) return `new SplittableRandom.${method}`;
29817
+ }
29761
29818
  if (JAVA_RANDOM_METHODS.has(method) && /ThreadLocalRandom\.current\(\)/.test(receiver)) {
29762
29819
  return `ThreadLocalRandom.current.${method}`;
29763
29820
  }
@@ -30576,7 +30633,7 @@ var JwtVerifyDisabledPass = class {
30576
30633
  out2.push({ pattern: "Algorithm.none()", api: "JWT.require" });
30577
30634
  }
30578
30635
  }
30579
- if (method === "parse" && receiver.includes("parser")) {
30636
+ if (method === "parse" && /\bJwts\s*\.\s*parser\s*\(/.test(receiver)) {
30580
30637
  out2.push({ pattern: "parse() instead of parseClaimsJws()", api: "Jwts.parser().parse" });
30581
30638
  }
30582
30639
  return out2;
@@ -11795,6 +11795,18 @@ var DEFAULT_SINKS = [
11795
11795
  // value position so a tainted variable is detected.
11796
11796
  { method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
11797
11797
  { method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
11798
+ // Python: Flask/Werkzeug/FastAPI/Django response header sinks (CWE-113).
11799
+ // Subscript assignment (`resp.headers['X-A'] = name`) is NOT covered because
11800
+ // the IR does not emit subscript writes as calls — a known limitation, see
11801
+ // cognium-dev #111. The method-call forms below ARE captured (receiver
11802
+ // suffix-match on `.headers` via receiverMightBeClass).
11803
+ { method: "set", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11804
+ { method: "add", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11805
+ { method: "setdefault", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11806
+ { method: "extend", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["python"] },
11807
+ { method: "__setitem__", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11808
+ // Flask/Werkzeug response.set_cookie(name, value, ...) — value is CRLF-sensitive.
11809
+ { method: "set_cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11798
11810
  // Mass-assignment (CWE-915 / CWE-1321) — Sprint 6, #86; cognium-dev #68 Sprint 10.
11799
11811
  // JS Object.assign(target, ...sources), `_.merge`, `_.extend`, `$.extend`,
11800
11812
  // `Object.defineProperty` — when fed an attacker-controlled bag, they write
@@ -12834,6 +12846,10 @@ function receiverMightBeClass(receiver, className) {
12834
12846
  }
12835
12847
  }
12836
12848
  }
12849
+ const chainedCallSuffix = `.${className}()`;
12850
+ if (receiver.endsWith(chainedCallSuffix) || receiver.toLowerCase().endsWith(chainedCallSuffix.toLowerCase())) {
12851
+ return true;
12852
+ }
12837
12853
  if (receiver.includes("::")) {
12838
12854
  const scopePrefix = receiver.match(/^(\w+)::/);
12839
12855
  if (scopePrefix) {
@@ -11729,6 +11729,18 @@ var DEFAULT_SINKS = [
11729
11729
  // value position so a tainted variable is detected.
11730
11730
  { method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
11731
11731
  { method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
11732
+ // Python: Flask/Werkzeug/FastAPI/Django response header sinks (CWE-113).
11733
+ // Subscript assignment (`resp.headers['X-A'] = name`) is NOT covered because
11734
+ // the IR does not emit subscript writes as calls — a known limitation, see
11735
+ // cognium-dev #111. The method-call forms below ARE captured (receiver
11736
+ // suffix-match on `.headers` via receiverMightBeClass).
11737
+ { method: "set", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11738
+ { method: "add", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11739
+ { method: "setdefault", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11740
+ { method: "extend", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["python"] },
11741
+ { method: "__setitem__", class: "headers", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11742
+ // Flask/Werkzeug response.set_cookie(name, value, ...) — value is CRLF-sensitive.
11743
+ { method: "set_cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["python"] },
11732
11744
  // Mass-assignment (CWE-915 / CWE-1321) — Sprint 6, #86; cognium-dev #68 Sprint 10.
11733
11745
  // JS Object.assign(target, ...sources), `_.merge`, `_.extend`, `$.extend`,
11734
11746
  // `Object.defineProperty` — when fed an attacker-controlled bag, they write
@@ -12768,6 +12780,10 @@ function receiverMightBeClass(receiver, className) {
12768
12780
  }
12769
12781
  }
12770
12782
  }
12783
+ const chainedCallSuffix = `.${className}()`;
12784
+ if (receiver.endsWith(chainedCallSuffix) || receiver.toLowerCase().endsWith(chainedCallSuffix.toLowerCase())) {
12785
+ return true;
12786
+ }
12771
12787
  if (receiver.includes("::")) {
12772
12788
  const scopePrefix = receiver.match(/^(\w+)::/);
12773
12789
  if (scopePrefix) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.74.0",
3
+ "version": "3.77.0",
4
4
  "description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",