cognium-dev 4.9.27 → 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 +463 -35
  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",
@@ -5506,6 +5532,33 @@ function extractCSharpCalls(tree, cache) {
5506
5532
  });
5507
5533
  continue;
5508
5534
  }
5535
+ if (left?.type === "identifier" && asn.parent?.type === "initializer_expression") {
5536
+ const creation = asn.parent.parent;
5537
+ if (creation?.type !== "object_creation_expression")
5538
+ continue;
5539
+ const ctorType = creation.childForFieldName("type");
5540
+ const right2 = asn.childForFieldName("right");
5541
+ if (!ctorType || !right2)
5542
+ continue;
5543
+ const typeName = csharpBareName(ctorType);
5544
+ const rhsText2 = getNodeText(right2);
5545
+ calls.push({
5546
+ method_name: getNodeText(left),
5547
+ receiver: typeName,
5548
+ receiver_type: typeName,
5549
+ receiver_type_fqn: null,
5550
+ arguments: [{
5551
+ position: 0,
5552
+ expression: rhsText2,
5553
+ variable: right2.type === "identifier" ? rhsText2 : null,
5554
+ literal: CSHARP_LITERAL_NODE_TYPES.has(right2.type) ? rhsText2 : null,
5555
+ value: null
5556
+ }],
5557
+ location: { line: asn.startPosition.row + 1, column: asn.startPosition.column },
5558
+ in_method: findEnclosingMethod(asn)
5559
+ });
5560
+ continue;
5561
+ }
5509
5562
  if (left?.type !== "member_access_expression")
5510
5563
  continue;
5511
5564
  const nameNode = left.childForFieldName("name");
@@ -5518,6 +5571,16 @@ function extractCSharpCalls(tree, cache) {
5518
5571
  const recvType = recv ? typeMap.get(recv) : undefined;
5519
5572
  if (recvType !== "DirectorySearcher")
5520
5573
  continue;
5574
+ } else if (propName === "StatusDescription") {
5575
+ const recv = exprNode ? getNodeText(exprNode) : "";
5576
+ const recvType = recv ? typeMap.get(recv) : undefined;
5577
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType)))
5578
+ continue;
5579
+ } else if (propName === "BaseAddress") {
5580
+ const recv = exprNode ? getNodeText(exprNode) : null;
5581
+ const recvType = recv ? typeMap.get(recv) : undefined;
5582
+ if (recvType !== "HttpClient")
5583
+ continue;
5521
5584
  } else if (propName !== "CommandText") {
5522
5585
  continue;
5523
5586
  }
@@ -5541,8 +5604,36 @@ function extractCSharpCalls(tree, cache) {
5541
5604
  in_method: findEnclosingMethod(asn)
5542
5605
  });
5543
5606
  }
5607
+ const casts = getNodesFromCache(tree.rootNode, "cast_expression", cache);
5608
+ for (const cast of casts) {
5609
+ const typeNode = cast.childForFieldName("type");
5610
+ const valueNode = cast.childForFieldName("value");
5611
+ if (!typeNode || !valueNode)
5612
+ continue;
5613
+ const typeName = csharpBareName(typeNode);
5614
+ if (!CSHARP_RAW_MARKUP_CAST_TYPES.has(typeName))
5615
+ continue;
5616
+ const valueText = getNodeText(valueNode);
5617
+ calls.push({
5618
+ method_name: typeName,
5619
+ receiver: null,
5620
+ receiver_type: null,
5621
+ receiver_type_fqn: null,
5622
+ arguments: [{
5623
+ position: 0,
5624
+ expression: valueText,
5625
+ variable: valueNode.type === "identifier" ? valueText : null,
5626
+ literal: CSHARP_LITERAL_NODE_TYPES.has(valueNode.type) ? valueText : null,
5627
+ value: null
5628
+ }],
5629
+ location: { line: cast.startPosition.row + 1, column: cast.startPosition.column },
5630
+ in_method: findEnclosingMethod(cast),
5631
+ is_constructor: true
5632
+ });
5633
+ }
5544
5634
  return calls;
5545
5635
  }
5636
+ var CSHARP_RAW_MARKUP_CAST_TYPES = new Set(["MarkupString"]);
5546
5637
  function extractCSharpArguments(argsNode) {
5547
5638
  const args2 = [];
5548
5639
  let position = 0;
@@ -5563,12 +5654,22 @@ function extractCSharpArguments(argsNode) {
5563
5654
  position: position++,
5564
5655
  expression: text,
5565
5656
  variable: expr?.type === "identifier" ? text : null,
5566
- literal: expr?.type === "string_literal" ? text : null,
5657
+ literal: expr && CSHARP_LITERAL_NODE_TYPES.has(expr.type) ? text : null,
5567
5658
  value: null
5568
5659
  });
5569
5660
  }
5570
5661
  return args2;
5571
5662
  }
5663
+ var CSHARP_LITERAL_NODE_TYPES = new Set([
5664
+ "string_literal",
5665
+ "verbatim_string_literal",
5666
+ "raw_string_literal",
5667
+ "character_literal",
5668
+ "integer_literal",
5669
+ "real_literal",
5670
+ "boolean_literal",
5671
+ "null_literal"
5672
+ ]);
5572
5673
  function extractJavaScriptCalls(tree, cache) {
5573
5674
  const calls = [];
5574
5675
  const context = buildJSResolutionContext(tree, cache);
@@ -11133,6 +11234,14 @@ var DEFAULT_SOURCES = [
11133
11234
  { method: "nextLine", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11134
11235
  { method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11135
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"] },
11136
11245
  { method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11137
11246
  { method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11138
11247
  { method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
@@ -12482,8 +12591,6 @@ var DEFAULT_SINKS = [
12482
12591
  { method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12483
12592
  { method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12484
12593
  { method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12485
- { method: "from_str", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0] },
12486
- { method: "from_slice", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0] },
12487
12594
  { method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12488
12595
  { method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12489
12596
  { method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -12658,8 +12765,8 @@ var DEFAULT_SINKS = [
12658
12765
  { method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12659
12766
  { method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12660
12767
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12661
- { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
12662
- { 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"] },
12663
12770
  { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
12664
12771
  { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
12665
12772
  { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -12689,6 +12796,10 @@ var DEFAULT_SINKS = [
12689
12796
  { method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
12690
12797
  { method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12691
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"] },
12692
12803
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12693
12804
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
12694
12805
  { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
@@ -12707,10 +12818,12 @@ var DEFAULT_SINKS = [
12707
12818
  { method: "WriteAsync", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12708
12819
  { method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12709
12820
  { method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12821
+ { method: "StatusDescription", class: "Response", type: "xss", cwe: "CWE-81", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12710
12822
  { method: "Content", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12711
12823
  { method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12712
12824
  { method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
12713
12825
  { method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
12826
+ { method: "BaseAddress", class: "HttpClient", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12714
12827
  { method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12715
12828
  { method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12716
12829
  { method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
@@ -14274,6 +14387,15 @@ function escapeRe(s) {
14274
14387
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14275
14388
  }
14276
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
+ }
14277
14399
  function argIsStringLiteral(call, position) {
14278
14400
  const arg = call.arguments.find((a) => a.position === position);
14279
14401
  if (!arg)
@@ -14544,6 +14666,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14544
14666
  if (pattern.safe_if_string_literal_at !== undefined && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
14545
14667
  continue;
14546
14668
  }
14669
+ if (pattern.safe_if_stream_arg_at !== undefined && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
14670
+ continue;
14671
+ }
14547
14672
  if (pattern.type === "command_injection") {
14548
14673
  if (call.is_constructor) {
14549
14674
  if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
@@ -14920,8 +15045,9 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
14920
15045
  return false;
14921
15046
  } else if (!call.receiver && !call.receiver_type) {
14922
15047
  const target = call.resolution?.target;
15048
+ const normalisedTarget = language === "rust" ? target?.replace(/::/g, ".") : target;
14923
15049
  const expectedTail = `${pattern.class}.${pattern.method}`;
14924
- if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {} else {
15050
+ if (normalisedTarget && (normalisedTarget === expectedTail || normalisedTarget.endsWith("." + expectedTail))) {} else {
14925
15051
  return false;
14926
15052
  }
14927
15053
  }
@@ -26079,7 +26205,7 @@ class LanguageSourcesPass {
26079
26205
  }
26080
26206
  }
26081
26207
  if (language === "csharp") {
26082
- additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code));
26208
+ additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code), ...findCSharpEarlyReturnGuardSanitizers(code, language));
26083
26209
  }
26084
26210
  if (language === "java") {
26085
26211
  additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
@@ -26791,6 +26917,10 @@ function findCSharpRequestSources(sourceCode, language) {
26791
26917
  const formFileDeclRe = /\bIFormFile\s+([A-Za-z_]\w*)/g;
26792
26918
  const httpRequestReceivers = new Set(["Request"]);
26793
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;
26794
26924
  for (const line of lines) {
26795
26925
  const formRe = new RegExp(formFileDeclRe.source, "g");
26796
26926
  let d;
@@ -26799,15 +26929,25 @@ function findCSharpRequestSources(sourceCode, language) {
26799
26929
  const reqRe = new RegExp(httpRequestDeclRe.source, "g");
26800
26930
  while ((d = reqRe.exec(line)) !== null)
26801
26931
  httpRequestReceivers.add(d[1]);
26802
- }
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;
26803
26941
  const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
26804
26942
  const requestReadRe = new RegExp(`\\b(?:${[...httpRequestReceivers].join("|")})\\s*\\.\\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\\b`);
26805
- 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;
26806
26944
  const inlineReads = [
26807
26945
  [requestReadRe, "http_param"],
26808
26946
  [formFileReadRe, "http_param"],
26809
26947
  [consoleReadRe, "io_input"],
26810
- [envReadRe, "env_input"]
26948
+ [envReadRe, "env_input"],
26949
+ [streamReadRe, "io_input"],
26950
+ [dbReadRe, "db_input"]
26811
26951
  ];
26812
26952
  const inlineRead = (text) => {
26813
26953
  const argsStart = text.indexOf("(");
@@ -26829,7 +26969,8 @@ function findCSharpRequestSources(sourceCode, language) {
26829
26969
  const embeddedAssignRe = /(?:^|[{;])\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*([^;{}]+)/;
26830
26970
  for (let i2 = 0;i2 < lines.length; i2++) {
26831
26971
  const m = assignRe.exec(lines[i2]);
26832
- if (!m) {
26972
+ const rhsCrossesBlock = m !== null && m[2].includes("{");
26973
+ if (!m || rhsCrossesBlock) {
26833
26974
  const stmt = lines[i2];
26834
26975
  if (stmt.includes("{") && !/^\s*(?:\/\/|\/\*|\*)/.test(stmt)) {
26835
26976
  const emb = new RegExp(embeddedAssignRe.source, "g");
@@ -26847,7 +26988,7 @@ function findCSharpRequestSources(sourceCode, language) {
26847
26988
  sources.push({
26848
26989
  type: type3,
26849
26990
  location: `${varName2} = ${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""}`,
26850
- severity: "high",
26991
+ severity: type3 === "db_input" ? "medium" : "high",
26851
26992
  line: lineNumber3,
26852
26993
  confidence: 1,
26853
26994
  variable: varName2
@@ -26870,7 +27011,7 @@ function findCSharpRequestSources(sourceCode, language) {
26870
27011
  sources.push({
26871
27012
  type: type2,
26872
27013
  location: `${read} in ${code.substring(0, 50)}${code.length > 50 ? "..." : ""}`,
26873
- severity: "high",
27014
+ severity: type2 === "db_input" ? "medium" : "high",
26874
27015
  line: lineNumber2,
26875
27016
  confidence: 1,
26876
27017
  variable: "<inline>",
@@ -26888,7 +27029,7 @@ function findCSharpRequestSources(sourceCode, language) {
26888
27029
  sources.push({
26889
27030
  type,
26890
27031
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
26891
- severity: "high",
27032
+ severity: type === "db_input" ? "medium" : "high",
26892
27033
  line: lineNumber,
26893
27034
  confidence: 1,
26894
27035
  variable: varName
@@ -27082,7 +27223,7 @@ function findPythonAssignmentSources(sourceCode, language) {
27082
27223
  sources.push({
27083
27224
  type,
27084
27225
  location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
27085
- severity: "high",
27226
+ severity: type === "db_input" ? "medium" : "high",
27086
27227
  line: lineNumber,
27087
27228
  confidence: 0.95,
27088
27229
  variable: varName
@@ -29574,6 +29715,76 @@ function guardRejects(lines, i2, terminatorRe, window2) {
29574
29715
  }
29575
29716
  return false;
29576
29717
  }
29718
+ function findCSharpEarlyReturnGuardSanitizers(code, language) {
29719
+ if (language !== "csharp")
29720
+ return [];
29721
+ const sanitizers = [];
29722
+ const lines = code.split(`
29723
+ `);
29724
+ const terminatorRe = /\b(throw|return)\b/;
29725
+ const allowlistRes = [
29726
+ /\bif\s*\(\s*Array\s*\.\s*IndexOf\s*\([^,]+,\s*([A-Za-z_][\w.]*)\s*\)\s*<\s*0\s*\)/,
29727
+ /\bif\s*\(\s*!\s*[A-Za-z_][\w.]*\s*\.\s*Contains\s*\(\s*([A-Za-z_][\w.]*)\s*\)\s*\)/
29728
+ ];
29729
+ const charClassRe = /\bif\s*\(\s*!\s*([A-Za-z_][\w.]*)\s*\.\s*All\s*\(\s*char\s*\.\s*Is[A-Za-z]+\s*\)\s*\)/;
29730
+ for (let i2 = 0;i2 < lines.length; i2++) {
29731
+ let guardedExpr = null;
29732
+ for (const re of allowlistRes) {
29733
+ const m = re.exec(lines[i2]);
29734
+ if (m) {
29735
+ guardedExpr = m[1];
29736
+ break;
29737
+ }
29738
+ }
29739
+ if (!guardedExpr) {
29740
+ const m = charClassRe.exec(lines[i2]);
29741
+ if (m)
29742
+ guardedExpr = m[1];
29743
+ }
29744
+ if (!guardedExpr)
29745
+ continue;
29746
+ if (!guardRejects(lines, i2, terminatorRe, 6))
29747
+ continue;
29748
+ const root = guardedExpr.split(".")[0];
29749
+ const guarded = new Set([guardedExpr, root]);
29750
+ const assignRe = /^\s*(?:(?:var|string)\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
29751
+ for (let b = i2 - 1;b >= 0; b--) {
29752
+ const a = assignRe.exec(lines[b]);
29753
+ if (!a || !guarded.has(a[1]))
29754
+ continue;
29755
+ for (const id of a[2].match(/[A-Za-z_]\w*/g) ?? [])
29756
+ guarded.add(id);
29757
+ }
29758
+ const mentions = (t) => {
29759
+ for (const n of guarded)
29760
+ if (new RegExp(`\\b${n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(t))
29761
+ return true;
29762
+ return false;
29763
+ };
29764
+ for (let l = i2 + 1;l < lines.length; l++) {
29765
+ const a = assignRe.exec(lines[l]);
29766
+ if (a && mentions(a[2]))
29767
+ guarded.add(a[1]);
29768
+ if (mentions(lines[l])) {
29769
+ sanitizers.push({
29770
+ type: "csharp_early_return_guard",
29771
+ method: "guard",
29772
+ line: l + 1,
29773
+ sanitizes: [
29774
+ "ssrf",
29775
+ "xpath_injection",
29776
+ "ldap_injection",
29777
+ "path_traversal",
29778
+ "command_injection",
29779
+ "sql_injection",
29780
+ "external_taint_escape"
29781
+ ]
29782
+ });
29783
+ }
29784
+ }
29785
+ }
29786
+ return sanitizers;
29787
+ }
29577
29788
  function findCSharpFullPathStartsWithGuardSanitizers(code) {
29578
29789
  const sanitizers = [];
29579
29790
  const lines = code.split(`
@@ -36647,6 +36858,65 @@ class LibraryProfileXssGatePass {
36647
36858
  }
36648
36859
 
36649
36860
  // ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
36861
+ var CONTAINER_READ_SOURCES = new Set(["plugin_param", "config_param"]);
36862
+ function makeConstPropVeto(constProp, graph, sources, code) {
36863
+ const lines = typeof code === "string" ? code.split(`
36864
+ `) : [];
36865
+ const selfReassign = new Map;
36866
+ const escape = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
36867
+ for (const [line, defs] of graph.defsByLine) {
36868
+ const text = lines[line - 1];
36869
+ if (!text)
36870
+ continue;
36871
+ for (const d of defs) {
36872
+ if (d.kind !== "local" && d.kind !== "param")
36873
+ continue;
36874
+ const v = escape(d.variable);
36875
+ const m = new RegExp(`(?:^|[^\\w.$])${v}\\s*(?:([-+*/%&|^]|<<|>>)=|=(?!=))(.*)$`).exec(text);
36876
+ if (!m)
36877
+ continue;
36878
+ if (m[1] || new RegExp(`(?:^|[^\\w.$])${v}\\b`).test(m[2])) {
36879
+ const arr = selfReassign.get(d.variable) ?? [];
36880
+ arr.push(line);
36881
+ selfReassign.set(d.variable, arr);
36882
+ }
36883
+ }
36884
+ }
36885
+ const reassignedAt = (v, line) => (selfReassign.get(v) ?? []).includes(line);
36886
+ const defAt = (v, line) => {
36887
+ const own = (graph.defsByLine.get(line) ?? []).find((d) => d.variable === v);
36888
+ if (own)
36889
+ return own;
36890
+ const use = (graph.usesByLine.get(line) ?? []).find((u) => u.variable === v && u.def_id != null);
36891
+ return use ? graph.defById.get(use.def_id) : undefined;
36892
+ };
36893
+ return (step, sourceLine, sinkLine) => {
36894
+ const r = isFalsePositive(constProp, step.line, step.variable);
36895
+ if (!r.isFalsePositive)
36896
+ return false;
36897
+ if (r.reason !== "variable_not_tainted")
36898
+ return true;
36899
+ const lo = Math.min(sourceLine, sinkLine);
36900
+ let d = defAt(step.variable, step.line);
36901
+ let selfHops = 0;
36902
+ for (let guard = 0;d && d.line > lo && reassignedAt(d.variable, d.line) && guard < 32; guard++) {
36903
+ const here = d.id;
36904
+ const prior = (graph.usesByLine.get(d.line) ?? []).find((u) => u.variable === d.variable && u.def_id != null && u.def_id !== here);
36905
+ if (!prior)
36906
+ break;
36907
+ selfHops++;
36908
+ d = graph.defById.get(prior.def_id);
36909
+ }
36910
+ if (!d)
36911
+ return true;
36912
+ if (d.line === sourceLine && sources.some((s) => s.line === sourceLine && !CONTAINER_READ_SOURCES.has(s.type)))
36913
+ return false;
36914
+ if (selfHops > 0 && d.line >= lo)
36915
+ return false;
36916
+ return true;
36917
+ };
36918
+ }
36919
+
36650
36920
  class TaintPropagationPass {
36651
36921
  name = "taint-propagation";
36652
36922
  category = "security";
@@ -36664,12 +36934,12 @@ class TaintPropagationPass {
36664
36934
  return { flows: [] };
36665
36935
  }
36666
36936
  const propagationResult = propagateTaint(graph, sources, sinks, sanitizers);
36937
+ const isVetoed = makeConstPropVeto(constProp, graph, sources, ctx.code);
36667
36938
  const verifiedFlows = propagationResult.flows.filter((flow) => {
36668
36939
  if (constProp.unreachableLines.has(flow.sink.line))
36669
36940
  return false;
36670
36941
  for (const step of flow.path) {
36671
- const fpCheck = isFalsePositive(constProp, step.line, step.variable);
36672
- if (fpCheck.isFalsePositive)
36942
+ if (isVetoed(step, flow.source.line, flow.sink.line))
36673
36943
  return false;
36674
36944
  }
36675
36945
  if (isCorrelatedPredicateFP(constProp, flow))
@@ -36775,7 +37045,7 @@ class TaintPropagationPass {
36775
37045
  continue;
36776
37046
  let isFP = false;
36777
37047
  for (const step of f.path) {
36778
- if (isFalsePositive(constProp, step.line, step.variable).isFalsePositive) {
37048
+ if (isVetoed(step, f.source_line, f.sink_line)) {
36779
37049
  isFP = true;
36780
37050
  break;
36781
37051
  }
@@ -36801,7 +37071,7 @@ class TaintPropagationPass {
36801
37071
  continue;
36802
37072
  let isFP = false;
36803
37073
  for (const step of f.path) {
36804
- if (isFalsePositive(constProp, step.line, step.variable).isFalsePositive) {
37074
+ if (isVetoed(step, f.source_line, f.sink_line)) {
36805
37075
  isFP = true;
36806
37076
  break;
36807
37077
  }
@@ -37549,13 +37819,17 @@ function maskStringLiteralsForTokenScan(expr) {
37549
37819
  }
37550
37820
  return out2;
37551
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
+ }
37552
37826
  function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
37553
37827
  const flows = [];
37554
- const goFnRanges = language === "go" && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
37555
- 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) => {
37556
37830
  let best = -1;
37557
37831
  let bestSpan = -1;
37558
- for (const [start2, end] of goFnRanges) {
37832
+ for (const [start2, end] of ranges) {
37559
37833
  if (line >= start2 && line <= end && end - start2 > bestSpan) {
37560
37834
  best = start2;
37561
37835
  bestSpan = end - start2;
@@ -37563,6 +37837,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
37563
37837
  }
37564
37838
  return best;
37565
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);
37566
37844
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
37567
37845
  const aliasSanitizedFor = new Map;
37568
37846
  if (language === "python" && typeof code === "string") {
@@ -37898,15 +38176,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
37898
38176
  const [, obj, rhs] = m;
37899
38177
  if (known.has(obj))
37900
38178
  continue;
37901
- const codePart = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ");
37902
- const rhsTainted = sourcesWithVar.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
37903
- if (!rhsTainted)
38179
+ if (!argsReferenceTaintedVar(rhs, sourcesWithVar))
37904
38180
  continue;
37905
38181
  sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
37906
38182
  added = true;
37907
38183
  }
37908
38184
  }
37909
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
+ }
37910
38224
  const SIMPLE_IDENT = /^[\p{L}\p{N}_]+$/u;
37911
38225
  const reCache = new Map;
37912
38226
  for (const s of sourcesWithVar) {
@@ -37977,6 +38291,12 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
37977
38291
  if (srcFn !== -1 && sinkFn !== -1 && srcFn !== sinkFn)
37978
38292
  continue;
37979
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
+ }
37980
38300
  if (complex) {
37981
38301
  const re = reCache.get(source.variable);
37982
38302
  if (!re || !re.test(expr))
@@ -38205,6 +38525,109 @@ var GO_PURE_PACKAGES = new Set([
38205
38525
  "hex",
38206
38526
  "base64"
38207
38527
  ]);
38528
+ var CSHARP_PURE_STATIC_RECEIVERS = new Set([
38529
+ "string",
38530
+ "String",
38531
+ "Convert",
38532
+ "Encoding",
38533
+ "BitConverter",
38534
+ "Math",
38535
+ "Int16",
38536
+ "Int32",
38537
+ "Int64",
38538
+ "UInt16",
38539
+ "UInt32",
38540
+ "UInt64",
38541
+ "Double",
38542
+ "Single",
38543
+ "Decimal",
38544
+ "Boolean",
38545
+ "Byte",
38546
+ "SByte",
38547
+ "Char",
38548
+ "Guid",
38549
+ "DateTime",
38550
+ "DateTimeOffset",
38551
+ "TimeSpan",
38552
+ "Enum",
38553
+ "Array",
38554
+ "Buffer",
38555
+ "Nullable",
38556
+ "CultureInfo",
38557
+ "StringComparer",
38558
+ "Comparer",
38559
+ "EqualityComparer",
38560
+ "Console",
38561
+ "Debug",
38562
+ "Trace",
38563
+ "Logger",
38564
+ "logger",
38565
+ "_logger",
38566
+ "Log"
38567
+ ]);
38568
+ var CSHARP_NON_ESCAPE_METHODS = new Set([
38569
+ "ToString",
38570
+ "ToArray",
38571
+ "ToList",
38572
+ "ToCharArray",
38573
+ "GetHashCode",
38574
+ "Equals",
38575
+ "CompareTo",
38576
+ "Trim",
38577
+ "TrimStart",
38578
+ "TrimEnd",
38579
+ "Substring",
38580
+ "Split",
38581
+ "Join",
38582
+ "Concat",
38583
+ "PadLeft",
38584
+ "PadRight",
38585
+ "ToLower",
38586
+ "ToUpper",
38587
+ "ToLowerInvariant",
38588
+ "ToUpperInvariant",
38589
+ "StartsWith",
38590
+ "EndsWith",
38591
+ "Contains",
38592
+ "IndexOf",
38593
+ "LastIndexOf",
38594
+ "Insert",
38595
+ "Remove",
38596
+ "Normalize",
38597
+ "Append",
38598
+ "AppendLine",
38599
+ "AppendFormat",
38600
+ "TryParse",
38601
+ "Parse",
38602
+ "LogInformation",
38603
+ "LogWarning",
38604
+ "LogError",
38605
+ "LogDebug",
38606
+ "LogTrace",
38607
+ "LogCritical",
38608
+ "Information",
38609
+ "Warning",
38610
+ "Verbose",
38611
+ "Fatal",
38612
+ "BeginScope",
38613
+ "Add",
38614
+ "AddLast",
38615
+ "AddFirst",
38616
+ "AddRange",
38617
+ "TryAdd",
38618
+ "Insert",
38619
+ "Push",
38620
+ "Enqueue"
38621
+ ]);
38622
+ function isCSharpNonEscapeCall(call) {
38623
+ const name2 = call.method_name;
38624
+ const recv = (call.receiver ?? "").split(".").pop() ?? "";
38625
+ if (CSHARP_PURE_STATIC_RECEIVERS.has(recv))
38626
+ return true;
38627
+ if (CSHARP_NON_ESCAPE_METHODS.has(name2))
38628
+ return true;
38629
+ return false;
38630
+ }
38208
38631
  function isGoNonEscapeCall(call) {
38209
38632
  const name2 = call.method_name;
38210
38633
  const recv = (call.receiver ?? "").split(".").pop() ?? "";
@@ -38452,6 +38875,9 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
38452
38875
  if (graph.ir.meta.language === "go" && isGoNonEscapeCall(call)) {
38453
38876
  continue;
38454
38877
  }
38878
+ if (graph.ir.meta.language === "csharp" && isCSharpNonEscapeCall(call)) {
38879
+ continue;
38880
+ }
38455
38881
  const bashSafeBuiltins = new Set([
38456
38882
  "echo",
38457
38883
  "printf",
@@ -40086,6 +40512,8 @@ class VariableShadowingPass {
40086
40512
  const outerEntry = declLocals[0];
40087
40513
  for (let i2 = 1;i2 < numDeclLocals; i2++) {
40088
40514
  const inner = declLocals[i2];
40515
+ if (inner.def.line <= outerEntry.def.line)
40516
+ continue;
40089
40517
  if (!isInNestedScope(codeLines, outerEntry.def.line, inner.def.line))
40090
40518
  continue;
40091
40519
  const key = `${variable}-${inner.def.line}`;
@@ -50357,7 +50785,7 @@ var colors = {
50357
50785
  };
50358
50786
 
50359
50787
  // src/version.ts
50360
- var version = "4.9.27";
50788
+ var version = "4.9.29";
50361
50789
 
50362
50790
  // src/formatters.ts
50363
50791
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -50909,8 +51337,8 @@ function generateSarifResults(results, crossFileData) {
50909
51337
 
50910
51338
  // src/build-info.ts
50911
51339
  var buildInfo = {
50912
- gitSha: "39d07d9",
50913
- builtAt: "2026-09-29T23:22:05.194Z"
51340
+ gitSha: "9d1d45a",
51341
+ builtAt: "2026-10-01T03:14:59.073Z"
50914
51342
  };
50915
51343
 
50916
51344
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.27",
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.27"
68
+ "circle-ir": "4.9.29"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.2",