cognium-dev 4.9.27 → 4.9.28

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 +324 -12
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -5506,6 +5506,33 @@ function extractCSharpCalls(tree, cache) {
5506
5506
  });
5507
5507
  continue;
5508
5508
  }
5509
+ if (left?.type === "identifier" && asn.parent?.type === "initializer_expression") {
5510
+ const creation = asn.parent.parent;
5511
+ if (creation?.type !== "object_creation_expression")
5512
+ continue;
5513
+ const ctorType = creation.childForFieldName("type");
5514
+ const right2 = asn.childForFieldName("right");
5515
+ if (!ctorType || !right2)
5516
+ continue;
5517
+ const typeName = csharpBareName(ctorType);
5518
+ const rhsText2 = getNodeText(right2);
5519
+ calls.push({
5520
+ method_name: getNodeText(left),
5521
+ receiver: typeName,
5522
+ receiver_type: typeName,
5523
+ receiver_type_fqn: null,
5524
+ arguments: [{
5525
+ position: 0,
5526
+ expression: rhsText2,
5527
+ variable: right2.type === "identifier" ? rhsText2 : null,
5528
+ literal: CSHARP_LITERAL_NODE_TYPES.has(right2.type) ? rhsText2 : null,
5529
+ value: null
5530
+ }],
5531
+ location: { line: asn.startPosition.row + 1, column: asn.startPosition.column },
5532
+ in_method: findEnclosingMethod(asn)
5533
+ });
5534
+ continue;
5535
+ }
5509
5536
  if (left?.type !== "member_access_expression")
5510
5537
  continue;
5511
5538
  const nameNode = left.childForFieldName("name");
@@ -5518,6 +5545,15 @@ function extractCSharpCalls(tree, cache) {
5518
5545
  const recvType = recv ? typeMap.get(recv) : undefined;
5519
5546
  if (recvType !== "DirectorySearcher")
5520
5547
  continue;
5548
+ } else if (propName === "StatusDescription") {
5549
+ const recv = exprNode ? getNodeText(exprNode) : "";
5550
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv))
5551
+ continue;
5552
+ } else if (propName === "BaseAddress") {
5553
+ const recv = exprNode ? getNodeText(exprNode) : null;
5554
+ const recvType = recv ? typeMap.get(recv) : undefined;
5555
+ if (recvType !== "HttpClient")
5556
+ continue;
5521
5557
  } else if (propName !== "CommandText") {
5522
5558
  continue;
5523
5559
  }
@@ -5541,8 +5577,36 @@ function extractCSharpCalls(tree, cache) {
5541
5577
  in_method: findEnclosingMethod(asn)
5542
5578
  });
5543
5579
  }
5580
+ const casts = getNodesFromCache(tree.rootNode, "cast_expression", cache);
5581
+ for (const cast of casts) {
5582
+ const typeNode = cast.childForFieldName("type");
5583
+ const valueNode = cast.childForFieldName("value");
5584
+ if (!typeNode || !valueNode)
5585
+ continue;
5586
+ const typeName = csharpBareName(typeNode);
5587
+ if (!CSHARP_RAW_MARKUP_CAST_TYPES.has(typeName))
5588
+ continue;
5589
+ const valueText = getNodeText(valueNode);
5590
+ calls.push({
5591
+ method_name: typeName,
5592
+ receiver: null,
5593
+ receiver_type: null,
5594
+ receiver_type_fqn: null,
5595
+ arguments: [{
5596
+ position: 0,
5597
+ expression: valueText,
5598
+ variable: valueNode.type === "identifier" ? valueText : null,
5599
+ literal: CSHARP_LITERAL_NODE_TYPES.has(valueNode.type) ? valueText : null,
5600
+ value: null
5601
+ }],
5602
+ location: { line: cast.startPosition.row + 1, column: cast.startPosition.column },
5603
+ in_method: findEnclosingMethod(cast),
5604
+ is_constructor: true
5605
+ });
5606
+ }
5544
5607
  return calls;
5545
5608
  }
5609
+ var CSHARP_RAW_MARKUP_CAST_TYPES = new Set(["MarkupString"]);
5546
5610
  function extractCSharpArguments(argsNode) {
5547
5611
  const args2 = [];
5548
5612
  let position = 0;
@@ -5563,12 +5627,22 @@ function extractCSharpArguments(argsNode) {
5563
5627
  position: position++,
5564
5628
  expression: text,
5565
5629
  variable: expr?.type === "identifier" ? text : null,
5566
- literal: expr?.type === "string_literal" ? text : null,
5630
+ literal: expr && CSHARP_LITERAL_NODE_TYPES.has(expr.type) ? text : null,
5567
5631
  value: null
5568
5632
  });
5569
5633
  }
5570
5634
  return args2;
5571
5635
  }
5636
+ var CSHARP_LITERAL_NODE_TYPES = new Set([
5637
+ "string_literal",
5638
+ "verbatim_string_literal",
5639
+ "raw_string_literal",
5640
+ "character_literal",
5641
+ "integer_literal",
5642
+ "real_literal",
5643
+ "boolean_literal",
5644
+ "null_literal"
5645
+ ]);
5572
5646
  function extractJavaScriptCalls(tree, cache) {
5573
5647
  const calls = [];
5574
5648
  const context = buildJSResolutionContext(tree, cache);
@@ -12482,8 +12556,6 @@ var DEFAULT_SINKS = [
12482
12556
  { method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12483
12557
  { method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12484
12558
  { 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
12559
  { method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12488
12560
  { method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12489
12561
  { method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -12707,10 +12779,12 @@ var DEFAULT_SINKS = [
12707
12779
  { method: "WriteAsync", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12708
12780
  { method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12709
12781
  { method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12782
+ { method: "StatusDescription", class: "Response", type: "xss", cwe: "CWE-81", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12710
12783
  { method: "Content", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
12711
12784
  { method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12712
12785
  { method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
12713
12786
  { method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
12787
+ { method: "BaseAddress", class: "HttpClient", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
12714
12788
  { method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12715
12789
  { method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
12716
12790
  { method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
@@ -14920,8 +14994,9 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
14920
14994
  return false;
14921
14995
  } else if (!call.receiver && !call.receiver_type) {
14922
14996
  const target = call.resolution?.target;
14997
+ const normalisedTarget = language === "rust" ? target?.replace(/::/g, ".") : target;
14923
14998
  const expectedTail = `${pattern.class}.${pattern.method}`;
14924
- if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {} else {
14999
+ if (normalisedTarget && (normalisedTarget === expectedTail || normalisedTarget.endsWith("." + expectedTail))) {} else {
14925
15000
  return false;
14926
15001
  }
14927
15002
  }
@@ -26079,7 +26154,7 @@ class LanguageSourcesPass {
26079
26154
  }
26080
26155
  }
26081
26156
  if (language === "csharp") {
26082
- additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code));
26157
+ additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code), ...findCSharpEarlyReturnGuardSanitizers(code, language));
26083
26158
  }
26084
26159
  if (language === "java") {
26085
26160
  additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
@@ -29574,6 +29649,76 @@ function guardRejects(lines, i2, terminatorRe, window2) {
29574
29649
  }
29575
29650
  return false;
29576
29651
  }
29652
+ function findCSharpEarlyReturnGuardSanitizers(code, language) {
29653
+ if (language !== "csharp")
29654
+ return [];
29655
+ const sanitizers = [];
29656
+ const lines = code.split(`
29657
+ `);
29658
+ const terminatorRe = /\b(throw|return)\b/;
29659
+ const allowlistRes = [
29660
+ /\bif\s*\(\s*Array\s*\.\s*IndexOf\s*\([^,]+,\s*([A-Za-z_][\w.]*)\s*\)\s*<\s*0\s*\)/,
29661
+ /\bif\s*\(\s*!\s*[A-Za-z_][\w.]*\s*\.\s*Contains\s*\(\s*([A-Za-z_][\w.]*)\s*\)\s*\)/
29662
+ ];
29663
+ const charClassRe = /\bif\s*\(\s*!\s*([A-Za-z_][\w.]*)\s*\.\s*All\s*\(\s*char\s*\.\s*Is[A-Za-z]+\s*\)\s*\)/;
29664
+ for (let i2 = 0;i2 < lines.length; i2++) {
29665
+ let guardedExpr = null;
29666
+ for (const re of allowlistRes) {
29667
+ const m = re.exec(lines[i2]);
29668
+ if (m) {
29669
+ guardedExpr = m[1];
29670
+ break;
29671
+ }
29672
+ }
29673
+ if (!guardedExpr) {
29674
+ const m = charClassRe.exec(lines[i2]);
29675
+ if (m)
29676
+ guardedExpr = m[1];
29677
+ }
29678
+ if (!guardedExpr)
29679
+ continue;
29680
+ if (!guardRejects(lines, i2, terminatorRe, 6))
29681
+ continue;
29682
+ const root = guardedExpr.split(".")[0];
29683
+ const guarded = new Set([guardedExpr, root]);
29684
+ const assignRe = /^\s*(?:(?:var|string)\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
29685
+ for (let b = i2 - 1;b >= 0; b--) {
29686
+ const a = assignRe.exec(lines[b]);
29687
+ if (!a || !guarded.has(a[1]))
29688
+ continue;
29689
+ for (const id of a[2].match(/[A-Za-z_]\w*/g) ?? [])
29690
+ guarded.add(id);
29691
+ }
29692
+ const mentions = (t) => {
29693
+ for (const n of guarded)
29694
+ if (new RegExp(`\\b${n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(t))
29695
+ return true;
29696
+ return false;
29697
+ };
29698
+ for (let l = i2 + 1;l < lines.length; l++) {
29699
+ const a = assignRe.exec(lines[l]);
29700
+ if (a && mentions(a[2]))
29701
+ guarded.add(a[1]);
29702
+ if (mentions(lines[l])) {
29703
+ sanitizers.push({
29704
+ type: "csharp_early_return_guard",
29705
+ method: "guard",
29706
+ line: l + 1,
29707
+ sanitizes: [
29708
+ "ssrf",
29709
+ "xpath_injection",
29710
+ "ldap_injection",
29711
+ "path_traversal",
29712
+ "command_injection",
29713
+ "sql_injection",
29714
+ "external_taint_escape"
29715
+ ]
29716
+ });
29717
+ }
29718
+ }
29719
+ }
29720
+ return sanitizers;
29721
+ }
29577
29722
  function findCSharpFullPathStartsWithGuardSanitizers(code) {
29578
29723
  const sanitizers = [];
29579
29724
  const lines = code.split(`
@@ -36647,6 +36792,65 @@ class LibraryProfileXssGatePass {
36647
36792
  }
36648
36793
 
36649
36794
  // ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
36795
+ var CONTAINER_READ_SOURCES = new Set(["plugin_param", "config_param"]);
36796
+ function makeConstPropVeto(constProp, graph, sources, code) {
36797
+ const lines = typeof code === "string" ? code.split(`
36798
+ `) : [];
36799
+ const selfReassign = new Map;
36800
+ const escape = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
36801
+ for (const [line, defs] of graph.defsByLine) {
36802
+ const text = lines[line - 1];
36803
+ if (!text)
36804
+ continue;
36805
+ for (const d of defs) {
36806
+ if (d.kind !== "local" && d.kind !== "param")
36807
+ continue;
36808
+ const v = escape(d.variable);
36809
+ const m = new RegExp(`(?:^|[^\\w.$])${v}\\s*(?:([-+*/%&|^]|<<|>>)=|=(?!=))(.*)$`).exec(text);
36810
+ if (!m)
36811
+ continue;
36812
+ if (m[1] || new RegExp(`(?:^|[^\\w.$])${v}\\b`).test(m[2])) {
36813
+ const arr = selfReassign.get(d.variable) ?? [];
36814
+ arr.push(line);
36815
+ selfReassign.set(d.variable, arr);
36816
+ }
36817
+ }
36818
+ }
36819
+ const reassignedAt = (v, line) => (selfReassign.get(v) ?? []).includes(line);
36820
+ const defAt = (v, line) => {
36821
+ const own = (graph.defsByLine.get(line) ?? []).find((d) => d.variable === v);
36822
+ if (own)
36823
+ return own;
36824
+ const use = (graph.usesByLine.get(line) ?? []).find((u) => u.variable === v && u.def_id != null);
36825
+ return use ? graph.defById.get(use.def_id) : undefined;
36826
+ };
36827
+ return (step, sourceLine, sinkLine) => {
36828
+ const r = isFalsePositive(constProp, step.line, step.variable);
36829
+ if (!r.isFalsePositive)
36830
+ return false;
36831
+ if (r.reason !== "variable_not_tainted")
36832
+ return true;
36833
+ const lo = Math.min(sourceLine, sinkLine);
36834
+ let d = defAt(step.variable, step.line);
36835
+ let selfHops = 0;
36836
+ for (let guard = 0;d && d.line > lo && reassignedAt(d.variable, d.line) && guard < 32; guard++) {
36837
+ const here = d.id;
36838
+ const prior = (graph.usesByLine.get(d.line) ?? []).find((u) => u.variable === d.variable && u.def_id != null && u.def_id !== here);
36839
+ if (!prior)
36840
+ break;
36841
+ selfHops++;
36842
+ d = graph.defById.get(prior.def_id);
36843
+ }
36844
+ if (!d)
36845
+ return true;
36846
+ if (d.line === sourceLine && sources.some((s) => s.line === sourceLine && !CONTAINER_READ_SOURCES.has(s.type)))
36847
+ return false;
36848
+ if (selfHops > 0 && d.line >= lo)
36849
+ return false;
36850
+ return true;
36851
+ };
36852
+ }
36853
+
36650
36854
  class TaintPropagationPass {
36651
36855
  name = "taint-propagation";
36652
36856
  category = "security";
@@ -36664,12 +36868,12 @@ class TaintPropagationPass {
36664
36868
  return { flows: [] };
36665
36869
  }
36666
36870
  const propagationResult = propagateTaint(graph, sources, sinks, sanitizers);
36871
+ const isVetoed = makeConstPropVeto(constProp, graph, sources, ctx.code);
36667
36872
  const verifiedFlows = propagationResult.flows.filter((flow) => {
36668
36873
  if (constProp.unreachableLines.has(flow.sink.line))
36669
36874
  return false;
36670
36875
  for (const step of flow.path) {
36671
- const fpCheck = isFalsePositive(constProp, step.line, step.variable);
36672
- if (fpCheck.isFalsePositive)
36876
+ if (isVetoed(step, flow.source.line, flow.sink.line))
36673
36877
  return false;
36674
36878
  }
36675
36879
  if (isCorrelatedPredicateFP(constProp, flow))
@@ -36775,7 +36979,7 @@ class TaintPropagationPass {
36775
36979
  continue;
36776
36980
  let isFP = false;
36777
36981
  for (const step of f.path) {
36778
- if (isFalsePositive(constProp, step.line, step.variable).isFalsePositive) {
36982
+ if (isVetoed(step, f.source_line, f.sink_line)) {
36779
36983
  isFP = true;
36780
36984
  break;
36781
36985
  }
@@ -36801,7 +37005,7 @@ class TaintPropagationPass {
36801
37005
  continue;
36802
37006
  let isFP = false;
36803
37007
  for (const step of f.path) {
36804
- if (isFalsePositive(constProp, step.line, step.variable).isFalsePositive) {
37008
+ if (isVetoed(step, f.source_line, f.sink_line)) {
36805
37009
  isFP = true;
36806
37010
  break;
36807
37011
  }
@@ -38205,6 +38409,109 @@ var GO_PURE_PACKAGES = new Set([
38205
38409
  "hex",
38206
38410
  "base64"
38207
38411
  ]);
38412
+ var CSHARP_PURE_STATIC_RECEIVERS = new Set([
38413
+ "string",
38414
+ "String",
38415
+ "Convert",
38416
+ "Encoding",
38417
+ "BitConverter",
38418
+ "Math",
38419
+ "Int16",
38420
+ "Int32",
38421
+ "Int64",
38422
+ "UInt16",
38423
+ "UInt32",
38424
+ "UInt64",
38425
+ "Double",
38426
+ "Single",
38427
+ "Decimal",
38428
+ "Boolean",
38429
+ "Byte",
38430
+ "SByte",
38431
+ "Char",
38432
+ "Guid",
38433
+ "DateTime",
38434
+ "DateTimeOffset",
38435
+ "TimeSpan",
38436
+ "Enum",
38437
+ "Array",
38438
+ "Buffer",
38439
+ "Nullable",
38440
+ "CultureInfo",
38441
+ "StringComparer",
38442
+ "Comparer",
38443
+ "EqualityComparer",
38444
+ "Console",
38445
+ "Debug",
38446
+ "Trace",
38447
+ "Logger",
38448
+ "logger",
38449
+ "_logger",
38450
+ "Log"
38451
+ ]);
38452
+ var CSHARP_NON_ESCAPE_METHODS = new Set([
38453
+ "ToString",
38454
+ "ToArray",
38455
+ "ToList",
38456
+ "ToCharArray",
38457
+ "GetHashCode",
38458
+ "Equals",
38459
+ "CompareTo",
38460
+ "Trim",
38461
+ "TrimStart",
38462
+ "TrimEnd",
38463
+ "Substring",
38464
+ "Split",
38465
+ "Join",
38466
+ "Concat",
38467
+ "PadLeft",
38468
+ "PadRight",
38469
+ "ToLower",
38470
+ "ToUpper",
38471
+ "ToLowerInvariant",
38472
+ "ToUpperInvariant",
38473
+ "StartsWith",
38474
+ "EndsWith",
38475
+ "Contains",
38476
+ "IndexOf",
38477
+ "LastIndexOf",
38478
+ "Insert",
38479
+ "Remove",
38480
+ "Normalize",
38481
+ "Append",
38482
+ "AppendLine",
38483
+ "AppendFormat",
38484
+ "TryParse",
38485
+ "Parse",
38486
+ "LogInformation",
38487
+ "LogWarning",
38488
+ "LogError",
38489
+ "LogDebug",
38490
+ "LogTrace",
38491
+ "LogCritical",
38492
+ "Information",
38493
+ "Warning",
38494
+ "Verbose",
38495
+ "Fatal",
38496
+ "BeginScope",
38497
+ "Add",
38498
+ "AddLast",
38499
+ "AddFirst",
38500
+ "AddRange",
38501
+ "TryAdd",
38502
+ "Insert",
38503
+ "Push",
38504
+ "Enqueue"
38505
+ ]);
38506
+ function isCSharpNonEscapeCall(call) {
38507
+ const name2 = call.method_name;
38508
+ const recv = (call.receiver ?? "").split(".").pop() ?? "";
38509
+ if (CSHARP_PURE_STATIC_RECEIVERS.has(recv))
38510
+ return true;
38511
+ if (CSHARP_NON_ESCAPE_METHODS.has(name2))
38512
+ return true;
38513
+ return false;
38514
+ }
38208
38515
  function isGoNonEscapeCall(call) {
38209
38516
  const name2 = call.method_name;
38210
38517
  const recv = (call.receiver ?? "").split(".").pop() ?? "";
@@ -38452,6 +38759,9 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
38452
38759
  if (graph.ir.meta.language === "go" && isGoNonEscapeCall(call)) {
38453
38760
  continue;
38454
38761
  }
38762
+ if (graph.ir.meta.language === "csharp" && isCSharpNonEscapeCall(call)) {
38763
+ continue;
38764
+ }
38455
38765
  const bashSafeBuiltins = new Set([
38456
38766
  "echo",
38457
38767
  "printf",
@@ -40086,6 +40396,8 @@ class VariableShadowingPass {
40086
40396
  const outerEntry = declLocals[0];
40087
40397
  for (let i2 = 1;i2 < numDeclLocals; i2++) {
40088
40398
  const inner = declLocals[i2];
40399
+ if (inner.def.line <= outerEntry.def.line)
40400
+ continue;
40089
40401
  if (!isInNestedScope(codeLines, outerEntry.def.line, inner.def.line))
40090
40402
  continue;
40091
40403
  const key = `${variable}-${inner.def.line}`;
@@ -50357,7 +50669,7 @@ var colors = {
50357
50669
  };
50358
50670
 
50359
50671
  // src/version.ts
50360
- var version = "4.9.27";
50672
+ var version = "4.9.28";
50361
50673
 
50362
50674
  // src/formatters.ts
50363
50675
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -50909,8 +51221,8 @@ function generateSarifResults(results, crossFileData) {
50909
51221
 
50910
51222
  // src/build-info.ts
50911
51223
  var buildInfo = {
50912
- gitSha: "39d07d9",
50913
- builtAt: "2026-09-29T23:22:05.194Z"
51224
+ gitSha: "7bfe5de",
51225
+ builtAt: "2026-10-01T00:24:15.337Z"
50914
51226
  };
50915
51227
 
50916
51228
  // 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.28",
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.28"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.2",