cognium-dev 3.167.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 +287 -96
  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
  }
@@ -13624,6 +13697,66 @@ function formatCallCode(call) {
13624
13697
  }
13625
13698
  return `${call.method_name}(${args2})`;
13626
13699
  }
13700
+ // ../circle-ir/dist/analysis/non-executable-lines.js
13701
+ function isNonExecutableSourceLine(sourceCode, line, language) {
13702
+ if (!sourceCode || line < 1)
13703
+ return false;
13704
+ const lines = sourceCode.split(`
13705
+ `);
13706
+ if (line > lines.length)
13707
+ return false;
13708
+ const raw = lines[line - 1] ?? "";
13709
+ const trimmed = raw.trim();
13710
+ if (trimmed === "")
13711
+ return true;
13712
+ const lang = language.toLowerCase();
13713
+ if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*/") || trimmed.startsWith("*") || trimmed === "/**") {
13714
+ return true;
13715
+ }
13716
+ switch (lang) {
13717
+ case "java":
13718
+ case "javascript":
13719
+ case "typescript":
13720
+ case "go":
13721
+ case "rust":
13722
+ return isNonExecutableCurly(trimmed);
13723
+ case "python":
13724
+ return isNonExecutablePython(trimmed);
13725
+ default:
13726
+ return false;
13727
+ }
13728
+ }
13729
+ function isNonExecutableCurly(trimmed) {
13730
+ if (/^(import|package|use|from)\s/.test(trimmed))
13731
+ return true;
13732
+ if (/^@[A-Za-z_][\w.]*(?:\s*\([^)]*\))?\s*$/.test(trimmed))
13733
+ return true;
13734
+ if (/^(?:(?:public|private|protected|internal)\s+)?(?:static\s+)?(?:final\s+|readonly\s+|const\s+)(?:[A-Za-z_][\w<>.\[\]]*\s+)?[A-Za-z_]\w*(?:\s*:\s*[^=]+)?\s*=\s*(?:["'`][^"'`]*["'`]|-?\d+(?:\.\d+)?)\s*;?\s*$/.test(trimmed)) {
13735
+ return true;
13736
+ }
13737
+ if (/^(?:const|let|var|static)\s+[A-Za-z_]\w*(?:\s*:\s*[^=]+)?\s*=\s*(?:["'`][^"'`]*["'`]|-?\d+(?:\.\d+)?)\s*;?\s*$/.test(trimmed)) {
13738
+ return true;
13739
+ }
13740
+ if (/^(?:(?:public|private|protected)\s+)?(?:static\s+)?(?:final\s+)[A-Za-z_][\w<>.\[\]]*\s+[A-Za-z_]\w*\s*;\s*$/.test(trimmed)) {
13741
+ return true;
13742
+ }
13743
+ return false;
13744
+ }
13745
+ function isNonExecutablePython(trimmed) {
13746
+ if (/^(import|from)\s/.test(trimmed))
13747
+ return true;
13748
+ if (trimmed.startsWith("#"))
13749
+ return true;
13750
+ if (trimmed === '"""' || trimmed === "'''")
13751
+ return true;
13752
+ if (/^@[A-Za-z_][\w.]*(?:\s*\([^)]*\))?\s*$/.test(trimmed))
13753
+ return true;
13754
+ if (/^[A-Z_][A-Z0-9_]*\s*(?::\s*[^=]+)?\s*=\s*(?:["'][^"']*["']|-?\d+(?:\.\d+)?)\s*$/.test(trimmed)) {
13755
+ return true;
13756
+ }
13757
+ return false;
13758
+ }
13759
+
13627
13760
  // ../circle-ir/dist/analysis/findings.js
13628
13761
  function canSourceReachSink(sourceType, sinkType) {
13629
13762
  const sourceToSinkMapping = {
@@ -15874,6 +16007,20 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15874
16007
  return { visited, lines, hopCapReached };
15875
16008
  }
15876
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
+
15877
16024
  // ../circle-ir/dist/analysis/taint-propagation.js
15878
16025
  function buildSanitizersByLine(sanitizers) {
15879
16026
  const out2 = new Map;
@@ -16095,7 +16242,7 @@ function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
16095
16242
  if (sanitizersAtTarget && sanitizersAtTarget.length > 0) {
16096
16243
  for (const san of sanitizersAtTarget) {
16097
16244
  if (isKnownSinkType) {
16098
- if (san.sanitizes.includes(sinkType)) {
16245
+ if (sanitizerCoversSink(san, sinkType)) {
16099
16246
  return { sanitized: true, sanitizer: san };
16100
16247
  }
16101
16248
  } else if (san.sanitizes.length > 0) {
@@ -16115,7 +16262,7 @@ function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
16115
16262
  continue;
16116
16263
  for (const san of sansAtLine) {
16117
16264
  if (isKnownSinkType) {
16118
- if (san.sanitizes.includes(sinkType)) {
16265
+ if (sanitizerCoversSink(san, sinkType)) {
16119
16266
  return { sanitized: true, sanitizer: san };
16120
16267
  }
16121
16268
  } else if (san.sanitizes.length > 0) {
@@ -23133,7 +23280,7 @@ class CrossFilePass {
23133
23280
  for (const san of sinkIR.taint.sanitizers ?? []) {
23134
23281
  if (san.line < lo || san.line > hi)
23135
23282
  continue;
23136
- if (san.sanitizes.includes(sinkTypeStr)) {
23283
+ if (sanitizerCoversSink(san, sinkTypeStr)) {
23137
23284
  return false;
23138
23285
  }
23139
23286
  }
@@ -23142,7 +23289,7 @@ class CrossFilePass {
23142
23289
  for (const san of sinkIR.taint.sanitizers ?? []) {
23143
23290
  if (san.line !== tp.sink.line)
23144
23291
  continue;
23145
- if (san.sanitizes.includes(sinkTypeStr)) {
23292
+ if (sanitizerCoversSink(san, sinkTypeStr)) {
23146
23293
  return false;
23147
23294
  }
23148
23295
  }
@@ -30930,7 +31077,8 @@ class SinkFilterPass {
30930
31077
  const taintMatcher = ctx.getResult("taint-matcher");
30931
31078
  const constProp = ctx.getResult("constant-propagation");
30932
31079
  const langSources = ctx.getResult("language-sources");
30933
- const sources = [...taintMatcher.sources, ...langSources.additionalSources];
31080
+ const mergedSources = [...taintMatcher.sources, ...langSources.additionalSources];
31081
+ const sources = ctx.code && ctx.language ? mergedSources.filter((s) => !isNonExecutableSourceLine(ctx.code, s.line, ctx.language)) : mergedSources;
30934
31082
  const sinks = [...taintMatcher.sinks];
30935
31083
  for (const s of langSources.additionalSinks) {
30936
31084
  if (!sinks.some((x) => x.line === s.line && x.cwe === s.cwe && x.type === s.type)) {
@@ -32022,7 +32170,7 @@ function filterSanitizedSinks(sinks, sanitizers, calls) {
32022
32170
  if (!lineSanitizers || lineSanitizers.length === 0)
32023
32171
  return true;
32024
32172
  for (const san of lineSanitizers) {
32025
- if (san.sanitizes.includes(sink.type)) {
32173
+ if (sanitizerCoversSink(san, sink.type)) {
32026
32174
  const lineCalls = callsByLine.get(sink.line) ?? [];
32027
32175
  for (const call of lineCalls) {
32028
32176
  for (const arg of call.arguments) {
@@ -32512,15 +32660,25 @@ class TaintPropagationPass {
32512
32660
  confidence: flow.confidence,
32513
32661
  sanitized: flow.sanitized
32514
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
+ };
32515
32675
  const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines, types) ?? [];
32516
32676
  for (const f of arrayFlows) {
32517
- if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type)) {
32518
- flows.push(f);
32519
- }
32677
+ pushIfNew(f);
32520
32678
  }
32521
32679
  const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines, ctx.code, types) ?? [];
32522
32680
  for (const f of collectionFlows) {
32523
- 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)))
32524
32682
  continue;
32525
32683
  const flowForCheck = {
32526
32684
  source: { line: f.source_line },
@@ -32538,17 +32696,15 @@ class TaintPropagationPass {
32538
32696
  }
32539
32697
  if (isFP)
32540
32698
  continue;
32541
- flows.push(f);
32699
+ pushIfNew(f);
32542
32700
  }
32543
32701
  const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code) ?? [];
32544
32702
  for (const f of paramFlows) {
32545
- if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type)) {
32546
- flows.push(f);
32547
- }
32703
+ pushIfNew(f);
32548
32704
  }
32549
32705
  const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, constProp.tainted, ctx.code, ctx.language) ?? [];
32550
32706
  for (const f of exprScanFlows) {
32551
- 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)))
32552
32708
  continue;
32553
32709
  const flowForCheck = {
32554
32710
  source: { line: f.source_line },
@@ -32566,7 +32722,7 @@ class TaintPropagationPass {
32566
32722
  }
32567
32723
  if (isFP)
32568
32724
  continue;
32569
- flows.push(f);
32725
+ pushIfNew(f);
32570
32726
  }
32571
32727
  const sanitizedNames = constProp.sanitizedVars;
32572
32728
  let finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
@@ -32599,7 +32755,7 @@ class TaintPropagationPass {
32599
32755
  if (!sansAtLine)
32600
32756
  continue;
32601
32757
  for (const san of sansAtLine) {
32602
- if (san.sanitizes.includes(f.sink_type)) {
32758
+ if (sanitizerCoversSink(san, f.sink_type)) {
32603
32759
  return false;
32604
32760
  }
32605
32761
  }
@@ -32609,7 +32765,7 @@ class TaintPropagationPass {
32609
32765
  const sansAtSink = sanitizersByLine.get(f.sink_line);
32610
32766
  if (sansAtSink && sansAtSink.length > 0) {
32611
32767
  for (const san of sansAtSink) {
32612
- if (san.sanitizes.includes(f.sink_type)) {
32768
+ if (sanitizerCoversSink(san, f.sink_type)) {
32613
32769
  return false;
32614
32770
  }
32615
32771
  }
@@ -32635,7 +32791,7 @@ class TaintPropagationPass {
32635
32791
  if (!sansAtLine || sansAtLine.length === 0)
32636
32792
  continue;
32637
32793
  for (const san of sansAtLine) {
32638
- if (san.sanitizes.includes(f.sink_type)) {
32794
+ if (sanitizerCoversSink(san, f.sink_type)) {
32639
32795
  return false;
32640
32796
  }
32641
32797
  }
@@ -34178,7 +34334,7 @@ class InterproceduralPass {
34178
34334
  if (!sansAtLine)
34179
34335
  continue;
34180
34336
  for (const san of sansAtLine) {
34181
- if (san.sanitizes.includes(f.sink_type)) {
34337
+ if (sanitizerCoversSink(san, f.sink_type)) {
34182
34338
  sanitizedSinkKeys.add(`${f.sink_line}:${f.sink_type}`);
34183
34339
  return false;
34184
34340
  }
@@ -34190,7 +34346,7 @@ class InterproceduralPass {
34190
34346
  if (!sansAtSink || sansAtSink.length === 0)
34191
34347
  return true;
34192
34348
  for (const san of sansAtSink) {
34193
- if (san.sanitizes.includes(f.sink_type)) {
34349
+ if (sanitizerCoversSink(san, f.sink_type)) {
34194
34350
  return false;
34195
34351
  }
34196
34352
  }
@@ -43428,7 +43584,9 @@ function getNodeTypesForLanguage(language) {
43428
43584
  "jsx_self_closing_element",
43429
43585
  "jsx_opening_element",
43430
43586
  "jsx_attribute",
43431
- "jsx_expression"
43587
+ "jsx_expression",
43588
+ "function",
43589
+ "function_expression"
43432
43590
  ]);
43433
43591
  case "bash":
43434
43592
  return new Set([
@@ -43482,7 +43640,9 @@ function getNodeTypesForLanguage(language) {
43482
43640
  "field_declaration",
43483
43641
  "import_declaration",
43484
43642
  "interface_declaration",
43485
- "enum_declaration"
43643
+ "enum_declaration",
43644
+ "package_declaration",
43645
+ "local_variable_declaration"
43486
43646
  ]);
43487
43647
  }
43488
43648
  }
@@ -43540,10 +43700,21 @@ async function analyze(code, filePath, language, options = {}) {
43540
43700
  }
43541
43701
  }
43542
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();
43543
43711
  const tree = await parse(code, parseGrammar);
43712
+ phaseEnd("parse", tParse);
43544
43713
  try {
43545
43714
  logger.trace("Parsed AST", { rootNodeType: tree.rootNode.type });
43715
+ const tParseStatus = phaseStart();
43546
43716
  const parseStatus = extractParseStatus(tree);
43717
+ phaseEnd("extractParseStatus", tParseStatus);
43547
43718
  if (parseStatus.has_errors) {
43548
43719
  logger.warn("Partial parse — IR may be incomplete", {
43549
43720
  filePath,
@@ -43552,18 +43723,37 @@ async function analyze(code, filePath, language, options = {}) {
43552
43723
  firstErrorLine: parseStatus.error_locations[0]?.line
43553
43724
  });
43554
43725
  }
43726
+ const tCollect = phaseStart();
43555
43727
  const nodeCache = collectAllNodes(tree.rootNode, getNodeTypesForLanguage(language));
43728
+ phaseEnd("collectAllNodes", tCollect);
43729
+ const tMeta = phaseStart();
43556
43730
  const meta = extractMeta(code, tree, filePath, language);
43557
43731
  if (options.projectProfile !== undefined) {
43558
43732
  meta.projectProfile = makeProfileResolver2(options.projectProfile)(filePath);
43559
43733
  }
43734
+ phaseEnd("extractMeta", tMeta);
43735
+ const tTypes = phaseStart();
43560
43736
  const types = extractTypes(tree, nodeCache, language);
43737
+ phaseEnd("extractTypes", tTypes);
43738
+ const tCalls = phaseStart();
43561
43739
  const calls = extractCalls(tree, nodeCache, language);
43562
- 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();
43563
43745
  const exports = extractExports(types);
43564
- 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();
43565
43751
  const dfg = buildDFG(tree, nodeCache, language);
43752
+ phaseEnd("buildDFG", tDFG);
43753
+ const tRuntime = phaseStart();
43566
43754
  const runtimeRegistrations = extractRuntimeRegistrations(tree, nodeCache, language, imports);
43755
+ phaseEnd("extractRuntimeRegistrations", tRuntime);
43756
+ const tGraph = phaseStart();
43567
43757
  const graph = new CodeGraph({
43568
43758
  meta,
43569
43759
  types,
@@ -43576,6 +43766,7 @@ async function analyze(code, filePath, language, options = {}) {
43576
43766
  unresolved: [],
43577
43767
  enriched: {}
43578
43768
  });
43769
+ phaseEnd("buildCodeGraph", tGraph);
43579
43770
  const config = options.taintConfig ?? getDefaultConfig();
43580
43771
  const disabledPasses = new Set(options.disabledPasses ?? []);
43581
43772
  const passOpts = options.passOptions ?? {};
@@ -44534,7 +44725,7 @@ var colors = {
44534
44725
  };
44535
44726
 
44536
44727
  // src/version.ts
44537
- var version = "3.167.0";
44728
+ var version = "3.173.0";
44538
44729
 
44539
44730
  // src/formatters.ts
44540
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.167.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.167.0"
69
+ "circle-ir": "^3.173.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",