cognium-dev 3.168.0 → 3.173.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +225 -95
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -6861,33 +6861,33 @@ function detectLanguage2(tree) {
6861
6861
  return "python";
6862
6862
  return jsScore > javaScore ? "javascript" : "java";
6863
6863
  }
6864
- function extractImports(tree, language) {
6864
+ function extractImports(tree, language, cache) {
6865
6865
  const effectiveLanguage = language ?? detectLanguage2(tree);
6866
6866
  const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
6867
6867
  const isPython = effectiveLanguage === "python";
6868
6868
  const isRust = effectiveLanguage === "rust";
6869
6869
  if (effectiveLanguage === "go") {
6870
- return extractGoImports(tree);
6870
+ return extractGoImports(tree, cache);
6871
6871
  }
6872
6872
  if (isRust) {
6873
- return extractRustImports(tree);
6873
+ return extractRustImports(tree, cache);
6874
6874
  }
6875
6875
  if (isPython) {
6876
- return extractPythonImports(tree);
6876
+ return extractPythonImports(tree, cache);
6877
6877
  }
6878
6878
  if (isJavaScript) {
6879
- return extractJavaScriptImports(tree);
6879
+ return extractJavaScriptImports(tree, cache);
6880
6880
  }
6881
- return extractJavaImports(tree);
6881
+ return extractJavaImports(tree, cache);
6882
6882
  }
6883
- function extractJavaScriptImports(tree) {
6883
+ function extractJavaScriptImports(tree, cache) {
6884
6884
  const imports = [];
6885
- const importStatements = findNodes(tree.rootNode, "import_statement");
6885
+ const importStatements = getNodesFromCache(tree.rootNode, "import_statement", cache);
6886
6886
  for (const importStmt of importStatements) {
6887
6887
  const importInfos = extractJSImportInfo(importStmt);
6888
6888
  imports.push(...importInfos);
6889
6889
  }
6890
- const exportStatements = findNodes(tree.rootNode, "export_statement");
6890
+ const exportStatements = getNodesFromCache(tree.rootNode, "export_statement", cache);
6891
6891
  for (const exportStmt of exportStatements) {
6892
6892
  const sourceNode = exportStmt.childForFieldName("source");
6893
6893
  if (!sourceNode)
@@ -6903,13 +6903,13 @@ function extractJavaScriptImports(tree) {
6903
6903
  line_number: exportStmt.startPosition.row + 1
6904
6904
  });
6905
6905
  }
6906
- const requireCalls = findRequireCalls(tree);
6906
+ const requireCalls = findRequireCalls(tree, cache);
6907
6907
  imports.push(...requireCalls);
6908
6908
  return imports;
6909
6909
  }
6910
- function extractJavaImports(tree) {
6910
+ function extractJavaImports(tree, cache) {
6911
6911
  const imports = [];
6912
- const importDecls = findNodes(tree.rootNode, "import_declaration");
6912
+ const importDecls = getNodesFromCache(tree.rootNode, "import_declaration", cache);
6913
6913
  for (const importDecl of importDecls) {
6914
6914
  const importInfo = extractJavaImportInfo(importDecl);
6915
6915
  if (importInfo) {
@@ -7061,9 +7061,9 @@ function extractJSImportInfo(node) {
7061
7061
  }
7062
7062
  return imports;
7063
7063
  }
7064
- function findRequireCalls(tree) {
7064
+ function findRequireCalls(tree, cache) {
7065
7065
  const imports = [];
7066
- const callExpressions = findNodes(tree.rootNode, "call_expression");
7066
+ const callExpressions = getNodesFromCache(tree.rootNode, "call_expression", cache);
7067
7067
  for (const call of callExpressions) {
7068
7068
  const funcNode = call.childForFieldName("function");
7069
7069
  if (!funcNode || getNodeText(funcNode) !== "require")
@@ -7182,14 +7182,14 @@ function parseImportPath(fullPath, isWildcard) {
7182
7182
  fromPackage: fullPath.substring(0, lastDot)
7183
7183
  };
7184
7184
  }
7185
- function extractPythonImports(tree) {
7185
+ function extractPythonImports(tree, cache) {
7186
7186
  const imports = [];
7187
- const importStatements = findNodes(tree.rootNode, "import_statement");
7187
+ const importStatements = getNodesFromCache(tree.rootNode, "import_statement", cache);
7188
7188
  for (const stmt of importStatements) {
7189
7189
  const importInfos = extractPythonImportStatement(stmt);
7190
7190
  imports.push(...importInfos);
7191
7191
  }
7192
- const importFromStatements = findNodes(tree.rootNode, "import_from_statement");
7192
+ const importFromStatements = getNodesFromCache(tree.rootNode, "import_from_statement", cache);
7193
7193
  for (const stmt of importFromStatements) {
7194
7194
  const importInfos = extractPythonFromImportStatement(stmt);
7195
7195
  imports.push(...importInfos);
@@ -7285,9 +7285,9 @@ function extractPythonFromImportStatement(node) {
7285
7285
  }
7286
7286
  return imports;
7287
7287
  }
7288
- function extractRustImports(tree) {
7288
+ function extractRustImports(tree, cache) {
7289
7289
  const imports = [];
7290
- const useDecls = findNodes(tree.rootNode, "use_declaration");
7290
+ const useDecls = getNodesFromCache(tree.rootNode, "use_declaration", cache);
7291
7291
  for (const useDecl of useDecls) {
7292
7292
  const useImports = extractRustUseDecl(useDecl);
7293
7293
  imports.push(...useImports);
@@ -7433,9 +7433,9 @@ function extractRustScopedUseList(node, lineNumber) {
7433
7433
  }
7434
7434
  return imports;
7435
7435
  }
7436
- function extractGoImports(tree) {
7436
+ function extractGoImports(tree, cache) {
7437
7437
  const imports = [];
7438
- const importDecls = findNodes(tree.rootNode, "import_declaration");
7438
+ const importDecls = getNodesFromCache(tree.rootNode, "import_declaration", cache);
7439
7439
  for (const decl of importDecls) {
7440
7440
  const singleSpec = findGoChildByType(decl, "import_spec");
7441
7441
  if (singleSpec) {
@@ -7576,7 +7576,7 @@ function detectLanguage3(tree) {
7576
7576
  }
7577
7577
  return jsScore > javaScore ? "javascript" : "java";
7578
7578
  }
7579
- function buildCFG(tree, language) {
7579
+ function buildCFG(tree, language, cache) {
7580
7580
  const effectiveLanguage = language ?? detectLanguage3(tree);
7581
7581
  const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
7582
7582
  const allBlocks = [];
@@ -7590,11 +7590,11 @@ function buildCFG(tree, language) {
7590
7590
  }
7591
7591
  if (isJavaScript) {
7592
7592
  const functions = [
7593
- ...findNodes(tree.rootNode, "function_declaration"),
7594
- ...findNodes(tree.rootNode, "arrow_function"),
7595
- ...findNodes(tree.rootNode, "method_definition"),
7596
- ...findNodes(tree.rootNode, "function"),
7597
- ...findNodes(tree.rootNode, "function_expression")
7593
+ ...getNodesFromCache(tree.rootNode, "function_declaration", cache),
7594
+ ...getNodesFromCache(tree.rootNode, "arrow_function", cache),
7595
+ ...getNodesFromCache(tree.rootNode, "method_definition", cache),
7596
+ ...getNodesFromCache(tree.rootNode, "function", cache),
7597
+ ...getNodesFromCache(tree.rootNode, "function_expression", cache)
7598
7598
  ];
7599
7599
  for (const func2 of functions) {
7600
7600
  const body2 = func2.childForFieldName("body");
@@ -7617,8 +7617,8 @@ function buildCFG(tree, language) {
7617
7617
  }
7618
7618
  } else {
7619
7619
  const methods = [
7620
- ...findNodes(tree.rootNode, "method_declaration"),
7621
- ...findNodes(tree.rootNode, "constructor_declaration")
7620
+ ...getNodesFromCache(tree.rootNode, "method_declaration", cache),
7621
+ ...getNodesFromCache(tree.rootNode, "constructor_declaration", cache)
7622
7622
  ];
7623
7623
  for (const method of methods) {
7624
7624
  const body2 = method.childForFieldName("body");
@@ -10507,7 +10507,51 @@ var DEFAULT_SOURCES = [
10507
10507
  { method: "recv", class: "TcpStream", type: "network_input", severity: "high", return_tainted: true },
10508
10508
  { method: "read", class: "TcpStream", type: "network_input", severity: "high", return_tainted: true },
10509
10509
  { method: "read_to_end", class: "TcpStream", type: "network_input", severity: "high", return_tainted: true },
10510
- { method: "read_to_string", class: "TcpStream", type: "network_input", severity: "high", return_tainted: true }
10510
+ { method: "read_to_string", class: "TcpStream", type: "network_input", severity: "high", return_tainted: true },
10511
+ { method_annotation: "Query", type: "http_body", severity: "high", languages: ["javascript", "typescript"] },
10512
+ { method_annotation: "Mutation", type: "http_body", severity: "high", languages: ["javascript", "typescript"] },
10513
+ { method_annotation: "Subscription", type: "http_body", severity: "high", languages: ["javascript", "typescript"] },
10514
+ { method_annotation: "FieldResolver", type: "http_body", severity: "high", languages: ["javascript", "typescript"] },
10515
+ { method_annotation: "ResolveField", type: "http_body", severity: "high", languages: ["javascript", "typescript"] },
10516
+ { annotation: "Arg", type: "http_param", severity: "high", param_tainted: true, languages: ["javascript", "typescript"] },
10517
+ { annotation: "Args", type: "http_param", severity: "high", param_tainted: true, languages: ["javascript", "typescript"] },
10518
+ { method_annotation: "strawberry.field", type: "http_body", severity: "high", languages: ["python"] },
10519
+ { method_annotation: "strawberry.mutation", type: "http_body", severity: "high", languages: ["python"] },
10520
+ { method_annotation: "strawberry.subscription", type: "http_body", severity: "high", languages: ["python"] },
10521
+ { method_annotation: "DgsQuery", type: "http_body", severity: "high", languages: ["java"] },
10522
+ { method_annotation: "DgsMutation", type: "http_body", severity: "high", languages: ["java"] },
10523
+ { method_annotation: "DgsSubscription", type: "http_body", severity: "high", languages: ["java"] },
10524
+ { method_annotation: "DgsData", type: "http_body", severity: "high", languages: ["java"] },
10525
+ { method_annotation: "GraphQLQuery", type: "http_body", severity: "high", languages: ["java"] },
10526
+ { method_annotation: "GraphQLMutation", type: "http_body", severity: "high", languages: ["java"] },
10527
+ { annotation: "InputArgument", type: "http_param", severity: "high", param_tainted: true, languages: ["java"] },
10528
+ { annotation: "GraphQLArgument", type: "http_param", severity: "high", param_tainted: true, languages: ["java"] },
10529
+ { method: "invocation_metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["python"] },
10530
+ { method: "get", class: "Metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
10531
+ { method: "getMap", class: "Metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
10532
+ { method: "FromIncomingContext", class: "metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["go"] },
10533
+ { method: "get", class: "Metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["java"] },
10534
+ { method: "get", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
10535
+ { method: "hget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
10536
+ { method: "mget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
10537
+ { method: "lrange", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
10538
+ { method: "get", class: "cache", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
10539
+ { method: "get_many", class: "cache", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
10540
+ { method: "get", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
10541
+ { method: "hget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
10542
+ { method: "mget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
10543
+ { method: "get", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
10544
+ { method: "hget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
10545
+ { method: "mget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
10546
+ { method: "decode", class: "jwt", type: "http_header", severity: "high", return_tainted: true, languages: ["python"] },
10547
+ { method: "get_unverified_claims", class: "jwt", type: "http_header", severity: "high", return_tainted: true, languages: ["python"] },
10548
+ { method: "get_unverified_header", class: "jwt", type: "http_header", severity: "high", return_tainted: true, languages: ["python"] },
10549
+ { method: "decode", class: "jwt", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
10550
+ { method: "decode", class: "jsonwebtoken", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
10551
+ { method: "decodeJwt", class: "jose", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
10552
+ { method: "decodeProtectedHeader", class: "jose", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
10553
+ { method: "decode", class: "JWT", type: "http_header", severity: "high", return_tainted: true, languages: ["java"] },
10554
+ { method: "ParseUnverified", class: "jwt", type: "http_header", severity: "high", return_tainted: true, languages: ["go"] }
10511
10555
  ];
10512
10556
  var DEFAULT_SINKS = [
10513
10557
  { method: "executeQuery", class: "Statement", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
@@ -11995,6 +12039,9 @@ function findSources(calls, types, patterns, sourceLines, language) {
11995
12039
  for (const param of method.parameters) {
11996
12040
  for (const pattern of patterns) {
11997
12041
  if (pattern.annotation && pattern.param_tainted) {
12042
+ if (pattern.languages && pattern.languages.length > 0 && language !== undefined && !pattern.languages.includes(language)) {
12043
+ continue;
12044
+ }
11998
12045
  if (matchesAnnotation(param.annotations, pattern.annotation)) {
11999
12046
  const paramLine = param.line ?? method.start_line;
12000
12047
  sources.push({
@@ -12016,6 +12063,9 @@ function findSources(calls, types, patterns, sourceLines, language) {
12016
12063
  for (const pattern of patterns) {
12017
12064
  if (!pattern.method_annotation)
12018
12065
  continue;
12066
+ if (pattern.languages && pattern.languages.length > 0 && language !== undefined && !pattern.languages.includes(language)) {
12067
+ continue;
12068
+ }
12019
12069
  if (!matchesAnnotation(method.annotations, pattern.method_annotation))
12020
12070
  continue;
12021
12071
  for (const param of method.parameters) {
@@ -12269,7 +12319,7 @@ function isParameterizedQueryCall(call, pattern) {
12269
12319
  const secondArg = call.arguments.find((a) => a.position === 1);
12270
12320
  if (secondArg?.expression) {
12271
12321
  const expr = secondArg.expression.trim();
12272
- if (expr.startsWith("[")) {
12322
+ if (expr.startsWith("[") && !/^\[\s*\]$/.test(expr)) {
12273
12323
  return true;
12274
12324
  }
12275
12325
  }
@@ -12757,52 +12807,75 @@ function matchesSourcePattern(call, pattern) {
12757
12807
  }
12758
12808
  return false;
12759
12809
  }
12810
+ var JS_SOURCE_PATTERN_CACHE = new WeakMap;
12811
+ var JS_FALLBACK_COMPILED = (() => {
12812
+ const bases = [
12813
+ { base: "req\\.params", sourceType: "http_param" },
12814
+ { base: "req\\.query", sourceType: "http_param" },
12815
+ { base: "req\\.body", sourceType: "http_body" },
12816
+ { base: "req\\.headers", sourceType: "http_header" },
12817
+ { base: "req\\.cookies", sourceType: "http_cookie" },
12818
+ { base: "req\\.url", sourceType: "http_path" },
12819
+ { base: "req\\.path", sourceType: "http_path" },
12820
+ { base: "req\\.originalUrl", sourceType: "http_path" },
12821
+ { base: "req\\.file", sourceType: "file_input" },
12822
+ { base: "req\\.files", sourceType: "file_input" },
12823
+ { base: "request\\.params", sourceType: "http_param" },
12824
+ { base: "request\\.query", sourceType: "http_param" },
12825
+ { base: "request\\.body", sourceType: "http_body" },
12826
+ { base: "request\\.headers", sourceType: "http_header" },
12827
+ { base: "process\\.env", sourceType: "env_input" },
12828
+ { base: "process\\.argv", sourceType: "io_input" },
12829
+ { base: "ctx\\.query", sourceType: "http_param" },
12830
+ { base: "ctx\\.params", sourceType: "http_param" },
12831
+ { base: "ctx\\.request", sourceType: "http_body" }
12832
+ ];
12833
+ const exact = [];
12834
+ const contained = [];
12835
+ for (const { base, sourceType } of bases) {
12836
+ exact.push({ pattern: new RegExp(`^${base}\\b`), sourceType });
12837
+ contained.push({ pattern: new RegExp(`\\b${base}\\b`), sourceType });
12838
+ }
12839
+ return { exact, contained };
12840
+ })();
12841
+ function compileJsSourcePatterns(sourcePatterns) {
12842
+ const cached = JS_SOURCE_PATTERN_CACHE.get(sourcePatterns);
12843
+ if (cached)
12844
+ return cached;
12845
+ const exact = [];
12846
+ const contained = [];
12847
+ for (const sp of sourcePatterns) {
12848
+ if (sp.property && sp.object && sp.property_tainted) {
12849
+ exact.push({
12850
+ pattern: new RegExp(`^${sp.object}\\.${sp.property}\\b`),
12851
+ sourceType: sp.type
12852
+ });
12853
+ contained.push({
12854
+ pattern: new RegExp(`\\b${sp.object}\\.${sp.property}\\b`),
12855
+ sourceType: sp.type
12856
+ });
12857
+ }
12858
+ }
12859
+ const compiled = { exact, contained };
12860
+ JS_SOURCE_PATTERN_CACHE.set(sourcePatterns, compiled);
12861
+ return compiled;
12862
+ }
12760
12863
  function isJavaScriptTaintedArgument(argExpression, sourcePatterns) {
12761
- const exactPatterns = [];
12762
- const containedPatterns = [];
12763
- if (sourcePatterns) {
12764
- for (const sp of sourcePatterns) {
12765
- if (sp.property && sp.object && sp.property_tainted) {
12766
- const exactRegex = new RegExp(`^${sp.object}\\.${sp.property}\\b`);
12767
- exactPatterns.push({ pattern: exactRegex, sourceType: sp.type });
12768
- const containedRegex = new RegExp(`\\b${sp.object}\\.${sp.property}\\b`);
12769
- containedPatterns.push({ pattern: containedRegex, sourceType: sp.type });
12770
- }
12771
- }
12772
- }
12773
- if (exactPatterns.length === 0) {
12774
- const basePatterns = [
12775
- { base: "req\\.params", sourceType: "http_param" },
12776
- { base: "req\\.query", sourceType: "http_param" },
12777
- { base: "req\\.body", sourceType: "http_body" },
12778
- { base: "req\\.headers", sourceType: "http_header" },
12779
- { base: "req\\.cookies", sourceType: "http_cookie" },
12780
- { base: "req\\.url", sourceType: "http_path" },
12781
- { base: "req\\.path", sourceType: "http_path" },
12782
- { base: "req\\.originalUrl", sourceType: "http_path" },
12783
- { base: "req\\.file", sourceType: "file_input" },
12784
- { base: "req\\.files", sourceType: "file_input" },
12785
- { base: "request\\.params", sourceType: "http_param" },
12786
- { base: "request\\.query", sourceType: "http_param" },
12787
- { base: "request\\.body", sourceType: "http_body" },
12788
- { base: "request\\.headers", sourceType: "http_header" },
12789
- { base: "process\\.env", sourceType: "env_input" },
12790
- { base: "process\\.argv", sourceType: "io_input" },
12791
- { base: "ctx\\.query", sourceType: "http_param" },
12792
- { base: "ctx\\.params", sourceType: "http_param" },
12793
- { base: "ctx\\.request", sourceType: "http_body" }
12794
- ];
12795
- for (const { base, sourceType } of basePatterns) {
12796
- exactPatterns.push({ pattern: new RegExp(`^${base}\\b`), sourceType });
12797
- containedPatterns.push({ pattern: new RegExp(`\\b${base}\\b`), sourceType });
12864
+ let compiled;
12865
+ if (sourcePatterns && sourcePatterns.length > 0) {
12866
+ compiled = compileJsSourcePatterns(sourcePatterns);
12867
+ if (compiled.exact.length === 0) {
12868
+ compiled = JS_FALLBACK_COMPILED;
12798
12869
  }
12870
+ } else {
12871
+ compiled = JS_FALLBACK_COMPILED;
12799
12872
  }
12800
- for (const { pattern, sourceType } of exactPatterns) {
12873
+ for (const { pattern, sourceType } of compiled.exact) {
12801
12874
  if (pattern.test(argExpression)) {
12802
12875
  return { isTainted: true, sourceType };
12803
12876
  }
12804
12877
  }
12805
- for (const { pattern, sourceType } of containedPatterns) {
12878
+ for (const { pattern, sourceType } of compiled.contained) {
12806
12879
  if (pattern.test(argExpression)) {
12807
12880
  return { isTainted: true, sourceType };
12808
12881
  }
@@ -15934,6 +16007,20 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15934
16007
  return { visited, lines, hopCapReached };
15935
16008
  }
15936
16009
 
16010
+ // ../circle-ir/dist/analysis/sanitizer-index.js
16011
+ var SANITIZER_SET_CACHE = new WeakMap;
16012
+ function getSanitizesSet(san) {
16013
+ let s = SANITIZER_SET_CACHE.get(san);
16014
+ if (!s) {
16015
+ s = new Set(san.sanitizes);
16016
+ SANITIZER_SET_CACHE.set(san, s);
16017
+ }
16018
+ return s;
16019
+ }
16020
+ function sanitizerCoversSink(san, sinkType) {
16021
+ return getSanitizesSet(san).has(sinkType);
16022
+ }
16023
+
15937
16024
  // ../circle-ir/dist/analysis/taint-propagation.js
15938
16025
  function buildSanitizersByLine(sanitizers) {
15939
16026
  const out2 = new Map;
@@ -16155,7 +16242,7 @@ function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
16155
16242
  if (sanitizersAtTarget && sanitizersAtTarget.length > 0) {
16156
16243
  for (const san of sanitizersAtTarget) {
16157
16244
  if (isKnownSinkType) {
16158
- if (san.sanitizes.includes(sinkType)) {
16245
+ if (sanitizerCoversSink(san, sinkType)) {
16159
16246
  return { sanitized: true, sanitizer: san };
16160
16247
  }
16161
16248
  } else if (san.sanitizes.length > 0) {
@@ -16175,7 +16262,7 @@ function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
16175
16262
  continue;
16176
16263
  for (const san of sansAtLine) {
16177
16264
  if (isKnownSinkType) {
16178
- if (san.sanitizes.includes(sinkType)) {
16265
+ if (sanitizerCoversSink(san, sinkType)) {
16179
16266
  return { sanitized: true, sanitizer: san };
16180
16267
  }
16181
16268
  } else if (san.sanitizes.length > 0) {
@@ -23193,7 +23280,7 @@ class CrossFilePass {
23193
23280
  for (const san of sinkIR.taint.sanitizers ?? []) {
23194
23281
  if (san.line < lo || san.line > hi)
23195
23282
  continue;
23196
- if (san.sanitizes.includes(sinkTypeStr)) {
23283
+ if (sanitizerCoversSink(san, sinkTypeStr)) {
23197
23284
  return false;
23198
23285
  }
23199
23286
  }
@@ -23202,7 +23289,7 @@ class CrossFilePass {
23202
23289
  for (const san of sinkIR.taint.sanitizers ?? []) {
23203
23290
  if (san.line !== tp.sink.line)
23204
23291
  continue;
23205
- if (san.sanitizes.includes(sinkTypeStr)) {
23292
+ if (sanitizerCoversSink(san, sinkTypeStr)) {
23206
23293
  return false;
23207
23294
  }
23208
23295
  }
@@ -32083,7 +32170,7 @@ function filterSanitizedSinks(sinks, sanitizers, calls) {
32083
32170
  if (!lineSanitizers || lineSanitizers.length === 0)
32084
32171
  return true;
32085
32172
  for (const san of lineSanitizers) {
32086
- if (san.sanitizes.includes(sink.type)) {
32173
+ if (sanitizerCoversSink(san, sink.type)) {
32087
32174
  const lineCalls = callsByLine.get(sink.line) ?? [];
32088
32175
  for (const call of lineCalls) {
32089
32176
  for (const arg of call.arguments) {
@@ -32573,15 +32660,25 @@ class TaintPropagationPass {
32573
32660
  confidence: flow.confidence,
32574
32661
  sanitized: flow.sanitized
32575
32662
  }));
32663
+ const flowKey = (x) => `${x.source_line}|${x.sink_line}|${x.sink_type}`;
32664
+ const flowKeys = new Set;
32665
+ for (const x of flows)
32666
+ flowKeys.add(flowKey(x));
32667
+ const pushIfNew = (f) => {
32668
+ const k = flowKey(f);
32669
+ if (flowKeys.has(k))
32670
+ return false;
32671
+ flowKeys.add(k);
32672
+ flows.push(f);
32673
+ return true;
32674
+ };
32576
32675
  const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines, types) ?? [];
32577
32676
  for (const f of arrayFlows) {
32578
- if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type)) {
32579
- flows.push(f);
32580
- }
32677
+ pushIfNew(f);
32581
32678
  }
32582
32679
  const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines, ctx.code, types) ?? [];
32583
32680
  for (const f of collectionFlows) {
32584
- if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type))
32681
+ if (flowKeys.has(flowKey(f)))
32585
32682
  continue;
32586
32683
  const flowForCheck = {
32587
32684
  source: { line: f.source_line },
@@ -32599,17 +32696,15 @@ class TaintPropagationPass {
32599
32696
  }
32600
32697
  if (isFP)
32601
32698
  continue;
32602
- flows.push(f);
32699
+ pushIfNew(f);
32603
32700
  }
32604
32701
  const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code) ?? [];
32605
32702
  for (const f of paramFlows) {
32606
- if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type)) {
32607
- flows.push(f);
32608
- }
32703
+ pushIfNew(f);
32609
32704
  }
32610
32705
  const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, constProp.tainted, ctx.code, ctx.language) ?? [];
32611
32706
  for (const f of exprScanFlows) {
32612
- if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type))
32707
+ if (flowKeys.has(flowKey(f)))
32613
32708
  continue;
32614
32709
  const flowForCheck = {
32615
32710
  source: { line: f.source_line },
@@ -32627,7 +32722,7 @@ class TaintPropagationPass {
32627
32722
  }
32628
32723
  if (isFP)
32629
32724
  continue;
32630
- flows.push(f);
32725
+ pushIfNew(f);
32631
32726
  }
32632
32727
  const sanitizedNames = constProp.sanitizedVars;
32633
32728
  let finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
@@ -32660,7 +32755,7 @@ class TaintPropagationPass {
32660
32755
  if (!sansAtLine)
32661
32756
  continue;
32662
32757
  for (const san of sansAtLine) {
32663
- if (san.sanitizes.includes(f.sink_type)) {
32758
+ if (sanitizerCoversSink(san, f.sink_type)) {
32664
32759
  return false;
32665
32760
  }
32666
32761
  }
@@ -32670,7 +32765,7 @@ class TaintPropagationPass {
32670
32765
  const sansAtSink = sanitizersByLine.get(f.sink_line);
32671
32766
  if (sansAtSink && sansAtSink.length > 0) {
32672
32767
  for (const san of sansAtSink) {
32673
- if (san.sanitizes.includes(f.sink_type)) {
32768
+ if (sanitizerCoversSink(san, f.sink_type)) {
32674
32769
  return false;
32675
32770
  }
32676
32771
  }
@@ -32696,7 +32791,7 @@ class TaintPropagationPass {
32696
32791
  if (!sansAtLine || sansAtLine.length === 0)
32697
32792
  continue;
32698
32793
  for (const san of sansAtLine) {
32699
- if (san.sanitizes.includes(f.sink_type)) {
32794
+ if (sanitizerCoversSink(san, f.sink_type)) {
32700
32795
  return false;
32701
32796
  }
32702
32797
  }
@@ -34239,7 +34334,7 @@ class InterproceduralPass {
34239
34334
  if (!sansAtLine)
34240
34335
  continue;
34241
34336
  for (const san of sansAtLine) {
34242
- if (san.sanitizes.includes(f.sink_type)) {
34337
+ if (sanitizerCoversSink(san, f.sink_type)) {
34243
34338
  sanitizedSinkKeys.add(`${f.sink_line}:${f.sink_type}`);
34244
34339
  return false;
34245
34340
  }
@@ -34251,7 +34346,7 @@ class InterproceduralPass {
34251
34346
  if (!sansAtSink || sansAtSink.length === 0)
34252
34347
  return true;
34253
34348
  for (const san of sansAtSink) {
34254
- if (san.sanitizes.includes(f.sink_type)) {
34349
+ if (sanitizerCoversSink(san, f.sink_type)) {
34255
34350
  return false;
34256
34351
  }
34257
34352
  }
@@ -43489,7 +43584,9 @@ function getNodeTypesForLanguage(language) {
43489
43584
  "jsx_self_closing_element",
43490
43585
  "jsx_opening_element",
43491
43586
  "jsx_attribute",
43492
- "jsx_expression"
43587
+ "jsx_expression",
43588
+ "function",
43589
+ "function_expression"
43493
43590
  ]);
43494
43591
  case "bash":
43495
43592
  return new Set([
@@ -43543,7 +43640,9 @@ function getNodeTypesForLanguage(language) {
43543
43640
  "field_declaration",
43544
43641
  "import_declaration",
43545
43642
  "interface_declaration",
43546
- "enum_declaration"
43643
+ "enum_declaration",
43644
+ "package_declaration",
43645
+ "local_variable_declaration"
43547
43646
  ]);
43548
43647
  }
43549
43648
  }
@@ -43601,10 +43700,21 @@ async function analyze(code, filePath, language, options = {}) {
43601
43700
  }
43602
43701
  }
43603
43702
  logger.debug("Analyzing file", { filePath, language, parseGrammar, codeLength: code.length });
43703
+ const phaseTimingEnabled = globalThis.__circleIrPassTiming === true;
43704
+ const phaseStart = () => phaseTimingEnabled ? Date.now() : 0;
43705
+ const phaseEnd = (label, t0) => {
43706
+ if (!phaseTimingEnabled)
43707
+ return;
43708
+ console.error(`[phase-timing] ${label} ${Date.now() - t0}ms file=${filePath}`);
43709
+ };
43710
+ const tParse = phaseStart();
43604
43711
  const tree = await parse(code, parseGrammar);
43712
+ phaseEnd("parse", tParse);
43605
43713
  try {
43606
43714
  logger.trace("Parsed AST", { rootNodeType: tree.rootNode.type });
43715
+ const tParseStatus = phaseStart();
43607
43716
  const parseStatus = extractParseStatus(tree);
43717
+ phaseEnd("extractParseStatus", tParseStatus);
43608
43718
  if (parseStatus.has_errors) {
43609
43719
  logger.warn("Partial parse — IR may be incomplete", {
43610
43720
  filePath,
@@ -43613,18 +43723,37 @@ async function analyze(code, filePath, language, options = {}) {
43613
43723
  firstErrorLine: parseStatus.error_locations[0]?.line
43614
43724
  });
43615
43725
  }
43726
+ const tCollect = phaseStart();
43616
43727
  const nodeCache = collectAllNodes(tree.rootNode, getNodeTypesForLanguage(language));
43728
+ phaseEnd("collectAllNodes", tCollect);
43729
+ const tMeta = phaseStart();
43617
43730
  const meta = extractMeta(code, tree, filePath, language);
43618
43731
  if (options.projectProfile !== undefined) {
43619
43732
  meta.projectProfile = makeProfileResolver2(options.projectProfile)(filePath);
43620
43733
  }
43734
+ phaseEnd("extractMeta", tMeta);
43735
+ const tTypes = phaseStart();
43621
43736
  const types = extractTypes(tree, nodeCache, language);
43737
+ phaseEnd("extractTypes", tTypes);
43738
+ const tCalls = phaseStart();
43622
43739
  const calls = extractCalls(tree, nodeCache, language);
43623
- const imports = extractImports(tree, language);
43740
+ phaseEnd("extractCalls", tCalls);
43741
+ const tImports = phaseStart();
43742
+ const imports = extractImports(tree, language, nodeCache);
43743
+ phaseEnd("extractImports", tImports);
43744
+ const tExports = phaseStart();
43624
43745
  const exports = extractExports(types);
43625
- const cfg = buildCFG(tree, language);
43746
+ phaseEnd("extractExports", tExports);
43747
+ const tCFG = phaseStart();
43748
+ const cfg = buildCFG(tree, language, nodeCache);
43749
+ phaseEnd("buildCFG", tCFG);
43750
+ const tDFG = phaseStart();
43626
43751
  const dfg = buildDFG(tree, nodeCache, language);
43752
+ phaseEnd("buildDFG", tDFG);
43753
+ const tRuntime = phaseStart();
43627
43754
  const runtimeRegistrations = extractRuntimeRegistrations(tree, nodeCache, language, imports);
43755
+ phaseEnd("extractRuntimeRegistrations", tRuntime);
43756
+ const tGraph = phaseStart();
43628
43757
  const graph = new CodeGraph({
43629
43758
  meta,
43630
43759
  types,
@@ -43637,6 +43766,7 @@ async function analyze(code, filePath, language, options = {}) {
43637
43766
  unresolved: [],
43638
43767
  enriched: {}
43639
43768
  });
43769
+ phaseEnd("buildCodeGraph", tGraph);
43640
43770
  const config = options.taintConfig ?? getDefaultConfig();
43641
43771
  const disabledPasses = new Set(options.disabledPasses ?? []);
43642
43772
  const passOpts = options.passOptions ?? {};
@@ -44595,7 +44725,7 @@ var colors = {
44595
44725
  };
44596
44726
 
44597
44727
  // src/version.ts
44598
- var version = "3.168.0";
44728
+ var version = "3.173.0";
44599
44729
 
44600
44730
  // src/formatters.ts
44601
44731
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.168.0",
3
+ "version": "3.173.0",
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",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@cognium/project-profile-detect": "^1.1.0",
69
- "circle-ir": "^3.168.0"
69
+ "circle-ir": "^3.173.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",