cognium-dev 3.57.0 → 3.59.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 +586 -45
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -10233,10 +10233,10 @@ var DEFAULT_SOURCES = [
10233
10233
  { method: "cookie", class: "HttpRequest", type: "http_cookie", severity: "high", return_tainted: true },
10234
10234
  { method: "param", class: "Request", type: "http_param", severity: "high", return_tainted: true },
10235
10235
  { method: "cookies", class: "Request", type: "http_cookie", severity: "high", return_tainted: true },
10236
- { method: "Json", type: "http_body", severity: "high", return_tainted: true },
10237
- { method: "Query", type: "http_param", severity: "high", return_tainted: true },
10238
- { method: "Path", type: "http_path", severity: "high", return_tainted: true },
10239
- { method: "Form", type: "http_param", severity: "high", return_tainted: true },
10236
+ { method: "Json", type: "http_body", severity: "high", return_tainted: true, languages: ["rust"] },
10237
+ { method: "Query", type: "http_param", severity: "high", return_tainted: true, languages: ["rust"] },
10238
+ { method: "Path", type: "http_path", severity: "high", return_tainted: true, languages: ["rust"] },
10239
+ { method: "Form", type: "http_param", severity: "high", return_tainted: true, languages: ["rust"] },
10240
10240
  { method: "var", class: "env", type: "env_input", severity: "medium", return_tainted: true },
10241
10241
  { method: "var_os", class: "env", type: "env_input", severity: "medium", return_tainted: true },
10242
10242
  { method: "args", class: "env", type: "env_input", severity: "medium", return_tainted: true },
@@ -10389,10 +10389,10 @@ var DEFAULT_SINKS = [
10389
10389
  { method: "UrlResource", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10390
10390
  { method: "PathResource", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10391
10391
  { method: "forFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10392
- { method: "resolve", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10393
- { method: "resolve", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10394
- { method: "resolveSibling", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10395
- { method: "relativize", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
10392
+ { method: "resolve", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
10393
+ { method: "resolve", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
10394
+ { method: "resolveSibling", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["java"] },
10395
+ { method: "relativize", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0], languages: ["java"] },
10396
10396
  { method: "staticFiles", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10397
10397
  { method: "setRoot", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
10398
10398
  { method: "setWebRoot", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -11208,6 +11208,11 @@ var DEFAULT_SANITIZERS = [
11208
11208
  { method: "toRealPath", class: "Path", removes: ["path_traversal"] },
11209
11209
  { method: "file_name", removes: ["path_traversal"] },
11210
11210
  { method: "canonicalize", removes: ["path_traversal"] },
11211
+ { method: "Base", class: "filepath", removes: ["path_traversal"] },
11212
+ { method: "Base", class: "path", removes: ["path_traversal"] },
11213
+ { method: "Clean", class: "filepath", removes: ["path_traversal"] },
11214
+ { method: "Clean", class: "path", removes: ["path_traversal"] },
11215
+ { method: "EvalSymlinks", class: "filepath", removes: ["path_traversal"] },
11211
11216
  { method: "replace", removes: ["log_injection"] },
11212
11217
  { method: "encodeForLDAP", removes: ["ldap_injection"] },
11213
11218
  { method: "encodeForDN", removes: ["ldap_injection"] },
@@ -11268,6 +11273,7 @@ var DEFAULT_SANITIZERS = [
11268
11273
  { method: "abspath", class: "os.path", removes: ["path_traversal"] },
11269
11274
  { method: "realpath", class: "path", removes: ["path_traversal"] },
11270
11275
  { method: "abspath", class: "path", removes: ["path_traversal"] },
11276
+ { method: "resolve", class: "Path", removes: ["path_traversal"] },
11271
11277
  { method: "int", removes: ["sql_injection", "command_injection", "xss"] },
11272
11278
  { method: "float", removes: ["sql_injection", "command_injection"] },
11273
11279
  { method: "query!", removes: ["sql_injection"] },
@@ -11287,7 +11293,25 @@ var DEFAULT_SANITIZERS = [
11287
11293
  { method: "encode_text", class: "html_escape", removes: ["xss"] },
11288
11294
  { method: "encode_attribute", class: "html_escape", removes: ["xss"] },
11289
11295
  { method: "escape_html", removes: ["xss"] },
11290
- { method: "parse", removes: ["sql_injection", "command_injection", "xss"] }
11296
+ { method: "parse", removes: ["sql_injection", "command_injection", "xss"] },
11297
+ { method: "parseInt", class: "Integer", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11298
+ { method: "parseLong", class: "Long", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11299
+ { method: "parseFloat", class: "Float", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11300
+ { method: "parseDouble", class: "Double", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11301
+ { method: "parseShort", class: "Short", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11302
+ { method: "parseByte", class: "Byte", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11303
+ { method: "fromString", class: "UUID", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11304
+ { method: "BigInt", removes: ["sql_injection", "nosql_injection", "command_injection", "path_traversal", "code_injection"] },
11305
+ { method: "Atoi", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11306
+ { method: "ParseInt", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11307
+ { method: "ParseFloat", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11308
+ { method: "ParseUint", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11309
+ { method: "ParseBool", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11310
+ { method: "Parse", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11311
+ { method: "MustParse", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11312
+ { method: "bool", removes: ["sql_injection", "command_injection", "xss", "code_injection"] },
11313
+ { method: "UUID", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11314
+ { method: "Decimal", class: "decimal", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] }
11291
11315
  ];
11292
11316
  function getDefaultConfig() {
11293
11317
  return {
@@ -11398,7 +11422,7 @@ function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy,
11398
11422
  `) : undefined;
11399
11423
  const sources = findSources(calls, types, config.sources, sourceLines, language);
11400
11424
  const sinks = findSinks(calls, config.sinks, typeHierarchy, language, sourceLines);
11401
- const sanitizers = findSanitizers(calls, types, config.sanitizers);
11425
+ const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
11402
11426
  return { sources, sinks, sanitizers };
11403
11427
  }
11404
11428
  function attachSourceLineCode(sources, sinks, code) {
@@ -11419,6 +11443,9 @@ function findSources(calls, types, patterns, sourceLines, language) {
11419
11443
  const sources = [];
11420
11444
  for (const call of calls) {
11421
11445
  for (const pattern of patterns) {
11446
+ if (pattern.languages && pattern.languages.length > 0 && language !== undefined && !pattern.languages.includes(language)) {
11447
+ continue;
11448
+ }
11422
11449
  if (matchesSourcePattern(call, pattern)) {
11423
11450
  sources.push({
11424
11451
  type: pattern.type,
@@ -12048,6 +12075,15 @@ function receiverMightBeClass(receiver, className) {
12048
12075
  if (receiver === className) {
12049
12076
  return true;
12050
12077
  }
12078
+ if (receiver.endsWith(")")) {
12079
+ const ctorMatch = receiver.match(/^(\w+)\(/);
12080
+ if (ctorMatch) {
12081
+ const ctorName = ctorMatch[1];
12082
+ if (ctorName === className || ctorName.toLowerCase() === className.toLowerCase()) {
12083
+ return true;
12084
+ }
12085
+ }
12086
+ }
12051
12087
  if (receiver.includes("::")) {
12052
12088
  const scopePrefix = receiver.match(/^(\w+)::/);
12053
12089
  if (scopePrefix) {
@@ -12290,7 +12326,7 @@ function calculateSinkConfidence(call, pattern) {
12290
12326
  }
12291
12327
  return Math.min(confidence, 1);
12292
12328
  }
12293
- function findSanitizers(calls, types, patterns) {
12329
+ function findSanitizers(calls, types, patterns, sourceLines) {
12294
12330
  const sanitizers = [];
12295
12331
  const sanitizerMethods = new Set;
12296
12332
  for (const type of types) {
@@ -12300,6 +12336,77 @@ function findSanitizers(calls, types, patterns) {
12300
12336
  }
12301
12337
  }
12302
12338
  }
12339
+ const wrapperSanitizers = new Map;
12340
+ for (const type of types) {
12341
+ for (const method of type.methods) {
12342
+ const bodySize = method.end_line - method.start_line;
12343
+ if (bodySize < 0 || bodySize > 2)
12344
+ continue;
12345
+ const paramNames = new Set(method.parameters.map((p) => p.name));
12346
+ if (paramNames.size === 0)
12347
+ continue;
12348
+ const inside = [];
12349
+ for (const c of calls) {
12350
+ if (c.location.line < method.start_line || c.location.line > method.end_line)
12351
+ continue;
12352
+ if (c.method_name === method.name)
12353
+ continue;
12354
+ inside.push(c);
12355
+ }
12356
+ if (inside.length !== 1)
12357
+ continue;
12358
+ const innerCall = inside[0];
12359
+ let matched;
12360
+ for (const pattern of patterns) {
12361
+ if (matchesSanitizerPattern(innerCall, pattern)) {
12362
+ matched = pattern;
12363
+ break;
12364
+ }
12365
+ }
12366
+ if (!matched || !matched.removes || matched.removes.length === 0)
12367
+ continue;
12368
+ let argOk = false;
12369
+ for (const arg of innerCall.arguments) {
12370
+ if (arg.variable && paramNames.has(arg.variable)) {
12371
+ argOk = true;
12372
+ break;
12373
+ }
12374
+ }
12375
+ if (!argOk)
12376
+ continue;
12377
+ if (sourceLines) {
12378
+ const lineText = sourceLines[innerCall.location.line - 1] ?? "";
12379
+ const stripped = lineText.trim();
12380
+ const returnMatch = stripped.match(/^return\s+(?:await\s+)?(.*)$/);
12381
+ if (!returnMatch)
12382
+ continue;
12383
+ const after = returnMatch[1].replace(/;\s*$/, "").trimEnd();
12384
+ const callPrefix = innerCall.receiver ? `${innerCall.receiver}.${innerCall.method_name}(` : `${innerCall.method_name}(`;
12385
+ if (!after.startsWith(callPrefix))
12386
+ continue;
12387
+ if (!after.endsWith(")"))
12388
+ continue;
12389
+ }
12390
+ const existing = wrapperSanitizers.get(method.name);
12391
+ if (existing) {
12392
+ const set = new Set([...existing, ...matched.removes]);
12393
+ wrapperSanitizers.set(method.name, Array.from(set));
12394
+ } else {
12395
+ wrapperSanitizers.set(method.name, [...matched.removes]);
12396
+ }
12397
+ }
12398
+ }
12399
+ for (const call of calls) {
12400
+ const removes = wrapperSanitizers.get(call.method_name);
12401
+ if (!removes)
12402
+ continue;
12403
+ sanitizers.push({
12404
+ type: "derived_wrapper",
12405
+ method: formatSanitizerMethod(call),
12406
+ line: call.location.line,
12407
+ sanitizes: removes
12408
+ });
12409
+ }
12303
12410
  for (const call of calls) {
12304
12411
  if (sanitizerMethods.has(call.method_name)) {
12305
12412
  sanitizers.push({
@@ -12957,7 +13064,24 @@ var SANITIZER_METHODS = new Set([
12957
13064
  "validatePath",
12958
13065
  "validateCityName",
12959
13066
  "validateInput",
12960
- "sanitizeInput"
13067
+ "sanitizeInput",
13068
+ "parseInt",
13069
+ "parseLong",
13070
+ "parseFloat",
13071
+ "parseDouble",
13072
+ "parseShort",
13073
+ "parseByte",
13074
+ "fromString",
13075
+ "Number",
13076
+ "BigInt",
13077
+ "Atoi",
13078
+ "ParseInt",
13079
+ "ParseFloat",
13080
+ "ParseUint",
13081
+ "ParseBool",
13082
+ "int",
13083
+ "float",
13084
+ "bool"
12961
13085
  ]);
12962
13086
  var ANTI_SANITIZER_METHODS = new Set([
12963
13087
  "decode",
@@ -13030,6 +13154,7 @@ class ConstantPropagator {
13030
13154
  currentClassName = null;
13031
13155
  inConstructor = false;
13032
13156
  constructorParamPositions = new Map;
13157
+ safePatternFieldsCache = null;
13033
13158
  analyze(tree, sourceCode, additionalTaintPatterns = [], sanitizerMethods = [], taintedParameters = []) {
13034
13159
  this.source = sourceCode;
13035
13160
  this.additionalTaintPatterns = additionalTaintPatterns;
@@ -13060,12 +13185,14 @@ class ConstantPropagator {
13060
13185
  this.currentClassName = null;
13061
13186
  this.inConstructor = false;
13062
13187
  this.constructorParamPositions.clear();
13188
+ this.safePatternFieldsCache = null;
13063
13189
  this.collectClassFields(tree.rootNode);
13064
13190
  for (const methodName of sanitizerMethods) {
13065
13191
  this.methodReturnsSanitized.add(methodName);
13066
13192
  }
13067
13193
  this.evaluator = new ExpressionEvaluator(this.source, (name2) => this.lookupSymbol(name2));
13068
13194
  this.analyzeMethodReturns(tree.rootNode);
13195
+ this.seedPythonModuleConstants(tree.rootNode);
13069
13196
  this.visit(tree.rootNode);
13070
13197
  this.refineTaintFromConstants();
13071
13198
  const resultTainted = new Set(this.tainted);
@@ -13410,6 +13537,141 @@ class ConstantPropagator {
13410
13537
  }
13411
13538
  }
13412
13539
  }
13540
+ fieldDeclHasPrimitiveLiteralValue(node) {
13541
+ const primitive = new Set([
13542
+ "true",
13543
+ "false",
13544
+ "null_literal",
13545
+ "decimal_integer_literal",
13546
+ "hex_integer_literal",
13547
+ "octal_integer_literal",
13548
+ "binary_integer_literal",
13549
+ "decimal_floating_point_literal",
13550
+ "hex_floating_point_literal",
13551
+ "character_literal",
13552
+ "string_literal",
13553
+ "number",
13554
+ "string"
13555
+ ]);
13556
+ for (const child of node.children) {
13557
+ if (child.type !== "variable_declarator")
13558
+ continue;
13559
+ const value = child.childForFieldName("value");
13560
+ if (!value)
13561
+ continue;
13562
+ if (!primitive.has(value.type))
13563
+ return false;
13564
+ }
13565
+ return true;
13566
+ }
13567
+ getSafePatternFields() {
13568
+ if (this.safePatternFieldsCache !== null)
13569
+ return this.safePatternFieldsCache;
13570
+ const set = new Set;
13571
+ const re = /\b(?:public\s+|private\s+|protected\s+)?(?:static\s+final|final\s+static)\s+(?:java\.util\.regex\.)?Pattern\s+(\w+)\s*=\s*(?:java\.util\.regex\.)?Pattern\s*\.\s*compile\s*\(\s*"((?:[^"\\]|\\.)*)"/g;
13572
+ let m;
13573
+ while ((m = re.exec(this.source)) !== null) {
13574
+ const name2 = m[1];
13575
+ const regex = m[2];
13576
+ if (this.isStrictAnchoredRegex(regex))
13577
+ set.add(name2);
13578
+ }
13579
+ this.safePatternFieldsCache = set;
13580
+ return set;
13581
+ }
13582
+ isStrictAnchoredRegex(re) {
13583
+ if (!re.startsWith("^") || !re.endsWith("$"))
13584
+ return false;
13585
+ const stripped = re.replace(/\[(?:[^\]\\]|\\.)*\]/g, "");
13586
+ const cleaned = stripped.replace(/\\./g, "");
13587
+ if (cleaned.includes("."))
13588
+ return false;
13589
+ if (cleaned.includes("|"))
13590
+ return false;
13591
+ return true;
13592
+ }
13593
+ detectRegexAllowlistGuard(condition, consequence) {
13594
+ if (!consequence)
13595
+ return null;
13596
+ let condText = getNodeText2(condition, this.source).replace(/\s+/g, "");
13597
+ while (condText.startsWith("(") && condText.endsWith(")")) {
13598
+ const inner = condText.slice(1, -1);
13599
+ let depth = 0;
13600
+ let balanced = true;
13601
+ for (let i2 = 0;i2 < inner.length; i2++) {
13602
+ if (inner[i2] === "(")
13603
+ depth++;
13604
+ else if (inner[i2] === ")")
13605
+ depth--;
13606
+ if (depth < 0) {
13607
+ balanced = false;
13608
+ break;
13609
+ }
13610
+ }
13611
+ if (!balanced || depth !== 0)
13612
+ break;
13613
+ condText = inner;
13614
+ }
13615
+ const m = condText.match(/^!(\w+)\.matcher\((\w+)\)\.matches\(\)$/);
13616
+ if (!m)
13617
+ return null;
13618
+ const patternName = m[1];
13619
+ const varName = m[2];
13620
+ if (!this.getSafePatternFields().has(patternName))
13621
+ return null;
13622
+ if (!this.consequenceContainsThrow(consequence))
13623
+ return null;
13624
+ return varName;
13625
+ }
13626
+ consequenceContainsThrow(node) {
13627
+ if (node.type === "throw_statement")
13628
+ return true;
13629
+ const stack = [node];
13630
+ while (stack.length > 0) {
13631
+ const n = stack.pop();
13632
+ if (!n)
13633
+ continue;
13634
+ if (n.type === "throw_statement")
13635
+ return true;
13636
+ if (n.type === "if_statement" || n.type === "switch_statement")
13637
+ continue;
13638
+ for (const c of n.children)
13639
+ stack.push(c);
13640
+ }
13641
+ return false;
13642
+ }
13643
+ seedPythonModuleConstants(root) {
13644
+ if (root.type !== "module")
13645
+ return;
13646
+ for (const child of root.children) {
13647
+ const target = child.type === "assignment" ? child : child.type === "expression_statement" && child.children.length > 0 ? child.children[0] : null;
13648
+ if (!target || target.type !== "assignment")
13649
+ continue;
13650
+ const left = target.childForFieldName("left");
13651
+ const right = target.childForFieldName("right");
13652
+ if (!left || !right)
13653
+ continue;
13654
+ if (left.type !== "identifier")
13655
+ continue;
13656
+ const allowed = new Set([
13657
+ "true",
13658
+ "false",
13659
+ "none",
13660
+ "integer",
13661
+ "float",
13662
+ "string"
13663
+ ]);
13664
+ if (!allowed.has(right.type))
13665
+ continue;
13666
+ const name2 = getNodeText2(left, this.source);
13667
+ if (!name2)
13668
+ continue;
13669
+ const value = this.evaluateExpression(right);
13670
+ if (!isKnown(value))
13671
+ continue;
13672
+ this.symbols.set(name2, value);
13673
+ }
13674
+ }
13413
13675
  findAllMethods(node) {
13414
13676
  const methods = [];
13415
13677
  const stack = [node];
@@ -13495,6 +13757,11 @@ class ConstantPropagator {
13495
13757
  case "local_variable_declaration":
13496
13758
  this.handleVariableDeclaration(node);
13497
13759
  return false;
13760
+ case "field_declaration":
13761
+ if (this.fieldDeclHasPrimitiveLiteralValue(node)) {
13762
+ this.handleVariableDeclaration(node);
13763
+ }
13764
+ return false;
13498
13765
  case "assignment_expression":
13499
13766
  this.handleAssignment(node);
13500
13767
  return false;
@@ -13964,6 +14231,16 @@ class ConstantPropagator {
13964
14231
  }
13965
14232
  this.inConditionalBranch = wasInConditional;
13966
14233
  this.tainted = new Set([...taintedBefore, ...taintedAfterThen, ...taintedAfterElse]);
14234
+ const guardedVar = this.detectRegexAllowlistGuard(condition, consequence);
14235
+ if (guardedVar) {
14236
+ this.tainted.delete(guardedVar);
14237
+ this.sanitizedVars.add(guardedVar);
14238
+ const scoped = this.getScopedName(guardedVar);
14239
+ if (scoped !== guardedVar) {
14240
+ this.tainted.delete(scoped);
14241
+ this.sanitizedVars.add(scoped);
14242
+ }
14243
+ }
13967
14244
  }
13968
14245
  }
13969
14246
  normalizeCondition(cond) {
@@ -14135,15 +14412,26 @@ class ConstantPropagator {
14135
14412
  return null;
14136
14413
  }
14137
14414
  isSanitizerMethodCall(node) {
14138
- if (node.type !== "method_invocation") {
14415
+ const methodName = this.extractCallName(node);
14416
+ if (!methodName)
14139
14417
  return false;
14418
+ return SANITIZER_METHODS.has(methodName) || this.methodReturnsSanitized.has(methodName);
14419
+ }
14420
+ extractCallName(node) {
14421
+ let fnNode = null;
14422
+ if (node.type === "method_invocation") {
14423
+ fnNode = node.childForFieldName("name");
14424
+ } else if (node.type === "call_expression" || node.type === "call") {
14425
+ fnNode = node.childForFieldName("function");
14140
14426
  }
14141
- const nameNode = node.childForFieldName("name");
14142
- if (!nameNode) {
14143
- return false;
14427
+ if (!fnNode)
14428
+ return null;
14429
+ if (fnNode.type === "selector_expression" || fnNode.type === "member_expression" || fnNode.type === "attribute") {
14430
+ const tail = fnNode.childForFieldName("field") || fnNode.childForFieldName("property") || fnNode.childForFieldName("attribute");
14431
+ if (tail)
14432
+ return getNodeText2(tail, this.source);
14144
14433
  }
14145
- const methodName = getNodeText2(nameNode, this.source);
14146
- return SANITIZER_METHODS.has(methodName) || this.methodReturnsSanitized.has(methodName);
14434
+ return getNodeText2(fnNode, this.source);
14147
14435
  }
14148
14436
  isAntiSanitizerCall(node) {
14149
14437
  if (node.type !== "method_invocation") {
@@ -20350,6 +20638,7 @@ class LanguageSourcesPass {
20350
20638
  const additionalSources = [];
20351
20639
  const additionalSinks = [];
20352
20640
  additionalSources.push(...findGetterSources(types, constProp.instanceFieldTaint, code));
20641
+ additionalSources.push(...findOopFieldReadSources(types, code, language));
20353
20642
  additionalSources.push(...findJavaScriptAssignmentSources(code, language));
20354
20643
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
20355
20644
  for (const s of jsDOMSinks) {
@@ -20455,6 +20744,128 @@ function findGetterSources(types, instanceFieldTaint, _sourceCode) {
20455
20744
  }
20456
20745
  return sources;
20457
20746
  }
20747
+ function findOopFieldReadSources(types, sourceCode, language) {
20748
+ if (language !== "java" && language !== "python")
20749
+ return [];
20750
+ const sources = [];
20751
+ const lines = sourceCode.split(`
20752
+ `);
20753
+ const isPython = language === "python";
20754
+ const SELF = isPython ? "self" : "this";
20755
+ const javaHttpPattern = /\b(?:req|request|httpRequest|servletRequest|httpServletRequest)\.(?:getParameter|getParameterValues|getParameterMap|getHeader|getHeaders|getCookies|getQueryString|getPathInfo|getRequestURI|getRequestURL|getInputStream|getReader)\b/;
20756
+ const fieldAssignRe = new RegExp(`^\\s*${SELF}\\.([A-Za-z_]\\w*)\\s*=\\s*(.+?)(?:;\\s*)?$`);
20757
+ const commentPrefix = isPython ? "#" : "//";
20758
+ for (const type of types) {
20759
+ if (type.kind !== "class")
20760
+ continue;
20761
+ if (type.name === "<module>")
20762
+ continue;
20763
+ let ctor;
20764
+ for (const m of type.methods) {
20765
+ if (isPython) {
20766
+ if (m.name === "__init__") {
20767
+ ctor = m;
20768
+ break;
20769
+ }
20770
+ } else {
20771
+ if (m.name === type.name) {
20772
+ ctor = m;
20773
+ break;
20774
+ }
20775
+ }
20776
+ }
20777
+ if (!ctor)
20778
+ continue;
20779
+ const paramNames = new Set;
20780
+ for (const p of ctor.parameters) {
20781
+ if (p.name === "self" || p.name === "this")
20782
+ continue;
20783
+ paramNames.add(p.name);
20784
+ }
20785
+ const fieldTaint = new Map;
20786
+ const ctorStart = ctor.start_line;
20787
+ const ctorEnd = ctor.end_line;
20788
+ for (let i2 = ctorStart - 1;i2 < Math.min(ctorEnd, lines.length); i2++) {
20789
+ const line = lines[i2] ?? "";
20790
+ if (line.trim().startsWith(commentPrefix))
20791
+ continue;
20792
+ const m = line.match(fieldAssignRe);
20793
+ if (!m)
20794
+ continue;
20795
+ const fieldName = m[1];
20796
+ const rhs = m[2].trim().replace(/;\s*$/, "");
20797
+ let sourceType = null;
20798
+ if (paramNames.has(rhs)) {
20799
+ sourceType = "interprocedural_param";
20800
+ } else if (!isPython && javaHttpPattern.test(rhs)) {
20801
+ sourceType = "http_param";
20802
+ } else if (isPython) {
20803
+ for (const { pattern, type: type2 } of PYTHON_TAINTED_PATTERNS2) {
20804
+ if (pattern.test(rhs)) {
20805
+ sourceType = type2;
20806
+ break;
20807
+ }
20808
+ }
20809
+ }
20810
+ if (sourceType) {
20811
+ fieldTaint.set(fieldName, { line: i2 + 1, type: sourceType });
20812
+ }
20813
+ }
20814
+ if (fieldTaint.size === 0)
20815
+ continue;
20816
+ for (const [fieldName, info2] of fieldTaint) {
20817
+ sources.push({
20818
+ type: info2.type,
20819
+ location: `${type.name}.${SELF}.${fieldName} (constructor-injected field, #78)`,
20820
+ severity: "high",
20821
+ line: info2.line,
20822
+ confidence: 0.85,
20823
+ variable: `${SELF}.${fieldName}`
20824
+ });
20825
+ }
20826
+ for (const m of type.methods) {
20827
+ if (m === ctor)
20828
+ continue;
20829
+ const nonSelfParams = m.parameters.filter((p) => p.name !== "self" && p.name !== "this");
20830
+ if (nonSelfParams.length !== 0)
20831
+ continue;
20832
+ const mStart = m.start_line;
20833
+ const mEnd = m.end_line;
20834
+ let returnedField = null;
20835
+ let returnStatementCount = 0;
20836
+ const returnRe = new RegExp(`\\breturn\\s+${SELF}\\.([A-Za-z_]\\w*)\\s*[;}]?`);
20837
+ for (let i2 = mStart - 1;i2 < Math.min(mEnd, lines.length); i2++) {
20838
+ const raw = lines[i2] ?? "";
20839
+ const trimmed = raw.trim();
20840
+ if (!trimmed)
20841
+ continue;
20842
+ if (trimmed.startsWith(commentPrefix))
20843
+ continue;
20844
+ const rm = trimmed.match(returnRe);
20845
+ if (rm) {
20846
+ returnedField = rm[1];
20847
+ returnStatementCount++;
20848
+ } else if (/\breturn\b/.test(trimmed)) {
20849
+ returnStatementCount = 99;
20850
+ break;
20851
+ }
20852
+ }
20853
+ if (returnStatementCount === 1 && returnedField && fieldTaint.has(returnedField)) {
20854
+ const fieldInfo = fieldTaint.get(returnedField);
20855
+ const getterVar = isPython ? `${SELF}.${m.name}` : m.name;
20856
+ sources.push({
20857
+ type: fieldInfo.type,
20858
+ location: `${type.name}.${m.name} returns tainted field '${returnedField}' (#78)`,
20859
+ severity: "high",
20860
+ line: m.start_line,
20861
+ confidence: 0.85,
20862
+ variable: getterVar
20863
+ });
20864
+ }
20865
+ }
20866
+ }
20867
+ return sources;
20868
+ }
20458
20869
  function findJavaScriptAssignmentSources(sourceCode, language) {
20459
20870
  if (!["javascript", "typescript"].includes(language))
20460
20871
  return [];
@@ -20821,26 +21232,44 @@ function findBashTaintSources(sourceCode, dfg) {
20821
21232
  const lines = sourceCode.split(`
20822
21233
  `);
20823
21234
  const definedVars = new Set(dfg.defs.filter((d) => d.kind === "local").map((d) => d.variable));
21235
+ const fnHeaderRe = /^\s*(?:function\s+)?[A-Za-z_][\w-]*\s*\(\s*\)\s*\{?\s*$|^\s*function\s+[A-Za-z_][\w-]*\s*\{?\s*$/;
21236
+ let braceDepth = 0;
20824
21237
  for (let i2 = 0;i2 < lines.length; i2++) {
20825
21238
  const line = lines[i2];
20826
21239
  const trimmed = line.trim();
20827
21240
  const lineNumber = i2 + 1;
20828
21241
  if (trimmed.startsWith("#"))
20829
21242
  continue;
20830
- const positionalRe = /\$([1-9@*])|\$\{([1-9@*])\}/g;
20831
- let m;
20832
- while ((m = positionalRe.exec(line)) !== null) {
20833
- const param = m[1] ?? m[2];
20834
- const alreadyExists = sources.some((s) => s.line === lineNumber && s.variable === param);
20835
- if (!alreadyExists) {
20836
- sources.push({
20837
- type: "io_input",
20838
- location: `positional parameter $${param}`,
20839
- severity: "high",
20840
- line: lineNumber,
20841
- confidence: 1,
20842
- variable: param
20843
- });
21243
+ const insideFunction = braceDepth > 0;
21244
+ if (!insideFunction) {
21245
+ const positionalRe = /\$([1-9@*])|\$\{([1-9@*])\}/g;
21246
+ let m;
21247
+ while ((m = positionalRe.exec(line)) !== null) {
21248
+ const param = m[1] ?? m[2];
21249
+ const alreadyExists = sources.some((s) => s.line === lineNumber && s.variable === param);
21250
+ if (!alreadyExists) {
21251
+ sources.push({
21252
+ type: "io_input",
21253
+ location: `positional parameter $${param}`,
21254
+ severity: "high",
21255
+ line: lineNumber,
21256
+ confidence: 1,
21257
+ variable: param
21258
+ });
21259
+ }
21260
+ }
21261
+ }
21262
+ if (fnHeaderRe.test(line) || /^\s*[A-Za-z_][\w-]*\s*\(\s*\)\s*\{/.test(line)) {
21263
+ const openBracesOnLine = (line.match(/\{/g) ?? []).length;
21264
+ const closeBracesOnLine = (line.match(/\}/g) ?? []).length;
21265
+ braceDepth += openBracesOnLine - closeBracesOnLine;
21266
+ } else {
21267
+ if (braceDepth > 0) {
21268
+ const openBracesOnLine = (line.match(/\{/g) ?? []).length;
21269
+ const closeBracesOnLine = (line.match(/\}/g) ?? []).length;
21270
+ braceDepth += openBracesOnLine - closeBracesOnLine;
21271
+ if (braceDepth < 0)
21272
+ braceDepth = 0;
20844
21273
  }
20845
21274
  }
20846
21275
  const cmdSubAssign = trimmed.match(/^(\w+)=\$\((\w+)\s/);
@@ -21287,10 +21716,15 @@ function filterCleanVariableSinks(sinks, calls, taintedVars, symbols, dfg, sanit
21287
21716
  const callsAtSink = callsByLine.get(sink.line) ?? [];
21288
21717
  const isInSynchronizedBlock = synchronizedLines?.has(sink.line) ?? false;
21289
21718
  const relevantCalls = sink.method ? callsAtSink.filter((c) => c.method_name === sink.method) : callsAtSink;
21719
+ const trustArgPositions = language !== "bash" && language !== "shell";
21290
21720
  for (const call of relevantCalls) {
21291
21721
  let allArgsAreClean = true;
21722
+ let dangerousArgCount = 0;
21292
21723
  const methodName = call.in_method;
21293
21724
  for (const arg of call.arguments) {
21725
+ if (trustArgPositions && sink.argPositions && sink.argPositions.length > 0 && !sink.argPositions.includes(arg.position))
21726
+ continue;
21727
+ dangerousArgCount++;
21294
21728
  if (language === "bash" && arg.expression === call.method_name && !arg.variable && arg.literal == null)
21295
21729
  continue;
21296
21730
  if (arg.variable && !arg.expression?.includes("[")) {
@@ -21318,7 +21752,7 @@ function filterCleanVariableSinks(sinks, calls, taintedVars, symbols, dfg, sanit
21318
21752
  allArgsAreClean = false;
21319
21753
  }
21320
21754
  }
21321
- if (allArgsAreClean && call.arguments.length > 0)
21755
+ if (allArgsAreClean && dangerousArgCount > 0)
21322
21756
  return false;
21323
21757
  }
21324
21758
  return true;
@@ -21369,6 +21803,17 @@ function filterSanitizedSinks(sinks, sanitizers, calls) {
21369
21803
  }
21370
21804
 
21371
21805
  // ../circle-ir/dist/analysis/taint-propagation.js
21806
+ function buildSanitizersByLine(sanitizers) {
21807
+ const out2 = new Map;
21808
+ for (const san of sanitizers) {
21809
+ const existing = out2.get(san.line);
21810
+ if (existing)
21811
+ existing.push(san);
21812
+ else
21813
+ out2.set(san.line, [san]);
21814
+ }
21815
+ return out2;
21816
+ }
21372
21817
  function propagateTaint2(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanitizers, sanitizersArg) {
21373
21818
  let graph;
21374
21819
  let sources;
@@ -21404,7 +21849,7 @@ function propagateTaint2(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSani
21404
21849
  const defsByLine = graph.defsByLine;
21405
21850
  const usesByLine = graph.usesByLine;
21406
21851
  const callsByLine = graph.callsByLine;
21407
- const sanitizersByLine = graph.sanitizersByLine;
21852
+ const sanitizersByLine = sanitizers.length > 0 ? buildSanitizersByLine(sanitizers) : graph.sanitizersByLine;
21408
21853
  const defById = graph.defById;
21409
21854
  const rawInitialTaint = findInitialTaint(sources, callsByLine, defsByLine);
21410
21855
  const initialTaint = rawInitialTaint.filter((tv) => {
@@ -21672,7 +22117,7 @@ class TaintPropagationPass {
21672
22117
  flows.push(f);
21673
22118
  }
21674
22119
  }
21675
- const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines) ?? [];
22120
+ const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines, ctx.code) ?? [];
21676
22121
  for (const f of collectionFlows) {
21677
22122
  if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line))
21678
22123
  continue;
@@ -21694,13 +22139,13 @@ class TaintPropagationPass {
21694
22139
  continue;
21695
22140
  flows.push(f);
21696
22141
  }
21697
- const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines) ?? [];
22142
+ const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code) ?? [];
21698
22143
  for (const f of paramFlows) {
21699
22144
  if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line)) {
21700
22145
  flows.push(f);
21701
22146
  }
21702
22147
  }
21703
- const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, ctx.code, ctx.language) ?? [];
22148
+ const exprScanFlows = detectExpressionScanFlows(calls, sources, sinks, sanitizers, constProp.unreachableLines, constProp.tainted, ctx.code, ctx.language) ?? [];
21704
22149
  for (const f of exprScanFlows) {
21705
22150
  if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line && x.sink_type === f.sink_type))
21706
22151
  continue;
@@ -21722,10 +22167,25 @@ class TaintPropagationPass {
21722
22167
  continue;
21723
22168
  flows.push(f);
21724
22169
  }
21725
- return { flows };
22170
+ const sanitizedNames = constProp.sanitizedVars;
22171
+ const finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
22172
+ if (f.path.length === 0)
22173
+ return true;
22174
+ const sourceVar = f.path[0].variable;
22175
+ if (!sourceVar)
22176
+ return true;
22177
+ if (sanitizedNames.has(sourceVar))
22178
+ return false;
22179
+ for (const s of sanitizedNames) {
22180
+ if (s.endsWith(`:${sourceVar}`))
22181
+ return false;
22182
+ }
22183
+ return true;
22184
+ });
22185
+ return { flows: finalFlows };
21726
22186
  }
21727
22187
  }
21728
- function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines) {
22188
+ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code) {
21729
22189
  const flows = [];
21730
22190
  const callsByLine = new Map;
21731
22191
  for (const call of calls) {
@@ -21748,6 +22208,9 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
21748
22208
  if (taintedVars.has(varName) || taintedVars.has(scopedName)) {
21749
22209
  const source = sources[0];
21750
22210
  if (source) {
22211
+ if (typeof code === "string" && isReassignedToLiteralBetween(code, varName, source.line, sink.line)) {
22212
+ continue;
22213
+ }
21751
22214
  flows.push({
21752
22215
  source_line: source.line,
21753
22216
  sink_line: sink.line,
@@ -21782,6 +22245,9 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
21782
22245
  if (taintedVars.has(collectionVar) || taintedVars.has(scopedCollection)) {
21783
22246
  const source = sources[0];
21784
22247
  if (source) {
22248
+ if (typeof code === "string" && isReassignedToLiteralBetween(code, collectionVar, source.line, sink.line)) {
22249
+ continue;
22250
+ }
21785
22251
  flows.push({
21786
22252
  source_line: source.line,
21787
22253
  sink_line: sink.line,
@@ -21852,7 +22318,7 @@ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, un
21852
22318
  }
21853
22319
  return flows;
21854
22320
  }
21855
- function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines) {
22321
+ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines, tainted, code) {
21856
22322
  const flows = [];
21857
22323
  const paramSourcesByMethod = new Map;
21858
22324
  for (const source of sources) {
@@ -21898,6 +22364,9 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
21898
22364
  if (paramSource) {
21899
22365
  const exists = flows.some((f) => f.source_line === paramSource.line && f.sink_line === sink.line);
21900
22366
  if (!exists) {
22367
+ if (typeof code === "string" && isReassignedToLiteralBetween(code, arg.variable, paramSource.line, sink.line)) {
22368
+ continue;
22369
+ }
21901
22370
  flows.push({
21902
22371
  source_line: paramSource.line,
21903
22372
  sink_line: sink.line,
@@ -21918,7 +22387,28 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
21918
22387
  }
21919
22388
  return flows;
21920
22389
  }
21921
- function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, code, language) {
22390
+ function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
22391
+ if (!variable || sinkLine - srcLine < 2)
22392
+ return false;
22393
+ if (!/^[A-Za-z_][\w]*$/.test(variable))
22394
+ return false;
22395
+ const lines = code.split(`
22396
+ `);
22397
+ const lo = Math.max(0, srcLine);
22398
+ const hi = Math.min(lines.length, sinkLine - 1);
22399
+ const strLit = `(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|\`[^\`\\\\]*(?:\\\\.[^\`\\\\]*)*\`)`;
22400
+ const reNaked = new RegExp(`^\\s*${variable}\\s*(?::?=)\\s*${strLit}\\s*;?\\s*$`);
22401
+ const reGuarded = new RegExp(`^\\s*if\\b.*\\b${variable}\\s*=\\s*${strLit}\\s*;?\\s*$`);
22402
+ for (let i2 = lo;i2 < hi; i2++) {
22403
+ const line = lines[i2];
22404
+ if (!line)
22405
+ continue;
22406
+ if (reNaked.test(line) || reGuarded.test(line))
22407
+ return true;
22408
+ }
22409
+ return false;
22410
+ }
22411
+ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language) {
21922
22412
  const flows = [];
21923
22413
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
21924
22414
  const aliasSanitizedFor = new Map;
@@ -21973,10 +22463,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
21973
22463
  continue;
21974
22464
  const rhs = rhsMatch[1];
21975
22465
  for (const san of lineSans) {
21976
- const sanMatch = san.method.match(/^(?:(\w+)\.)?(\w+)\(\)$/);
22466
+ const sanMatch = san.method.match(/(\w+)\(\)$/);
21977
22467
  if (!sanMatch)
21978
22468
  continue;
21979
- const sanName = sanMatch[1] ? `${sanMatch[1]}.${sanMatch[2]}` : sanMatch[2];
22469
+ const sanName = sanMatch[1];
21980
22470
  if (!rhs.includes(`${sanName}(`))
21981
22471
  continue;
21982
22472
  let set = aliasSanitizedFor.get(varName);
@@ -22048,6 +22538,9 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
22048
22538
  if (aliasSanitizedFor.get(source.variable)?.has(sink.type)) {
22049
22539
  break;
22050
22540
  }
22541
+ if (typeof code === "string" && isReassignedToLiteralBetween(code, source.variable, source.line, sink.line)) {
22542
+ break;
22543
+ }
22051
22544
  flows.push({
22052
22545
  source_line: source.line,
22053
22546
  sink_line: sink.line,
@@ -22082,6 +22575,9 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
22082
22575
  for (const source of colocSources) {
22083
22576
  if (!canSourceReachSink(source.type, sink.type))
22084
22577
  continue;
22578
+ if (source.type === "file_input" && sink.type === "path_traversal" && sink.method && source.location.includes(`${sink.method}(`)) {
22579
+ continue;
22580
+ }
22085
22581
  if (flows.some((f) => f.source_line === source.line && f.sink_line === sink.line && f.sink_type === sink.type))
22086
22582
  continue;
22087
22583
  flows.push({
@@ -26325,6 +26821,25 @@ var JS_ROUTE_METHODS = new Set([
26325
26821
  "head",
26326
26822
  "options"
26327
26823
  ]);
26824
+ var SECURITY_MIDDLEWARE_METHODS = new Set([
26825
+ "helmet",
26826
+ "frameguard",
26827
+ "contentSecurityPolicy",
26828
+ "hsts",
26829
+ "noSniff",
26830
+ "xssFilter",
26831
+ "referrerPolicy",
26832
+ "permittedCrossDomainPolicies",
26833
+ "dnsPrefetchControl",
26834
+ "frameOptions",
26835
+ "headers",
26836
+ "httpStrictTransportSecurity",
26837
+ "contentTypeOptions",
26838
+ "xssProtection",
26839
+ "Talisman",
26840
+ "Secure"
26841
+ ]);
26842
+ var SECURITY_MIDDLEWARE_ANNOTATIONS_RE = /\b(EnableWebSecurity|SecurityFilterChain|after_request|before_request)\b/;
26328
26843
 
26329
26844
  class SecurityHeadersPass {
26330
26845
  name = "security-headers";
@@ -26355,6 +26870,7 @@ class SecurityHeadersPass {
26355
26870
  list.push(call);
26356
26871
  }
26357
26872
  const hasHandler = detectHandler(graph, calls);
26873
+ const hasGlobalMiddleware = detectGlobalSecurityMiddleware(graph, calls);
26358
26874
  for (const rule of this.rules) {
26359
26875
  const headerKey = rule.header.toLowerCase();
26360
26876
  const writes = writtenHeaders.get(headerKey) ?? [];
@@ -26363,6 +26879,8 @@ class SecurityHeadersPass {
26363
26879
  continue;
26364
26880
  if (rule.requiresHandler !== false && !hasHandler)
26365
26881
  continue;
26882
+ if (hasGlobalMiddleware)
26883
+ continue;
26366
26884
  ctx.addFinding({
26367
26885
  id: `${rule.rule_id}-${file}`,
26368
26886
  pass: this.name,
@@ -26537,6 +27055,28 @@ function detectHandler(graph, calls) {
26537
27055
  }
26538
27056
  return false;
26539
27057
  }
27058
+ function detectGlobalSecurityMiddleware(graph, calls) {
27059
+ for (const call of calls) {
27060
+ if (SECURITY_MIDDLEWARE_METHODS.has(call.method_name))
27061
+ return true;
27062
+ if (call.method_name === "use" && call.arguments.length > 0) {
27063
+ const firstArg = call.arguments[0].expression ?? "";
27064
+ if (/\b(helmet|Talisman|secure)\b/.test(firstArg))
27065
+ return true;
27066
+ }
27067
+ }
27068
+ for (const type of graph.ir.types) {
27069
+ if (type.annotations.some((a) => SECURITY_MIDDLEWARE_ANNOTATIONS_RE.test(a)))
27070
+ return true;
27071
+ for (const method of type.methods) {
27072
+ if (method.annotations.some((a) => SECURITY_MIDDLEWARE_ANNOTATIONS_RE.test(a)))
27073
+ return true;
27074
+ if (/^security[A-Za-z]*FilterChain$/i.test(method.name))
27075
+ return true;
27076
+ }
27077
+ }
27078
+ return false;
27079
+ }
26540
27080
  function checkInheritedCorsHeaders(fileAnalyses, typeHierarchy, sourceLines) {
26541
27081
  const findings = [];
26542
27082
  for (const { file: parentFile, analysis: parentIR } of fileAnalyses) {
@@ -30023,7 +30563,7 @@ var colors = {
30023
30563
  };
30024
30564
 
30025
30565
  // src/version.ts
30026
- var version = "3.57.0";
30566
+ var version = "3.59.0";
30027
30567
 
30028
30568
  // src/formatters.ts
30029
30569
  var SINK_SEVERITY = {
@@ -30797,7 +31337,8 @@ var TEST_PATTERNS = [
30797
31337
  /_test\.py$/,
30798
31338
  /_tests\.py$/,
30799
31339
  /test_.*\.py$/,
30800
- /_test\.rs$/
31340
+ /_test\.rs$/,
31341
+ /_test\.go$/
30801
31342
  ];
30802
31343
  function isTestFile2(filePath) {
30803
31344
  return TEST_PATTERNS.some((pattern) => pattern.test(filePath));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.57.0",
3
+ "version": "3.59.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",
@@ -65,7 +65,7 @@
65
65
  "registry": "https://registry.npmjs.org/"
66
66
  },
67
67
  "dependencies": {
68
- "circle-ir": "^3.57.0"
68
+ "circle-ir": "^3.59.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",