cognium-dev 4.9.28 → 4.9.29

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 +143 -27
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -3689,6 +3689,26 @@ function extractTypes(tree, cache, language) {
3689
3689
  }
3690
3690
  return extractJavaTypes(tree, cache);
3691
3691
  }
3692
+ function csharpMemberNodes(body2) {
3693
+ if (!body2)
3694
+ return [];
3695
+ const out2 = [];
3696
+ const visit = (parent, depth) => {
3697
+ if (depth > 32)
3698
+ return;
3699
+ for (let i2 = 0;i2 < parent.childCount; i2++) {
3700
+ const c = parent.child(i2);
3701
+ if (!c)
3702
+ continue;
3703
+ if (c.type.startsWith("preproc_"))
3704
+ visit(c, depth + 1);
3705
+ else
3706
+ out2.push(c);
3707
+ }
3708
+ };
3709
+ visit(body2, 0);
3710
+ return out2;
3711
+ }
3692
3712
  function extractCSharpTypes(tree, cache) {
3693
3713
  const types = [];
3694
3714
  const KINDS = {
@@ -3705,9 +3725,8 @@ function extractCSharpTypes(tree, cache) {
3705
3725
  const methods = [];
3706
3726
  const fields = extractCSharpFields(body2);
3707
3727
  if (body2) {
3708
- for (let i2 = 0;i2 < body2.childCount; i2++) {
3709
- const m = body2.child(i2);
3710
- if (!m || m.type !== "method_declaration")
3728
+ for (const m of csharpMemberNodes(body2)) {
3729
+ if (m.type !== "method_declaration")
3711
3730
  continue;
3712
3731
  const mName = m.childForFieldName("name");
3713
3732
  const paramList = m.childForFieldName("parameters");
@@ -3761,10 +3780,7 @@ function extractCSharpFields(body2) {
3761
3780
  const fields = [];
3762
3781
  if (!body2)
3763
3782
  return fields;
3764
- for (let i2 = 0;i2 < body2.childCount; i2++) {
3765
- const c = body2.child(i2);
3766
- if (!c)
3767
- continue;
3783
+ for (const c of csharpMemberNodes(body2)) {
3768
3784
  if (c.type === "field_declaration") {
3769
3785
  let varDecl = null;
3770
3786
  for (let k = 0;k < c.childCount; k++) {
@@ -5351,6 +5367,15 @@ function extractCalls(tree, cache, language) {
5351
5367
  function buildCSharpReceiverTypeMap(tree, cache) {
5352
5368
  const map = new Map;
5353
5369
  const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
5370
+ for (const prm of getNodesFromCache(tree.rootNode, "parameter", cache)) {
5371
+ const nameNode = prm.childForFieldName("name");
5372
+ const typeNode = prm.childForFieldName("type");
5373
+ if (nameNode?.type !== "identifier" || !typeNode)
5374
+ continue;
5375
+ const t = getNodeText(typeNode);
5376
+ if (t && t !== "var")
5377
+ map.set(getNodeText(nameNode), simple(t));
5378
+ }
5354
5379
  for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
5355
5380
  const typeNode = vd.childForFieldName("type");
5356
5381
  const declaredType = typeNode ? getNodeText(typeNode) : null;
@@ -5387,6 +5412,7 @@ function findFirstDescendant(node, type) {
5387
5412
  }
5388
5413
  return null;
5389
5414
  }
5415
+ var CSHARP_RESPONSE_TYPES = new Set(["HttpResponse", "HttpResponseBase"]);
5390
5416
  var CSHARP_COMMAND_EXECUTE_METHODS = new Set([
5391
5417
  "ExecuteScalar",
5392
5418
  "ExecuteReader",
@@ -5547,7 +5573,8 @@ function extractCSharpCalls(tree, cache) {
5547
5573
  continue;
5548
5574
  } else if (propName === "StatusDescription") {
5549
5575
  const recv = exprNode ? getNodeText(exprNode) : "";
5550
- if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv))
5576
+ const recvType = recv ? typeMap.get(recv) : undefined;
5577
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType)))
5551
5578
  continue;
5552
5579
  } else if (propName === "BaseAddress") {
5553
5580
  const recv = exprNode ? getNodeText(exprNode) : null;
@@ -11207,6 +11234,14 @@ var DEFAULT_SOURCES = [
11207
11234
  { method: "nextLine", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11208
11235
  { method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11209
11236
  { method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11237
+ { method: "ReadLine", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11238
+ { method: "ReadLineAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11239
+ { method: "ReadToEnd", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11240
+ { method: "ReadToEndAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11241
+ { method: "GetStream", class: "TcpClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11242
+ { method: "AcceptTcpClient", class: "TcpListener", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11243
+ { method: "OpenRead", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11244
+ { method: "DownloadString", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11210
11245
  { method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11211
11246
  { method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11212
11247
  { method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
@@ -12730,8 +12765,8 @@ var DEFAULT_SINKS = [
12730
12765
  { method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12731
12766
  { method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12732
12767
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12733
- { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12734
- { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12768
+ { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
12769
+ { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
12735
12770
  { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
12736
12771
  { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
12737
12772
  { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -12761,6 +12796,10 @@ var DEFAULT_SINKS = [
12761
12796
  { method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
12762
12797
  { method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12763
12798
  { method: "RestClient", class: "constructor", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12799
+ { method: "CompileAssemblyFromSource", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
12800
+ { method: "CompileAssemblyFromSourceBatch", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
12801
+ { method: "ParseText", class: "CSharpSyntaxTree", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12802
+ { method: "ParseText", class: "SyntaxFactory", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12764
12803
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12765
12804
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12766
12805
  { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
@@ -14348,6 +14387,15 @@ function escapeRe(s) {
14348
14387
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14349
14388
  }
14350
14389
  var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
14390
+ function argIsStreamExpression(call, position) {
14391
+ const arg = call.arguments.find((a) => a.position === position);
14392
+ if (!arg)
14393
+ return false;
14394
+ const expr = (arg.expression ?? "").trim();
14395
+ if (!expr)
14396
+ return false;
14397
+ return /^new\s+[A-Za-z_][\w.]*Stream\s*\(/.test(expr) || /\.\s*Get(?:Response)?Stream\s*\(/.test(expr) || /\.\s*OpenReadStream\s*\(/.test(expr) || /\.\s*OpenRead\s*\(/.test(expr) || /\.\s*(?:Body|BaseStream)\s*$/.test(expr) || /^(?:[A-Za-z_]\w*\.)*[A-Za-z_]*[sS]tream\d*$/.test(expr);
14398
+ }
14351
14399
  function argIsStringLiteral(call, position) {
14352
14400
  const arg = call.arguments.find((a) => a.position === position);
14353
14401
  if (!arg)
@@ -14618,6 +14666,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14618
14666
  if (pattern.safe_if_string_literal_at !== undefined && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
14619
14667
  continue;
14620
14668
  }
14669
+ if (pattern.safe_if_stream_arg_at !== undefined && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
14670
+ continue;
14671
+ }
14621
14672
  if (pattern.type === "command_injection") {
14622
14673
  if (call.is_constructor) {
14623
14674
  if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
@@ -26866,6 +26917,10 @@ function findCSharpRequestSources(sourceCode, language) {
26866
26917
  const formFileDeclRe = /\bIFormFile\s+([A-Za-z_]\w*)/g;
26867
26918
  const httpRequestReceivers = new Set(["Request"]);
26868
26919
  const httpRequestDeclRe = /\b(?:HttpRequest|HttpRequestBase)\??\s+([A-Za-z_]\w*)/g;
26920
+ const streamReaderVars = new Set;
26921
+ const streamReaderDeclRe = /\b(?:StreamReader|TextReader|BinaryReader)\??\s+([A-Za-z_]\w*)/g;
26922
+ const dbReaderVars = new Set;
26923
+ const dbReaderDeclRe = /\b(?:SqlDataReader|DbDataReader|IDataReader|OdbcDataReader|OleDbDataReader)\??\s+([A-Za-z_]\w*)/g;
26869
26924
  for (const line of lines) {
26870
26925
  const formRe = new RegExp(formFileDeclRe.source, "g");
26871
26926
  let d;
@@ -26874,15 +26929,25 @@ function findCSharpRequestSources(sourceCode, language) {
26874
26929
  const reqRe = new RegExp(httpRequestDeclRe.source, "g");
26875
26930
  while ((d = reqRe.exec(line)) !== null)
26876
26931
  httpRequestReceivers.add(d[1]);
26877
- }
26932
+ const srRe = new RegExp(streamReaderDeclRe.source, "g");
26933
+ while ((d = srRe.exec(line)) !== null)
26934
+ streamReaderVars.add(d[1]);
26935
+ const dbRe = new RegExp(dbReaderDeclRe.source, "g");
26936
+ while ((d = dbRe.exec(line)) !== null)
26937
+ dbReaderVars.add(d[1]);
26938
+ }
26939
+ const streamReadRe = streamReaderVars.size > 0 ? new RegExp(`\\b(?:${[...streamReaderVars].join("|")})\\s*\\.\\s*(?:ReadLine|ReadToEnd|ReadLineAsync|ReadToEndAsync|ReadBlock|Read)\\s*\\(`) : null;
26940
+ const dbReadRe = dbReaderVars.size > 0 ? new RegExp(`\\b(?:${[...dbReaderVars].join("|")})\\s*\\.\\s*(?:GetString|GetValue|GetInt32|GetInt64|GetDecimal|GetDateTime|GetFieldValue|\\[)`) : null;
26878
26941
  const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
26879
26942
  const requestReadRe = new RegExp(`\\b(?:${[...httpRequestReceivers].join("|")})\\s*\\.\\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\\b`);
26880
- const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : null;
26943
+ const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : streamReadRe?.test(text) ? "io_input" : dbReadRe?.test(text) ? "db_input" : null;
26881
26944
  const inlineReads = [
26882
26945
  [requestReadRe, "http_param"],
26883
26946
  [formFileReadRe, "http_param"],
26884
26947
  [consoleReadRe, "io_input"],
26885
- [envReadRe, "env_input"]
26948
+ [envReadRe, "env_input"],
26949
+ [streamReadRe, "io_input"],
26950
+ [dbReadRe, "db_input"]
26886
26951
  ];
26887
26952
  const inlineRead = (text) => {
26888
26953
  const argsStart = text.indexOf("(");
@@ -26904,7 +26969,8 @@ function findCSharpRequestSources(sourceCode, language) {
26904
26969
  const embeddedAssignRe = /(?:^|[{;])\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*([^;{}]+)/;
26905
26970
  for (let i2 = 0;i2 < lines.length; i2++) {
26906
26971
  const m = assignRe.exec(lines[i2]);
26907
- if (!m) {
26972
+ const rhsCrossesBlock = m !== null && m[2].includes("{");
26973
+ if (!m || rhsCrossesBlock) {
26908
26974
  const stmt = lines[i2];
26909
26975
  if (stmt.includes("{") && !/^\s*(?:\/\/|\/\*|\*)/.test(stmt)) {
26910
26976
  const emb = new RegExp(embeddedAssignRe.source, "g");
@@ -26922,7 +26988,7 @@ function findCSharpRequestSources(sourceCode, language) {
26922
26988
  sources.push({
26923
26989
  type: type3,
26924
26990
  location: `${varName2} = ${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""}`,
26925
- severity: "high",
26991
+ severity: type3 === "db_input" ? "medium" : "high",
26926
26992
  line: lineNumber3,
26927
26993
  confidence: 1,
26928
26994
  variable: varName2
@@ -26945,7 +27011,7 @@ function findCSharpRequestSources(sourceCode, language) {
26945
27011
  sources.push({
26946
27012
  type: type2,
26947
27013
  location: `${read} in ${code.substring(0, 50)}${code.length > 50 ? "..." : ""}`,
26948
- severity: "high",
27014
+ severity: type2 === "db_input" ? "medium" : "high",
26949
27015
  line: lineNumber2,
26950
27016
  confidence: 1,
26951
27017
  variable: "<inline>",
@@ -26963,7 +27029,7 @@ function findCSharpRequestSources(sourceCode, language) {
26963
27029
  sources.push({
26964
27030
  type,
26965
27031
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
26966
- severity: "high",
27032
+ severity: type === "db_input" ? "medium" : "high",
26967
27033
  line: lineNumber,
26968
27034
  confidence: 1,
26969
27035
  variable: varName
@@ -27157,7 +27223,7 @@ function findPythonAssignmentSources(sourceCode, language) {
27157
27223
  sources.push({
27158
27224
  type,
27159
27225
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
27160
- severity: "high",
27226
+ severity: type === "db_input" ? "medium" : "high",
27161
27227
  line: lineNumber,
27162
27228
  confidence: 0.95,
27163
27229
  variable: varName
@@ -37753,13 +37819,17 @@ function maskStringLiteralsForTokenScan(expr) {
37753
37819
  }
37754
37820
  return out2;
37755
37821
  }
37822
+ function argsReferenceTaintedVar(expr, sources) {
37823
+ const codePart = expr.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
37824
+ return sources.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
37825
+ }
37756
37826
  function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
37757
37827
  const flows = [];
37758
- const goFnRanges = language === "go" && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
37759
- const goFnStart = (line) => {
37828
+ const methodRanges = (langs) => language && langs.includes(language) && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
37829
+ const enclosingStart = (ranges, line) => {
37760
37830
  let best = -1;
37761
37831
  let bestSpan = -1;
37762
- for (const [start2, end] of goFnRanges) {
37832
+ for (const [start2, end] of ranges) {
37763
37833
  if (line >= start2 && line <= end && end - start2 > bestSpan) {
37764
37834
  best = start2;
37765
37835
  bestSpan = end - start2;
@@ -37767,6 +37837,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
37767
37837
  }
37768
37838
  return best;
37769
37839
  };
37840
+ const goFnRanges = methodRanges(["go"]);
37841
+ const goFnStart = (line) => enclosingStart(goFnRanges, line);
37842
+ const csFnRanges = methodRanges(["csharp"]);
37843
+ const csFnStart = (line) => enclosingStart(csFnRanges, line);
37770
37844
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
37771
37845
  const aliasSanitizedFor = new Map;
37772
37846
  if (language === "python" && typeof code === "string") {
@@ -38102,15 +38176,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
38102
38176
  const [, obj, rhs] = m;
38103
38177
  if (known.has(obj))
38104
38178
  continue;
38105
- const codePart = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ");
38106
- const rhsTainted = sourcesWithVar.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
38107
- if (!rhsTainted)
38179
+ if (!argsReferenceTaintedVar(rhs, sourcesWithVar))
38108
38180
  continue;
38109
38181
  sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
38110
38182
  added = true;
38111
38183
  }
38112
38184
  }
38113
38185
  }
38186
+ const builderSeeds = new Set;
38187
+ if (language === "csharp" && typeof code === "string" && sourcesWithVar.length > 0) {
38188
+ const lines = code.split(`
38189
+ `);
38190
+ const builders = new Set;
38191
+ for (const line of lines) {
38192
+ for (const m of line.matchAll(/\bStringBuilder\s+([\p{L}_][\p{L}\p{N}_]*)/gu))
38193
+ builders.add(m[1]);
38194
+ for (const m of line.matchAll(/\b([\p{L}_][\p{L}\p{N}_]*)\s*=\s*new\s+StringBuilder\b/gu))
38195
+ builders.add(m[1]);
38196
+ }
38197
+ if (builders.size > 0) {
38198
+ const appendRe = /([\p{L}_][\p{L}\p{N}_]*)\s*\.\s*(?:Append|AppendLine|AppendFormat|Insert)\s*\(([\s\S]*)/u;
38199
+ const anchor = sourcesWithVar.reduce((a, s) => s.line < a.line ? s : a, sourcesWithVar[0]);
38200
+ let added = true;
38201
+ let guard = 0;
38202
+ while (added && guard < lines.length + 2) {
38203
+ added = false;
38204
+ guard++;
38205
+ const known = new Set(sourcesWithVar.map((s) => s.variable));
38206
+ for (let i2 = 0;i2 < lines.length; i2++) {
38207
+ const m = appendRe.exec(lines[i2]);
38208
+ if (!m)
38209
+ continue;
38210
+ const [, obj, args2] = m;
38211
+ if (!builders.has(obj) || known.has(obj))
38212
+ continue;
38213
+ const appendFn = csFnStart(i2 + 1);
38214
+ const reachable = sourcesWithVar.filter((sv) => appendFn === -1 || csFnStart(sv.line) === -1 || csFnStart(sv.line) === appendFn);
38215
+ if (reachable.length > 0 && argsReferenceTaintedVar(args2, reachable)) {
38216
+ sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
38217
+ builderSeeds.add(`${obj}@${i2 + 1}`);
38218
+ added = true;
38219
+ }
38220
+ }
38221
+ }
38222
+ }
38223
+ }
38114
38224
  const SIMPLE_IDENT = /^[\p{L}\p{N}_]+$/u;
38115
38225
  const reCache = new Map;
38116
38226
  for (const s of sourcesWithVar) {
@@ -38181,6 +38291,12 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
38181
38291
  if (srcFn !== -1 && sinkFn !== -1 && srcFn !== sinkFn)
38182
38292
  continue;
38183
38293
  }
38294
+ if (csFnRanges.length > 0 && builderSeeds.has(`${source.variable}@${source.line}`)) {
38295
+ const srcFn = csFnStart(source.line);
38296
+ const sinkFn = csFnStart(sink.line);
38297
+ if (srcFn !== sinkFn)
38298
+ continue;
38299
+ }
38184
38300
  if (complex) {
38185
38301
  const re = reCache.get(source.variable);
38186
38302
  if (!re || !re.test(expr))
@@ -50669,7 +50785,7 @@ var colors = {
50669
50785
  };
50670
50786
 
50671
50787
  // src/version.ts
50672
- var version = "4.9.28";
50788
+ var version = "4.9.29";
50673
50789
 
50674
50790
  // src/formatters.ts
50675
50791
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -51221,8 +51337,8 @@ function generateSarifResults(results, crossFileData) {
51221
51337
 
51222
51338
  // src/build-info.ts
51223
51339
  var buildInfo = {
51224
- gitSha: "7bfe5de",
51225
- builtAt: "2026-10-01T00:24:15.337Z"
51340
+ gitSha: "9d1d45a",
51341
+ builtAt: "2026-10-01T03:14:59.073Z"
51226
51342
  };
51227
51343
 
51228
51344
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.28",
3
+ "version": "4.9.29",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "bin": {
6
6
  "cognium-dev": "dist/cli.js"
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@cognium/project-profile-detect": "1.1.2",
68
- "circle-ir": "4.9.28"
68
+ "circle-ir": "4.9.29"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.2",