cognium-dev 4.9.21 → 4.9.23

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.
Files changed (2) hide show
  1. package/dist/cli.js +373 -20
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -14400,6 +14400,35 @@ function classifyGoDestinationType(decl) {
14400
14400
  return "unsafe";
14401
14401
  return "safe";
14402
14402
  }
14403
+ function isGoFprintToNonResponseWriter(call, pattern, language, sourceLines) {
14404
+ if (language !== "go")
14405
+ return false;
14406
+ if (pattern.type !== "xss" || pattern.class !== "fmt")
14407
+ return false;
14408
+ if (!/^Fprint(?:f|ln)?$/.test(call.method_name))
14409
+ return false;
14410
+ if (!sourceLines)
14411
+ return true;
14412
+ const writer = call.arguments.find((a) => a.position === 0);
14413
+ const name2 = (writer?.expression ?? "").trim();
14414
+ if (!/^[A-Za-z_]\w*$/.test(name2))
14415
+ return true;
14416
+ const rwRe = new RegExp(`\\b${name2}\\s+(?:net/)?http\\.ResponseWriter\\b`);
14417
+ for (let i2 = call.location.line - 1;i2 >= 0; i2--) {
14418
+ const line = sourceLines[i2] ?? "";
14419
+ const funcIdx = line.search(/\bfunc\b/);
14420
+ if (funcIdx < 0)
14421
+ continue;
14422
+ const header = sourceLines.slice(i2, i2 + 4).join(" ");
14423
+ const brace = header.indexOf("{");
14424
+ const params = header.slice(funcIdx, brace >= 0 ? brace : header.length);
14425
+ if (rwRe.test(params))
14426
+ return false;
14427
+ if (/^\s*func\b/.test(line))
14428
+ return true;
14429
+ }
14430
+ return true;
14431
+ }
14403
14432
  function isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines) {
14404
14433
  if (language !== "go")
14405
14434
  return false;
@@ -14545,6 +14574,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14545
14574
  if (isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines)) {
14546
14575
  continue;
14547
14576
  }
14577
+ if (isGoFprintToNonResponseWriter(call, pattern, language, sourceLines)) {
14578
+ continue;
14579
+ }
14548
14580
  if (isSafeJinjaRenderCall(call, pattern, language, sourceLines)) {
14549
14581
  continue;
14550
14582
  }
@@ -24126,7 +24158,23 @@ class GoPlugin extends BaseLanguagePlugin {
24126
24158
  type: "xss",
24127
24159
  cwe: "CWE-79",
24128
24160
  severity: "high",
24129
- argPositions: [1]
24161
+ argPositions: [1, 2, 3, 4, 5, 6]
24162
+ },
24163
+ {
24164
+ method: "Fprint",
24165
+ class: "fmt",
24166
+ type: "xss",
24167
+ cwe: "CWE-79",
24168
+ severity: "high",
24169
+ argPositions: [1, 2, 3, 4, 5, 6]
24170
+ },
24171
+ {
24172
+ method: "Fprintln",
24173
+ class: "fmt",
24174
+ type: "xss",
24175
+ cwe: "CWE-79",
24176
+ severity: "high",
24177
+ argPositions: [1, 2, 3, 4, 5, 6]
24130
24178
  },
24131
24179
  {
24132
24180
  method: "Write",
@@ -24604,16 +24652,22 @@ class CSharpPlugin extends BaseLanguagePlugin {
24604
24652
  }
24605
24653
 
24606
24654
  // ../circle-ir/dist/languages/plugins/index.js
24655
+ function createBuiltinPlugins() {
24656
+ return [
24657
+ new JavaPlugin,
24658
+ new JavaScriptPlugin,
24659
+ new PythonPlugin,
24660
+ new RustPlugin,
24661
+ new BashPlugin,
24662
+ new HtmlPlugin,
24663
+ new VuePlugin,
24664
+ new GoPlugin,
24665
+ new CSharpPlugin
24666
+ ];
24667
+ }
24607
24668
  function registerBuiltinPlugins() {
24608
- registerLanguage(new JavaPlugin);
24609
- registerLanguage(new JavaScriptPlugin);
24610
- registerLanguage(new PythonPlugin);
24611
- registerLanguage(new RustPlugin);
24612
- registerLanguage(new BashPlugin);
24613
- registerLanguage(new HtmlPlugin);
24614
- registerLanguage(new VuePlugin);
24615
- registerLanguage(new GoPlugin);
24616
- registerLanguage(new CSharpPlugin);
24669
+ for (const plugin of createBuiltinPlugins())
24670
+ registerLanguage(plugin);
24617
24671
  }
24618
24672
  // ../circle-ir/dist/analysis/passes/cross-file-pass.js
24619
24673
  class CrossFilePass {
@@ -25804,6 +25858,8 @@ class LanguageSourcesPass {
25804
25858
  additionalSources.push(...findGoArgvSources(code, language));
25805
25859
  additionalSources.push(...findGoArchiveEntrySources(code, language));
25806
25860
  additionalSources.push(...findGoMultipartUploadSources(code, language));
25861
+ additionalSources.push(...findGoGrpcRequestSources(code, language));
25862
+ additionalSources.push(...findGoRequestBodySources(code, language));
25807
25863
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
25808
25864
  for (const s of jsDOMSinks) {
25809
25865
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -26549,6 +26605,127 @@ function findGoMultipartUploadSources(sourceCode, language) {
26549
26605
  }
26550
26606
  return sources;
26551
26607
  }
26608
+ function findGoGrpcRequestSources(sourceCode, language) {
26609
+ if (language !== "go")
26610
+ return [];
26611
+ if (!sourceCode.includes("context.Context"))
26612
+ return [];
26613
+ const sources = [];
26614
+ const lines = sourceCode.split(`
26615
+ `);
26616
+ const sigRe = /^func\s*\([^)]*\)\s*[A-Z]\w*\s*\(\s*\w+\s+context\.Context\s*,\s*([A-Za-z_]\w*)\s+\*(?:\w+\.)?\w*(?:Request|Req)\s*\)\s*\(\s*\*[\w.]+\s*,\s*error\s*\)\s*\{/;
26617
+ const controlRe = /^\s*(?:\}\s*else\s+)?(?:if|for|switch|case|return)\b/;
26618
+ for (let i2 = 0;i2 < lines.length; i2++) {
26619
+ const sig = sigRe.exec(lines[i2]);
26620
+ if (!sig)
26621
+ continue;
26622
+ const param = sig[1];
26623
+ if (param === "_")
26624
+ continue;
26625
+ const readRe = new RegExp(`(?<![\\w.])${param}\\s*\\.\\s*(?:Get[A-Z]\\w*\\s*\\(\\s*\\)|[A-Z]\\w*\\b(?!\\s*\\())`);
26626
+ const directReadRe = new RegExp(`^(?:(?:\\[\\]\\w+|string)\\s*\\(\\s*)*${param}(?:\\s*\\.\\s*(?:Get[A-Z]\\w*\\s*\\(\\s*\\)|[A-Z]\\w*\\b(?!\\s*\\()))+(?:\\s*\\))*$`);
26627
+ const numericParseRe = new RegExp(`\\bstrconv\\s*\\.\\s*(?:Atoi|ParseInt|ParseUint|ParseFloat|ParseBool)\\s*\\(\\s*${param}\\s*\\.`);
26628
+ let end = i2 + 1;
26629
+ while (end < lines.length && !/^\}/.test(lines[end]))
26630
+ end++;
26631
+ for (let j = i2 + 1;j < end; j++) {
26632
+ const line = lines[j];
26633
+ const read = readRe.exec(line);
26634
+ if (!read)
26635
+ continue;
26636
+ if (numericParseRe.test(line))
26637
+ continue;
26638
+ let variable;
26639
+ const unmarshal = /\bUnmarshal\w*\s*\([^&]*&\s*([A-Za-z_]\w*)/.exec(line);
26640
+ const assign = /^\s*(?:var\s+)?([A-Za-z_][\w\s,]*?)\s*:?=(?!=)\s*(.+)$/.exec(line);
26641
+ if (unmarshal && line.indexOf(read[0]) < line.indexOf("&")) {
26642
+ variable = unmarshal[1];
26643
+ } else if (assign && directReadRe.test(assign[2].trim()) && !controlRe.test(line)) {
26644
+ variable = assign[1].split(",").map((v) => v.trim()).find((v) => v !== "_" && v !== "err" && v !== "ok" && /^[A-Za-z_]\w*$/.test(v));
26645
+ if (!variable)
26646
+ continue;
26647
+ } else if (controlRe.test(line)) {
26648
+ continue;
26649
+ } else {
26650
+ variable = param;
26651
+ }
26652
+ const lineNumber = j + 1;
26653
+ if (sources.some((sc) => sc.line === lineNumber && sc.variable === variable))
26654
+ continue;
26655
+ sources.push({
26656
+ type: "http_body",
26657
+ location: `${read[0].replace(/\s+/g, "")} — field of the gRPC request message \`${param}\``,
26658
+ severity: "high",
26659
+ line: lineNumber,
26660
+ confidence: 0.85,
26661
+ variable
26662
+ });
26663
+ }
26664
+ i2 = end;
26665
+ }
26666
+ return sources;
26667
+ }
26668
+ function findGoRequestBodySources(sourceCode, language) {
26669
+ if (language !== "go")
26670
+ return [];
26671
+ if (!sourceCode.includes(".Body"))
26672
+ return [];
26673
+ const lines = sourceCode.split(`
26674
+ `);
26675
+ const reqNames = new Set;
26676
+ const paramRe = /([A-Za-z_]\w*)\s+\*http\.Request\b/g;
26677
+ for (const line of lines) {
26678
+ let m;
26679
+ paramRe.lastIndex = 0;
26680
+ while ((m = paramRe.exec(line)) !== null)
26681
+ reqNames.add(m[1]);
26682
+ }
26683
+ if (reqNames.size === 0)
26684
+ return [];
26685
+ const bodyAlt = [...reqNames].map((n) => n.replace(/\$/g, "\\$")).join("|");
26686
+ const bodyRef = `(?<![\\w.])(?:${bodyAlt})\\s*\\.\\s*Body\\b`;
26687
+ const sources = [];
26688
+ const push = (variable, line, how) => {
26689
+ if (variable === "_" || variable === "")
26690
+ return;
26691
+ if (sources.some((s) => s.line === line && s.variable === variable))
26692
+ return;
26693
+ sources.push({
26694
+ type: "http_body",
26695
+ location: `${how} — HTTP request body`,
26696
+ severity: "high",
26697
+ line,
26698
+ confidence: 0.85,
26699
+ ...variable ? { variable } : {}
26700
+ });
26701
+ };
26702
+ const bodyRe = new RegExp(bodyRef);
26703
+ const decodeRe = new RegExp(`(?:json|xml)\\s*\\.\\s*NewDecoder\\s*\\(\\s*(?:io\\.NopCloser\\s*\\(\\s*)?(?:${bodyAlt})\\s*\\.\\s*Body\\b[^)]*\\)\\s*\\)?\\s*\\.\\s*Decode\\s*\\(\\s*&\\s*([A-Za-z_]\\w*)`);
26704
+ const readAllRe = new RegExp(`\\b([A-Za-z_]\\w*)\\s*,\\s*\\w+\\s*:?=\\s*(?:io|ioutil)\\s*\\.\\s*ReadAll\\s*\\(\\s*(?:${bodyAlt})\\s*\\.\\s*Body\\b`);
26705
+ const copyRe = new RegExp(`\\bio\\s*\\.\\s*Copy\\s*\\(\\s*([A-Za-z_]\\w*|&\\s*[A-Za-z_]\\w*)\\s*,\\s*(?:${bodyAlt})\\s*\\.\\s*Body\\b`);
26706
+ for (let i2 = 0;i2 < lines.length; i2++) {
26707
+ const line = lines[i2];
26708
+ if (!bodyRe.test(line))
26709
+ continue;
26710
+ const n = i2 + 1;
26711
+ const dec = decodeRe.exec(line);
26712
+ if (dec) {
26713
+ push(dec[1], n, `json/xml decode of ${dec[0].split(".")[0]}.Body`);
26714
+ continue;
26715
+ }
26716
+ const ra = readAllRe.exec(line);
26717
+ if (ra) {
26718
+ push(ra[1], n, "io.ReadAll(req.Body)");
26719
+ continue;
26720
+ }
26721
+ const cp = copyRe.exec(line);
26722
+ if (cp) {
26723
+ push(cp[1].replace(/^&\s*/, ""), n, "io.Copy(dst, req.Body)");
26724
+ continue;
26725
+ }
26726
+ }
26727
+ return sources;
26728
+ }
26552
26729
  function findCSharpRequestSources(sourceCode, language) {
26553
26730
  if (language !== "csharp")
26554
26731
  return [];
@@ -37784,6 +37961,116 @@ function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
37784
37961
  }
37785
37962
 
37786
37963
  // ../circle-ir/dist/analysis/interprocedural.js
37964
+ var GO_BUILTIN_CALLS = new Set([
37965
+ "len",
37966
+ "cap",
37967
+ "make",
37968
+ "new",
37969
+ "append",
37970
+ "copy",
37971
+ "delete",
37972
+ "close",
37973
+ "panic",
37974
+ "recover",
37975
+ "print",
37976
+ "println",
37977
+ "min",
37978
+ "max",
37979
+ "clear",
37980
+ "string",
37981
+ "int",
37982
+ "int8",
37983
+ "int16",
37984
+ "int32",
37985
+ "int64",
37986
+ "uint",
37987
+ "uint8",
37988
+ "uint16",
37989
+ "uint32",
37990
+ "uint64",
37991
+ "float32",
37992
+ "float64",
37993
+ "bool",
37994
+ "byte",
37995
+ "rune",
37996
+ "error"
37997
+ ]);
37998
+ var GO_ERROR_AND_LOG_CALLS = new Set([
37999
+ "Errorf",
38000
+ "Sprintf",
38001
+ "Sprint",
38002
+ "Sprintln",
38003
+ "Print",
38004
+ "Printf",
38005
+ "Println",
38006
+ "Fatal",
38007
+ "Fatalf",
38008
+ "Fatalln",
38009
+ "Panic",
38010
+ "Panicf",
38011
+ "Panicln",
38012
+ "Debug",
38013
+ "Debugf",
38014
+ "Debugln",
38015
+ "Debugw",
38016
+ "Info",
38017
+ "Infof",
38018
+ "Infoln",
38019
+ "Infow",
38020
+ "InfoS",
38021
+ "Warn",
38022
+ "Warnf",
38023
+ "Warnln",
38024
+ "Warnw",
38025
+ "Warning",
38026
+ "Warningf",
38027
+ "Warningln",
38028
+ "Error",
38029
+ "Errorln",
38030
+ "Errorw",
38031
+ "ErrorS",
38032
+ "Trace",
38033
+ "Tracef",
38034
+ "V",
38035
+ "WithError",
38036
+ "WithField",
38037
+ "WithFields",
38038
+ "WithValues",
38039
+ "Msg",
38040
+ "Msgf"
38041
+ ]);
38042
+ var GO_ERROR_PACKAGES = new Set(["errors", "status", "xerrors", "multierr", "multierror"]);
38043
+ var GO_PURE_PACKAGES = new Set([
38044
+ "strings",
38045
+ "strconv",
38046
+ "bytes",
38047
+ "unicode",
38048
+ "utf8",
38049
+ "path",
38050
+ "filepath",
38051
+ "math",
38052
+ "sort",
38053
+ "slices",
38054
+ "maps",
38055
+ "time",
38056
+ "regexp",
38057
+ "url",
38058
+ "hex",
38059
+ "base64"
38060
+ ]);
38061
+ function isGoNonEscapeCall(call) {
38062
+ const name2 = call.method_name;
38063
+ const recv = (call.receiver ?? "").split(".").pop() ?? "";
38064
+ if (!call.receiver && GO_BUILTIN_CALLS.has(name2))
38065
+ return true;
38066
+ if (GO_ERROR_PACKAGES.has(recv))
38067
+ return true;
38068
+ if (GO_PURE_PACKAGES.has(recv))
38069
+ return true;
38070
+ if (GO_ERROR_AND_LOG_CALLS.has(name2))
38071
+ return true;
38072
+ return false;
38073
+ }
37787
38074
  function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourcesOrSanitizers, sinksOrOptions, sanitizersArg, optionsArg = {}) {
37788
38075
  let graph;
37789
38076
  let sources;
@@ -38015,6 +38302,9 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
38015
38302
  if (!targetMethod) {
38016
38303
  if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name) && !ormQueryBuilderMethods.has(call.method_name)) {
38017
38304
  const isBash = graph.ir.meta.language === "bash";
38305
+ if (graph.ir.meta.language === "go" && isGoNonEscapeCall(call)) {
38306
+ continue;
38307
+ }
38018
38308
  const bashSafeBuiltins = new Set([
38019
38309
  "echo",
38020
38310
  "printf",
@@ -46991,6 +47281,8 @@ var JAVA_XML_OUTPUT_ONLY_RE = /\bnew\s+(?:DOMSource|StreamResult)\s*\(/;
46991
47281
  var JAVA_XML_PARSE_INPUT_RE = /\bnew\s+(?:StreamSource|SAXSource|InputSource)\s*\(/;
46992
47282
  var JAVA_DOC_BUILDER_NEW_DOCUMENT_RE = /\.\s*newDocument\s*\(\s*\)/;
46993
47283
  var JAVA_DOC_BUILDER_PARSE_RE = /(?:DocumentBuilder|builder)\s*\.\s*parse\s*\(/;
47284
+ var JAVA_HARDENING_SETTER_RE = /\.\s*(?:setFeature|setAttribute|setProperty|setXIncludeAware|setExpandEntityReferences)\s*\(/;
47285
+ var JAVA_FACTORY_CHAINED_RE = /\bnewInstance\s*\([^()]*\)\s*\.\s*(?:newDocumentBuilder|newSAXParser|newTransformer|newTemplates|newSchema|createXMLStreamReader|createXMLEventReader)\s*\(/;
46994
47286
  var PY_LXML_PARSER_INSECURE_DEFAULT_RE = /\bresolve_entities\s*=\s*False\b/;
46995
47287
 
46996
47288
  class XmlEntityExpansionPass {
@@ -47003,8 +47295,10 @@ class XmlEntityExpansionPass {
47003
47295
  const code = ctx.code ?? "";
47004
47296
  if (language === "java") {
47005
47297
  const safeInFile = JAVA_SAFE_EVIDENCE_RE.test(code);
47006
- if (safeInFile)
47007
- return { findings };
47298
+ const processWide = safeInFile && /jdk\.xml\./.test(code);
47299
+ const lines = safeInFile ? code.split(`
47300
+ `) : [];
47301
+ const methodRanges = safeInFile ? this.methodRanges(graph.ir.types) : [];
47008
47302
  const isXmlOutputOnly = JAVA_XML_OUTPUT_ONLY_RE.test(code) && !JAVA_XML_PARSE_INPUT_RE.test(code);
47009
47303
  const isDocumentBuilderEmptyOnly = JAVA_DOC_BUILDER_NEW_DOCUMENT_RE.test(code) && !JAVA_DOC_BUILDER_PARSE_RE.test(code);
47010
47304
  for (const call of graph.ir.calls) {
@@ -47016,6 +47310,8 @@ class XmlEntityExpansionPass {
47016
47310
  if (det.api === "DocumentBuilderFactory" && isDocumentBuilderEmptyOnly)
47017
47311
  continue;
47018
47312
  const line = call.location.line;
47313
+ if (safeInFile && (processWide || !this.isProvablyUnhardened(lines, line, methodRanges, det.api)))
47314
+ continue;
47019
47315
  findings.push({ line, language, ...det });
47020
47316
  ctx.addFinding({
47021
47317
  id: `${this.name}-${file}-${line}-${det.api}`,
@@ -47025,11 +47321,11 @@ class XmlEntityExpansionPass {
47025
47321
  cwe: det.cwe,
47026
47322
  severity: "high",
47027
47323
  level: "error",
47028
- message: `${det.api} created without disabling DTD / external-entity ` + "processing. Vulnerable to billion-laughs / quadratic " + "blow-up DoS (CWE-776) and external-entity disclosure " + '(CWE-611). Add `setFeature("http://apache.org/xml/features/' + 'disallow-doctype-decl", true)` (or the equivalent) before ' + "parsing.",
47324
+ message: `${det.api} created without disabling DTD / external-entity ` + "processing. Vulnerable to external-entity disclosure " + "(CWE-611) and billion-laughs / quadratic blow-up DoS " + '(CWE-776). Add `setFeature("http://apache.org/xml/features/' + 'disallow-doctype-decl", true)` (or the equivalent) before ' + "parsing.",
47029
47325
  file,
47030
47326
  line,
47031
47327
  fix: this.fixForJava(det.api),
47032
- evidence: { ...det, language, safeFeatureInFile: false }
47328
+ evidence: { ...det, language, safeFeatureInFile: safeInFile }
47033
47329
  });
47034
47330
  }
47035
47331
  return { findings };
@@ -47052,7 +47348,7 @@ class XmlEntityExpansionPass {
47052
47348
  cwe: det.cwe,
47053
47349
  severity: "high",
47054
47350
  level: "error",
47055
- message: `${det.api} called without an entity-safe parser. Vulnerable ` + "to billion-laughs / quadratic blow-up DoS (CWE-776) and " + "external-entity disclosure (CWE-611). Use `defusedxml` or pass " + "an `XMLParser(resolve_entities=False)` to lxml.",
47351
+ message: `${det.api} called without an entity-safe parser. Vulnerable ` + (det.cwe === "CWE-611" ? "to external-entity disclosure (CWE-611) and billion-laughs / " + "quadratic blow-up DoS (CWE-776). " : "to billion-laughs / quadratic blow-up DoS (CWE-776). ") + "Use `defusedxml` or pass " + "an `XMLParser(resolve_entities=False)` to lxml.",
47056
47352
  file,
47057
47353
  line,
47058
47354
  fix: this.fixForPython(det.api),
@@ -47063,6 +47359,63 @@ class XmlEntityExpansionPass {
47063
47359
  }
47064
47360
  return { findings };
47065
47361
  }
47362
+ methodRanges(types) {
47363
+ const out2 = [];
47364
+ for (const t of types ?? []) {
47365
+ for (const m of t.methods ?? []) {
47366
+ if (typeof m.start_line === "number" && typeof m.end_line === "number")
47367
+ out2.push([m.start_line, m.end_line]);
47368
+ }
47369
+ }
47370
+ return out2;
47371
+ }
47372
+ isProvablyUnhardened(lines, line, methodRanges, api) {
47373
+ let range = null;
47374
+ for (const r of methodRanges) {
47375
+ if (line < r[0] || line > r[1])
47376
+ continue;
47377
+ if (!range || r[1] - r[0] < range[1] - range[0])
47378
+ range = r;
47379
+ }
47380
+ if (!range)
47381
+ return false;
47382
+ let decl = "";
47383
+ let declEnd = line;
47384
+ for (;declEnd <= Math.min(line + 3, range[1]); declEnd++) {
47385
+ decl += " " + (lines[declEnd - 1] ?? "").trim();
47386
+ if (decl.includes(";"))
47387
+ break;
47388
+ }
47389
+ const body2 = lines.slice(line - 1, range[1]).join(`
47390
+ `);
47391
+ if (api === "TransformerFactory" && !/\bnew\s+StreamSource\s*\(/.test(body2))
47392
+ return false;
47393
+ if (JAVA_HARDENING_SETTER_RE.test(body2))
47394
+ return false;
47395
+ const chained = JAVA_FACTORY_CHAINED_RE.exec(decl);
47396
+ if (chained) {
47397
+ const prod = /[\w>\]]\s+([A-Za-z_$][\w$]*)\s*=/.exec(decl);
47398
+ if (!prod)
47399
+ return true;
47400
+ const pn = prod[1].replace(/\$/g, "\\$");
47401
+ const after = lines.slice(declEnd, range[1]).join(`
47402
+ `);
47403
+ return !new RegExp(`[(,]\\s*${pn}\\s*[,)]|\\breturn\\s+${pn}\\s*;`).test(after);
47404
+ }
47405
+ const m = /[\w>\]]\s+([A-Za-z_$][\w$]*)\s*=\s*[\w.]*\bnewInstance\s*\([^()]*\)\s*;/.exec(decl);
47406
+ if (!m)
47407
+ return false;
47408
+ const name2 = m[1].replace(/\$/g, "\\$");
47409
+ const rest = lines.slice(declEnd, range[1]).join(`
47410
+ `);
47411
+ const builds = new RegExp(`\\b${name2}\\s*\\.\\s*(?:newDocumentBuilder|newSAXParser|newTransformer|newTemplates|newSchema|createXMLStreamReader|createXMLEventReader)\\s*\\(`);
47412
+ if (!builds.test(rest))
47413
+ return false;
47414
+ const escapes = new RegExp(`[(,]\\s*${name2}\\s*[,)]|\\breturn\\s+${name2}\\s*;`);
47415
+ if (escapes.test(rest))
47416
+ return false;
47417
+ return true;
47418
+ }
47066
47419
  detectJavaCall(call) {
47067
47420
  if (call.method_name !== "newInstance")
47068
47421
  return null;
@@ -47073,7 +47426,7 @@ class XmlEntityExpansionPass {
47073
47426
  return {
47074
47427
  pattern: `${factory}.newInstance()`,
47075
47428
  api: factory,
47076
- cwe: "CWE-776"
47429
+ cwe: "CWE-611"
47077
47430
  };
47078
47431
  }
47079
47432
  }
@@ -47086,7 +47439,7 @@ class XmlEntityExpansionPass {
47086
47439
  return {
47087
47440
  pattern: `etree.${method}`,
47088
47441
  api: `lxml.etree.${method}`,
47089
- cwe: "CWE-776"
47442
+ cwe: "CWE-611"
47090
47443
  };
47091
47444
  }
47092
47445
  if ((method === "parse" || method === "fromstring") && (recv === "ET" || recv === "ElementTree" || recv.endsWith(".ElementTree"))) {
@@ -49777,7 +50130,7 @@ var colors = {
49777
50130
  };
49778
50131
 
49779
50132
  // src/version.ts
49780
- var version = "4.9.21";
50133
+ var version = "4.9.23";
49781
50134
 
49782
50135
  // src/formatters.ts
49783
50136
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -50322,8 +50675,8 @@ function generateSarifResults(results, crossFileData) {
50322
50675
 
50323
50676
  // src/build-info.ts
50324
50677
  var buildInfo = {
50325
- gitSha: "1ac1389",
50326
- builtAt: "2026-09-21T17:09:28.224Z"
50678
+ gitSha: "e7e65f8",
50679
+ builtAt: "2026-09-22T22:15:34.355Z"
50327
50680
  };
50328
50681
 
50329
50682
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.21",
3
+ "version": "4.9.23",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@cognium/project-profile-detect": "1.1.1",
70
- "circle-ir": "4.9.21"
70
+ "circle-ir": "4.9.23"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/node": "^26.6.1",