circle-ir 3.178.0 → 3.179.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.
@@ -12874,6 +12874,16 @@ var DEFAULT_SINKS = [
12874
12874
  { method: "format", class: "String", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12875
12875
  { method: "format", class: "Formatter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12876
12876
  { method: "printf", class: "System.out", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12877
+ // cognium-dev #264 — extend the Java format-string surface.
12878
+ // MessageFormat.format(pattern, args) — same receiver-family as String.format
12879
+ // but on the java.text side. PrintStream/PrintWriter.{printf,format} —
12880
+ // arbitrary streams (not just System.out) also honour format-string
12881
+ // specifiers, so a tainted first arg is a genuine CWE-134 sink.
12882
+ { method: "format", class: "MessageFormat", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12883
+ { method: "printf", class: "PrintStream", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12884
+ { method: "format", class: "PrintStream", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12885
+ { method: "printf", class: "PrintWriter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12886
+ { method: "format", class: "PrintWriter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12877
12887
  // NOTE: Python `userFmt.format(...)` and `userFmt % args` require
12878
12888
  // receiver-taint or operator-LHS-taint tracking — the format string is the
12879
12889
  // receiver, not an argument. Deferred to Sprint 6 (#86 follow-up).
@@ -12885,6 +12895,12 @@ var DEFAULT_SINKS = [
12885
12895
  { method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12886
12896
  { method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12887
12897
  { method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
12898
+ // cognium-dev #264 — Go stdlib `log` package format-string entry points.
12899
+ // log.Printf / Fatalf / Panicf take the format string at arg[0]; tainted
12900
+ // format string reaches the same fmt.Sprintf machinery internally.
12901
+ { method: "Printf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12902
+ { method: "Fatalf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12903
+ { method: "Panicf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12888
12904
  // CRLF / HTTP response splitting (CWE-113) — Sprint 6, #86.
12889
12905
  // Node.js / Express response header / cookie sinks. The header *name* (arg 0)
12890
12906
  // is also CRLF-sensitive but is almost always a string literal; we model
@@ -15072,6 +15088,11 @@ function calculateSinkConfidence(call, pattern) {
15072
15088
  if (pattern.class && call.receiver) {
15073
15089
  if (receiverMightBeClass(call.receiver, pattern.class)) {
15074
15090
  confidence += 0.1;
15091
+ const receiverLc = call.receiver.toLowerCase();
15092
+ const classLc = pattern.class.toLowerCase();
15093
+ if (call.receiver === pattern.class || receiverLc === classLc) {
15094
+ confidence += 0.05;
15095
+ }
15075
15096
  }
15076
15097
  }
15077
15098
  if (pattern.severity === "critical") {
@@ -32863,6 +32884,85 @@ function resolveFastjsonFromPom(pomXml) {
32863
32884
  }
32864
32885
  return null;
32865
32886
  }
32887
+ function resolveFastjsonFromGradle(buildGradle) {
32888
+ if (!buildGradle) return null;
32889
+ const directRe = /['"(]\s*com\.alibaba:fastjson:([^'"$\s)]+)\s*['")]/;
32890
+ const direct = buildGradle.match(directRe);
32891
+ if (direct) {
32892
+ const version2 = direct[1];
32893
+ return { version: version2, noneAutotype: /_noneautotype/i.test(version2) };
32894
+ }
32895
+ const propRefRe = /['"(]\s*com\.alibaba:fastjson:\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?\s*['")]/;
32896
+ const propRef = buildGradle.match(propRefRe);
32897
+ if (!propRef) return null;
32898
+ const propName = propRef[1];
32899
+ const defRe = new RegExp(
32900
+ `\\b${propName}\\s*[=:]\\s*['"]([^'"\\s]+)['"]`
32901
+ );
32902
+ const def = buildGradle.match(defRe);
32903
+ if (!def) return null;
32904
+ const version = def[1];
32905
+ return { version, noneAutotype: /_noneautotype/i.test(version) };
32906
+ }
32907
+ function resolvePyYamlFromRequirements(requirementsTxt) {
32908
+ if (!requirementsTxt) return null;
32909
+ for (const rawLine of requirementsTxt.split(/\r?\n/)) {
32910
+ const line = rawLine.replace(/#.*$/, "").trim();
32911
+ if (!line) continue;
32912
+ if (line.startsWith("-")) continue;
32913
+ const m = line.match(
32914
+ /^(pyyaml)\s*(==|>=|~=|>|===)\s*(\d+(?:\.\d+)*)/i
32915
+ );
32916
+ if (!m) continue;
32917
+ const version = m[3];
32918
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
32919
+ }
32920
+ return null;
32921
+ }
32922
+ function resolvePyYamlFromPyproject(pyprojectToml) {
32923
+ if (!pyprojectToml) return null;
32924
+ const poetryLine = pyprojectToml.match(
32925
+ /^\s*(pyyaml)\s*=\s*"([\^~=<>]*)(\d+(?:\.\d+)*)"/im
32926
+ );
32927
+ if (poetryLine) {
32928
+ const version = poetryLine[3];
32929
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
32930
+ }
32931
+ const poetryTable = pyprojectToml.match(
32932
+ /^\s*(pyyaml)\s*=\s*\{[^}]*\bversion\s*=\s*"([\^~=<>]*)(\d+(?:\.\d+)*)"/im
32933
+ );
32934
+ if (poetryTable) {
32935
+ const version = poetryTable[3];
32936
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
32937
+ }
32938
+ const pep621 = pyprojectToml.match(
32939
+ /"(pyyaml)\s*(==|>=|~=|>|===)\s*(\d+(?:\.\d+)*)/i
32940
+ );
32941
+ if (pep621) {
32942
+ const version = pep621[3];
32943
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
32944
+ }
32945
+ return null;
32946
+ }
32947
+ function isPyYamlVersionSafeByDefault(version) {
32948
+ const parts2 = version.split(".").map((s) => Number.parseInt(s, 10));
32949
+ if (parts2.length === 0 || !Number.isFinite(parts2[0])) return false;
32950
+ return parts2[0] >= 6;
32951
+ }
32952
+ function fileHasUnsafePyYamlLoader(sourceLines, sinkLine) {
32953
+ const start2 = Math.max(0, sinkLine - 1);
32954
+ const end = Math.min(sourceLines.length, start2 + 10);
32955
+ const unsafeRe = /\bLoader\s*=\s*(?:yaml\s*\.\s*)?(?:Loader|UnsafeLoader|FullLoader)\b/;
32956
+ for (let i2 = start2; i2 < end; i2++) {
32957
+ const ln = sourceLines[i2] ?? "";
32958
+ if (unsafeRe.test(ln)) return true;
32959
+ const stripped = ln.trim();
32960
+ if (i2 > start2 && (stripped === ")" || stripped.endsWith(")"))) {
32961
+ return false;
32962
+ }
32963
+ }
32964
+ return false;
32965
+ }
32866
32966
  function fileReenablesFastjsonAutotype(source) {
32867
32967
  if (!source) return false;
32868
32968
  return /\bsetAutoTypeSupport\s*\(\s*true\b/.test(source);
@@ -32888,6 +32988,8 @@ var JACKSON_METHODS = /* @__PURE__ */ new Set(["readValue", "convertValue", "tre
32888
32988
  var JACKSON_CLASSES = /* @__PURE__ */ new Set(["ObjectMapper", "ObjectReader"]);
32889
32989
  var SNAKEYAML_METHODS = /* @__PURE__ */ new Set(["load", "loadAs", "loadAll"]);
32890
32990
  var SNAKEYAML_CLASSES = /* @__PURE__ */ new Set(["Yaml"]);
32991
+ var PYYAML_METHODS = /* @__PURE__ */ new Set(["load"]);
32992
+ var PYYAML_CLASSES = /* @__PURE__ */ new Set(["yaml"]);
32891
32993
  var DeserializationSafetyGatePass = class {
32892
32994
  constructor(dependencyContext) {
32893
32995
  this.dependencyContext = dependencyContext;
@@ -32897,18 +32999,29 @@ var DeserializationSafetyGatePass = class {
32897
32999
  category = "security";
32898
33000
  run(ctx) {
32899
33001
  const { graph, language, code } = ctx;
32900
- if (language !== "java") {
32901
- return { droppedFastjson: 0, droppedJackson: 0, droppedSnakeYaml: 0 };
33002
+ if (language !== "java" && language !== "python") {
33003
+ return { droppedFastjson: 0, droppedJackson: 0, droppedSnakeYaml: 0, droppedPyYaml: 0 };
32902
33004
  }
32903
33005
  const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
32904
33006
  const pomXml = this.dependencyContext?.java?.pomXml;
32905
- const fastjson = pomXml ? resolveFastjsonFromPom(pomXml) : null;
33007
+ const buildGradle = this.dependencyContext?.java?.buildGradle;
33008
+ const fastjson = (pomXml ? resolveFastjsonFromPom(pomXml) : null) ?? (buildGradle ? resolveFastjsonFromGradle(buildGradle) : null);
32906
33009
  const fastjsonHardened = fastjson?.noneAutotype === true && !fileReenablesFastjsonAutotype(code);
32907
33010
  const jacksonSafe = !fileEnablesJacksonPolymorphism(code);
32908
33011
  const snakeYamlSafe = fileConfiguresSnakeYamlSafely(code);
33012
+ const requirementsTxt = this.dependencyContext?.python?.requirementsTxt;
33013
+ const pyprojectToml = this.dependencyContext?.python?.pyprojectToml;
33014
+ const pyYaml = (requirementsTxt ? resolvePyYamlFromRequirements(requirementsTxt) : null) ?? (pyprojectToml ? resolvePyYamlFromPyproject(pyprojectToml) : null);
33015
+ const pyYamlSafeByDefault = pyYaml?.safeByDefault === true;
33016
+ let sourceLines = null;
33017
+ const getSourceLines = () => {
33018
+ if (sourceLines === null) sourceLines = code.split("\n");
33019
+ return sourceLines;
33020
+ };
32909
33021
  let droppedFastjson = 0;
32910
33022
  let droppedJackson = 0;
32911
33023
  let droppedSnakeYaml = 0;
33024
+ let droppedPyYaml = 0;
32912
33025
  const kept = sinks.filter((sink) => {
32913
33026
  if (sink.type !== "deserialization") return true;
32914
33027
  if (!sink.method) return true;
@@ -32924,14 +33037,18 @@ var DeserializationSafetyGatePass = class {
32924
33037
  droppedSnakeYaml++;
32925
33038
  return false;
32926
33039
  }
33040
+ if (language === "python" && pyYamlSafeByDefault && PYYAML_METHODS.has(sink.method) && (sink.class === void 0 || PYYAML_CLASSES.has(sink.class)) && !fileHasUnsafePyYamlLoader(getSourceLines(), sink.line)) {
33041
+ droppedPyYaml++;
33042
+ return false;
33043
+ }
32927
33044
  return true;
32928
33045
  });
32929
- const totalDropped = droppedFastjson + droppedJackson + droppedSnakeYaml;
33046
+ const totalDropped = droppedFastjson + droppedJackson + droppedSnakeYaml + droppedPyYaml;
32930
33047
  if (totalDropped > 0) {
32931
33048
  sinks.length = 0;
32932
33049
  sinks.push(...kept);
32933
33050
  }
32934
- return { droppedFastjson, droppedJackson, droppedSnakeYaml };
33051
+ return { droppedFastjson, droppedJackson, droppedSnakeYaml, droppedPyYaml };
32935
33052
  }
32936
33053
  };
32937
33054
 
@@ -12269,6 +12269,16 @@ var DEFAULT_SINKS = [
12269
12269
  { method: "format", class: "String", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12270
12270
  { method: "format", class: "Formatter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12271
12271
  { method: "printf", class: "System.out", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12272
+ // cognium-dev #264 — extend the Java format-string surface.
12273
+ // MessageFormat.format(pattern, args) — same receiver-family as String.format
12274
+ // but on the java.text side. PrintStream/PrintWriter.{printf,format} —
12275
+ // arbitrary streams (not just System.out) also honour format-string
12276
+ // specifiers, so a tainted first arg is a genuine CWE-134 sink.
12277
+ { method: "format", class: "MessageFormat", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12278
+ { method: "printf", class: "PrintStream", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12279
+ { method: "format", class: "PrintStream", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12280
+ { method: "printf", class: "PrintWriter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12281
+ { method: "format", class: "PrintWriter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12272
12282
  // NOTE: Python `userFmt.format(...)` and `userFmt % args` require
12273
12283
  // receiver-taint or operator-LHS-taint tracking — the format string is the
12274
12284
  // receiver, not an argument. Deferred to Sprint 6 (#86 follow-up).
@@ -12280,6 +12290,12 @@ var DEFAULT_SINKS = [
12280
12290
  { method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12281
12291
  { method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12282
12292
  { method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
12293
+ // cognium-dev #264 — Go stdlib `log` package format-string entry points.
12294
+ // log.Printf / Fatalf / Panicf take the format string at arg[0]; tainted
12295
+ // format string reaches the same fmt.Sprintf machinery internally.
12296
+ { method: "Printf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12297
+ { method: "Fatalf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12298
+ { method: "Panicf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12283
12299
  // CRLF / HTTP response splitting (CWE-113) — Sprint 6, #86.
12284
12300
  // Node.js / Express response header / cookie sinks. The header *name* (arg 0)
12285
12301
  // is also CRLF-sensitive but is almost always a string literal; we model
@@ -14372,6 +14388,11 @@ function calculateSinkConfidence(call, pattern) {
14372
14388
  if (pattern.class && call.receiver) {
14373
14389
  if (receiverMightBeClass(call.receiver, pattern.class)) {
14374
14390
  confidence += 0.1;
14391
+ const receiverLc = call.receiver.toLowerCase();
14392
+ const classLc = pattern.class.toLowerCase();
14393
+ if (call.receiver === pattern.class || receiverLc === classLc) {
14394
+ confidence += 0.05;
14395
+ }
14375
14396
  }
14376
14397
  }
14377
14398
  if (pattern.severity === "critical") {
@@ -12203,6 +12203,16 @@ var DEFAULT_SINKS = [
12203
12203
  { method: "format", class: "String", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12204
12204
  { method: "format", class: "Formatter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12205
12205
  { method: "printf", class: "System.out", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12206
+ // cognium-dev #264 — extend the Java format-string surface.
12207
+ // MessageFormat.format(pattern, args) — same receiver-family as String.format
12208
+ // but on the java.text side. PrintStream/PrintWriter.{printf,format} —
12209
+ // arbitrary streams (not just System.out) also honour format-string
12210
+ // specifiers, so a tainted first arg is a genuine CWE-134 sink.
12211
+ { method: "format", class: "MessageFormat", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12212
+ { method: "printf", class: "PrintStream", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12213
+ { method: "format", class: "PrintStream", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12214
+ { method: "printf", class: "PrintWriter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12215
+ { method: "format", class: "PrintWriter", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["java"] },
12206
12216
  // NOTE: Python `userFmt.format(...)` and `userFmt % args` require
12207
12217
  // receiver-taint or operator-LHS-taint tracking — the format string is the
12208
12218
  // receiver, not an argument. Deferred to Sprint 6 (#86 follow-up).
@@ -12214,6 +12224,12 @@ var DEFAULT_SINKS = [
12214
12224
  { method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12215
12225
  { method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12216
12226
  { method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
12227
+ // cognium-dev #264 — Go stdlib `log` package format-string entry points.
12228
+ // log.Printf / Fatalf / Panicf take the format string at arg[0]; tainted
12229
+ // format string reaches the same fmt.Sprintf machinery internally.
12230
+ { method: "Printf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12231
+ { method: "Fatalf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12232
+ { method: "Panicf", class: "log", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
12217
12233
  // CRLF / HTTP response splitting (CWE-113) — Sprint 6, #86.
12218
12234
  // Node.js / Express response header / cookie sinks. The header *name* (arg 0)
12219
12235
  // is also CRLF-sensitive but is almost always a string literal; we model
@@ -14306,6 +14322,11 @@ function calculateSinkConfidence(call, pattern) {
14306
14322
  if (pattern.class && call.receiver) {
14307
14323
  if (receiverMightBeClass(call.receiver, pattern.class)) {
14308
14324
  confidence += 0.1;
14325
+ const receiverLc = call.receiver.toLowerCase();
14326
+ const classLc = pattern.class.toLowerCase();
14327
+ if (call.receiver === pattern.class || receiverLc === classLc) {
14328
+ confidence += 0.05;
14329
+ }
14309
14330
  }
14310
14331
  }
14311
14332
  if (pattern.severity === "critical") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.178.0",
3
+ "version": "3.179.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",