cognium-dev 3.108.0 → 3.111.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 +502 -20
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -10510,10 +10510,15 @@ var DEFAULT_SINKS = [
10510
10510
  { method: "bash", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10511
10511
  { method: "shell", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10512
10512
  { method: "sh", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10513
- { method: "spawn", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10513
+ { method: "spawn", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
10514
+ { method: "spawnSync", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
10515
+ { method: "execFile", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
10516
+ { method: "execFileSync", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
10514
10517
  { method: "fork", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10515
10518
  { method: "popen", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10516
10519
  { method: "system", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10520
+ { method: "command", class: "execa", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10521
+ { method: "commandSync", class: "execa", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10517
10522
  { method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10518
10523
  { method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10519
10524
  { method: "addArgument", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
@@ -11211,6 +11216,8 @@ var DEFAULT_SINKS = [
11211
11216
  { method: "delete_one", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11212
11217
  { method: "delete_many", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11213
11218
  { method: "aggregate", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11219
+ { method: "find", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11220
+ { method: "aggregate", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11214
11221
  { method: "find_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11215
11222
  { method: "update_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
11216
11223
  { method: "update_many", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
@@ -11221,18 +11228,24 @@ var DEFAULT_SINKS = [
11221
11228
  { method: "from_string", class: "Template", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11222
11229
  { method: "Template", class: "jinja2", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11223
11230
  { method: "Template", class: "mako", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11224
- { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11225
- { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11226
- { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11227
- { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11228
- { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11229
- { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11230
- { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11231
- { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11232
- { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11233
- { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11234
- { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1] },
11235
- { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11231
+ { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11232
+ { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11233
+ { method: "warn", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11234
+ { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11235
+ { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11236
+ { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11237
+ { method: "fatal", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11238
+ { method: "exception", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11239
+ { method: "log", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11240
+ { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11241
+ { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11242
+ { method: "warn", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11243
+ { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11244
+ { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11245
+ { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11246
+ { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11247
+ { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11248
+ { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11236
11249
  { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11237
11250
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11238
11251
  { method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
@@ -11663,10 +11676,61 @@ function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy,
11663
11676
  const sourceLines = code !== undefined ? code.split(`
11664
11677
  `) : undefined;
11665
11678
  const sources = findSources(calls, types, config.sources, sourceLines, language);
11666
- const sinks = findSinks(calls, config.sinks, typeHierarchy, language, sourceLines);
11679
+ const sinkPatterns = expandPromisifyAliases(config.sinks, sourceLines, language);
11680
+ const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines);
11667
11681
  const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
11668
11682
  return { sources, sinks, sanitizers };
11669
11683
  }
11684
+ function expandPromisifyAliases(patterns, sourceLines, language) {
11685
+ if (!sourceLines || sourceLines.length === 0)
11686
+ return patterns;
11687
+ if (language !== "javascript" && language !== "typescript")
11688
+ return patterns;
11689
+ const PROMISIFY_RE = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:util\s*\.\s*)?promisify\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/;
11690
+ const innerToPattern = new Map;
11691
+ for (const p of patterns) {
11692
+ if (p.languages && !p.languages.includes(language))
11693
+ continue;
11694
+ if (p.class !== undefined)
11695
+ continue;
11696
+ if (!innerToPattern.has(p.method))
11697
+ innerToPattern.set(p.method, p);
11698
+ }
11699
+ for (const p of patterns) {
11700
+ if (p.languages && !p.languages.includes(language))
11701
+ continue;
11702
+ if (innerToPattern.has(p.method))
11703
+ continue;
11704
+ innerToPattern.set(p.method, p);
11705
+ }
11706
+ const aliasPatterns = [];
11707
+ const seenAliases = new Set;
11708
+ for (const line of sourceLines) {
11709
+ const m = PROMISIFY_RE.exec(line);
11710
+ if (!m)
11711
+ continue;
11712
+ const aliasName = m[1];
11713
+ const innerName = m[2];
11714
+ if (!aliasName || !innerName)
11715
+ continue;
11716
+ if (seenAliases.has(aliasName))
11717
+ continue;
11718
+ const template = innerToPattern.get(innerName);
11719
+ if (!template)
11720
+ continue;
11721
+ seenAliases.add(aliasName);
11722
+ aliasPatterns.push({
11723
+ method: aliasName,
11724
+ type: template.type,
11725
+ cwe: template.cwe,
11726
+ severity: template.severity,
11727
+ arg_positions: template.arg_positions,
11728
+ languages: [language],
11729
+ note: `util.promisify alias of ${innerName} — cognium-dev #187`
11730
+ });
11731
+ }
11732
+ return aliasPatterns.length === 0 ? patterns : patterns.concat(aliasPatterns);
11733
+ }
11670
11734
  function attachSourceLineCode(sources, sinks, code) {
11671
11735
  const lines = code.split(`
11672
11736
  `);
@@ -12047,6 +12111,46 @@ function isSafeGoExecCommandCall(call, pattern, language) {
12047
12111
  return false;
12048
12112
  return true;
12049
12113
  }
12114
+ function isSafeJSChildProcessCall(call, pattern, language) {
12115
+ if (language !== "javascript" && language !== "typescript")
12116
+ return false;
12117
+ if (pattern.type !== "command_injection")
12118
+ return false;
12119
+ const m = call.method_name;
12120
+ if (m !== "spawn" && m !== "spawnSync" && m !== "execFile" && m !== "execFileSync")
12121
+ return false;
12122
+ const programArg = call.arguments.find((a) => a.position === 0);
12123
+ if (!programArg)
12124
+ return false;
12125
+ let program;
12126
+ if (programArg.literal !== null && programArg.literal !== undefined) {
12127
+ program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
12128
+ } else {
12129
+ const expr = (programArg.expression ?? "").trim();
12130
+ if (!(expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'"))) {
12131
+ return false;
12132
+ }
12133
+ const stripped = expr.slice(1, -1);
12134
+ program = stripped.split("/").pop() ?? stripped;
12135
+ }
12136
+ const SHELL_PROGRAMS = new Set([
12137
+ "sh",
12138
+ "bash",
12139
+ "zsh",
12140
+ "dash",
12141
+ "ash",
12142
+ "ksh",
12143
+ "cmd",
12144
+ "cmd.exe",
12145
+ "powershell",
12146
+ "pwsh",
12147
+ "powershell.exe",
12148
+ "pwsh.exe"
12149
+ ]);
12150
+ if (SHELL_PROGRAMS.has(program))
12151
+ return false;
12152
+ return true;
12153
+ }
12050
12154
  function isSafeRustCommandCall(call, pattern, language) {
12051
12155
  if (language !== "rust")
12052
12156
  return false;
@@ -12283,6 +12387,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12283
12387
  if (isSafeRustCommandCall(call, pattern, language)) {
12284
12388
  continue;
12285
12389
  }
12390
+ if (isSafeJSChildProcessCall(call, pattern, language)) {
12391
+ continue;
12392
+ }
12286
12393
  if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
12287
12394
  continue;
12288
12395
  }
@@ -12706,6 +12813,9 @@ function receiverMightBeClass(receiver, className) {
12706
12813
  return true;
12707
12814
  }
12708
12815
  }
12816
+ if (className === "Logger" && /\.getLogger\(.*\)$/.test(receiver)) {
12817
+ return true;
12818
+ }
12709
12819
  }
12710
12820
  if (receiver.includes("::") && receiver.endsWith(")")) {
12711
12821
  const scopedMatch = receiver.match(/^(\w+)::(\w+)\(.*\)$/);
@@ -23569,6 +23679,23 @@ var JS_XSS_SANITIZERS = [
23569
23679
  /\btrustAsHtml\s*\(/,
23570
23680
  /\bbypassSecurityTrust/
23571
23681
  ];
23682
+ var JS_LOG_INJECTION_SANITIZERS = [
23683
+ /\bstripCrlf\s*\(/,
23684
+ /\bstripCRLF\s*\(/,
23685
+ /\bremoveNewlines\s*\(/,
23686
+ /\bsanitizeLogValue\s*\(/,
23687
+ /\.replace\(\s*\/\[\s*\\r\s*\\n\s*\]\/[gimsu]*\s*,\s*['"`]['"`]\s*\)/,
23688
+ /\.replace\(\s*\/\\r\\n\/[gimsu]*\s*,\s*['"`]['"`]\s*\)/
23689
+ ];
23690
+ var PY_BUILTIN_LDAP_SANITIZERS = ["escape_filter_chars", "filter_format"];
23691
+ var PY_LDAP_METACHARS = ["(", ")", "=", "*", "\\"];
23692
+ var PY_DEF_RE = /^\s*def\s+([A-Za-z_]\w*)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*:\s*$/;
23693
+ var PY_LDAP_STRIP_RETURN_RE = /^\s*return\s+re\.sub\(\s*r?["']\[([^"'\]]+)\]["']\s*,\s*r?["']["']\s*,\s*([A-Za-z_]\w*)\s*\)\s*$/;
23694
+ var PY_XML_PARSER_HARDENED_RE = /\bXMLParser\s*\([^)]*\bresolve_entities\s*=\s*False\b[^)]*\)/;
23695
+ var PY_SQL_EXEC_METHODS = new Set(["execute", "executemany"]);
23696
+ var PY_INLINE_FULLMATCH_RE = /\bre\s*\.\s*fullmatch\s*\(\s*r?"((?:[^"\\]|\\.)*)"/g;
23697
+ var PY_INLINE_MATCH_ANCHORED_RE = /\bre\s*\.\s*match\s*\(\s*r?"\^((?:[^"\\]|\\.)*)\$"/g;
23698
+ var PY_BIND_PLACEHOLDER_RE = /\?|%s|:[A-Za-z_]\w*/;
23572
23699
  var DATA_PARSER_TYPES = new Set([
23573
23700
  "Parser",
23574
23701
  "CommandLine",
@@ -24358,9 +24485,355 @@ class SinkFilterPass {
24358
24485
  return true;
24359
24486
  });
24360
24487
  }
24488
+ if (["javascript", "typescript"].includes(language)) {
24489
+ const sourceLines = ctx.code.split(`
24490
+ `);
24491
+ filtered = filtered.filter((sink) => {
24492
+ if (sink.type !== "log_injection")
24493
+ return true;
24494
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
24495
+ if (JS_LOG_INJECTION_SANITIZERS.some((p) => p.test(sinkLineText)))
24496
+ return false;
24497
+ const varNames = extractSourceIdentifiers(sinkLineText);
24498
+ for (const v of varNames) {
24499
+ if (isAssignedFromSanitizerPattern(sourceLines, sink.line, v, JS_LOG_INJECTION_SANITIZERS)) {
24500
+ return false;
24501
+ }
24502
+ }
24503
+ return true;
24504
+ });
24505
+ }
24506
+ if (language === "python") {
24507
+ const sourceLines = ctx.code.split(`
24508
+ `);
24509
+ const wrappers = findPythonLdapStripWrappers(sourceLines);
24510
+ const allLdapSanitizers = [...PY_BUILTIN_LDAP_SANITIZERS, ...wrappers];
24511
+ if (allLdapSanitizers.length > 0) {
24512
+ const ldapPatterns = allLdapSanitizers.map((name2) => new RegExp(`\\b${escapeRegex(name2)}\\s*\\(`));
24513
+ filtered = filtered.filter((sink) => {
24514
+ if (sink.type !== "ldap_injection")
24515
+ return true;
24516
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
24517
+ if (ldapPatterns.some((p) => p.test(sinkLineText)))
24518
+ return false;
24519
+ const varNames = extractSourceIdentifiers(sinkLineText);
24520
+ for (const v of varNames) {
24521
+ if (isAssignedFromSanitizerPattern(sourceLines, sink.line, v, ldapPatterns)) {
24522
+ return false;
24523
+ }
24524
+ }
24525
+ return true;
24526
+ });
24527
+ }
24528
+ }
24529
+ if (language === "python") {
24530
+ const sourceLines = ctx.code.split(`
24531
+ `);
24532
+ filtered = filtered.filter((sink) => {
24533
+ if (sink.type !== "xxe")
24534
+ return true;
24535
+ const lookback = 30;
24536
+ const lo = Math.max(0, sink.line - 1 - lookback);
24537
+ for (let i2 = sink.line - 2;i2 >= lo; i2--) {
24538
+ const ln = sourceLines[i2] ?? "";
24539
+ if (PY_XML_PARSER_HARDENED_RE.test(ln))
24540
+ return false;
24541
+ if (/^\s*def\s+\w+\s*\(/.test(ln))
24542
+ break;
24543
+ }
24544
+ return true;
24545
+ });
24546
+ }
24547
+ if (language === "python") {
24548
+ const sourceLines = ctx.code.split(`
24549
+ `);
24550
+ filtered = filtered.filter((sink) => {
24551
+ if (sink.type !== "sql_injection")
24552
+ return true;
24553
+ const method = sink.method ?? "";
24554
+ if (!PY_SQL_EXEC_METHODS.has(method))
24555
+ return true;
24556
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
24557
+ const fstrMatch = sinkLineText.match(/\bf"((?:[^"\\]|\\.)*)"|\bf'((?:[^'\\]|\\.)*)'/);
24558
+ if (!fstrMatch)
24559
+ return true;
24560
+ const fstrBody = fstrMatch[1] ?? fstrMatch[2] ?? "";
24561
+ const { literals, interps } = splitPyFstringTokens(fstrBody);
24562
+ if (interps.length === 0)
24563
+ return true;
24564
+ if (!literals.some((lit) => PY_BIND_PLACEHOLDER_RE.test(lit)))
24565
+ return true;
24566
+ let sawHelperWithGuard = false;
24567
+ for (const interp of interps) {
24568
+ const t = interp.trim();
24569
+ if (t === "")
24570
+ return true;
24571
+ if (/^-?\d+$/.test(t) || /^"(?:[^"\\]|\\.)*"$/.test(t) || /^'(?:[^'\\]|\\.)*'$/.test(t)) {
24572
+ continue;
24573
+ }
24574
+ let helperName = null;
24575
+ const directCall = t.match(/^([A-Za-z_]\w*)\s*\(/);
24576
+ if (directCall) {
24577
+ helperName = directCall[1] ?? null;
24578
+ } else if (/^[A-Za-z_]\w*$/.test(t)) {
24579
+ helperName = findPythonAssignedHelperCall(t, sink.line, sourceLines);
24580
+ if (helperName === null)
24581
+ return true;
24582
+ } else {
24583
+ return true;
24584
+ }
24585
+ const body2 = findPythonFunctionBody(helperName, sourceLines);
24586
+ if (body2 && pythonBodyHasInlineRegexAllowlistRaise(body2)) {
24587
+ sawHelperWithGuard = true;
24588
+ }
24589
+ }
24590
+ if (!sawHelperWithGuard)
24591
+ return true;
24592
+ return false;
24593
+ });
24594
+ }
24361
24595
  return { sources, sinks: filtered, sanitizers };
24362
24596
  }
24363
24597
  }
24598
+ function escapeRegex(s) {
24599
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24600
+ }
24601
+ var RESERVED_KEYWORDS = new Set([
24602
+ "if",
24603
+ "else",
24604
+ "return",
24605
+ "const",
24606
+ "let",
24607
+ "var",
24608
+ "function",
24609
+ "class",
24610
+ "new",
24611
+ "this",
24612
+ "for",
24613
+ "while",
24614
+ "do",
24615
+ "break",
24616
+ "continue",
24617
+ "switch",
24618
+ "case",
24619
+ "default",
24620
+ "try",
24621
+ "catch",
24622
+ "finally",
24623
+ "throw",
24624
+ "typeof",
24625
+ "instanceof",
24626
+ "in",
24627
+ "of",
24628
+ "await",
24629
+ "async",
24630
+ "import",
24631
+ "export",
24632
+ "from",
24633
+ "as",
24634
+ "void",
24635
+ "null",
24636
+ "undefined",
24637
+ "true",
24638
+ "false",
24639
+ "super",
24640
+ "extends",
24641
+ "static",
24642
+ "def",
24643
+ "lambda",
24644
+ "yield",
24645
+ "pass",
24646
+ "with",
24647
+ "global",
24648
+ "nonlocal",
24649
+ "and",
24650
+ "or",
24651
+ "not",
24652
+ "is",
24653
+ "None",
24654
+ "True",
24655
+ "False",
24656
+ "raise",
24657
+ "except",
24658
+ "elif",
24659
+ "self",
24660
+ "cls",
24661
+ "print",
24662
+ "console",
24663
+ "req",
24664
+ "res",
24665
+ "request",
24666
+ "response"
24667
+ ]);
24668
+ function extractSourceIdentifiers(text) {
24669
+ const out2 = [];
24670
+ const re = /\b([A-Za-z_]\w+)\b/g;
24671
+ let m;
24672
+ while ((m = re.exec(text)) !== null) {
24673
+ const name2 = m[1];
24674
+ if (!RESERVED_KEYWORDS.has(name2))
24675
+ out2.push(name2);
24676
+ }
24677
+ return out2;
24678
+ }
24679
+ function isAssignedFromSanitizerPattern(sourceLines, sinkLine, varName, sanitizerPatterns, lookback = 30) {
24680
+ const escapedVar = escapeRegex(varName);
24681
+ const assignRe = new RegExp(`(?:\\b(?:const|let|var|final|String)\\s+)?\\b${escapedVar}\\s*=\\s*(.+)`);
24682
+ const fnBoundaryRe = /^\s*(?:def\s+\w+\s*\(|function\s+\w+\s*\(|\w+\s*[:=]\s*(?:async\s+)?\(?[^)]*\)?\s*=>)/;
24683
+ const lo = Math.max(0, sinkLine - 1 - lookback);
24684
+ for (let i2 = sinkLine - 2;i2 >= lo; i2--) {
24685
+ const ln = sourceLines[i2] ?? "";
24686
+ const m = ln.match(assignRe);
24687
+ if (m) {
24688
+ const rhs = (m[1] ?? "").trim();
24689
+ return sanitizerPatterns.some((p) => p.test(rhs));
24690
+ }
24691
+ if (fnBoundaryRe.test(ln))
24692
+ break;
24693
+ }
24694
+ return false;
24695
+ }
24696
+ function findPythonLdapStripWrappers(sourceLines) {
24697
+ const out2 = [];
24698
+ for (let i2 = 0;i2 < sourceLines.length; i2++) {
24699
+ const defMatch = sourceLines[i2].match(PY_DEF_RE);
24700
+ if (!defMatch)
24701
+ continue;
24702
+ const fnName = defMatch[1];
24703
+ const param = defMatch[2];
24704
+ for (let j = i2 + 1;j < Math.min(i2 + 4, sourceLines.length); j++) {
24705
+ const body2 = sourceLines[j];
24706
+ if (/^\s*$/.test(body2) || /^\s*#/.test(body2))
24707
+ continue;
24708
+ const ret = body2.match(PY_LDAP_STRIP_RETURN_RE);
24709
+ if (!ret)
24710
+ break;
24711
+ const charClass = ret[1];
24712
+ const argName = ret[2];
24713
+ if (argName !== param)
24714
+ break;
24715
+ const metaCount = PY_LDAP_METACHARS.filter((c) => charClass.includes(c)).length;
24716
+ if (metaCount >= 3)
24717
+ out2.push(fnName);
24718
+ break;
24719
+ }
24720
+ }
24721
+ return out2;
24722
+ }
24723
+ function splitPyFstringTokens(body2) {
24724
+ const literals = [];
24725
+ const interps = [];
24726
+ let i2 = 0;
24727
+ let cur = "";
24728
+ while (i2 < body2.length) {
24729
+ const ch = body2[i2] ?? "";
24730
+ if (ch === "{" && body2[i2 + 1] === "{") {
24731
+ cur += "{";
24732
+ i2 += 2;
24733
+ continue;
24734
+ }
24735
+ if (ch === "}" && body2[i2 + 1] === "}") {
24736
+ cur += "}";
24737
+ i2 += 2;
24738
+ continue;
24739
+ }
24740
+ if (ch === "{") {
24741
+ literals.push(cur);
24742
+ cur = "";
24743
+ i2++;
24744
+ let depth = 1;
24745
+ let expr = "";
24746
+ while (i2 < body2.length && depth > 0) {
24747
+ const ch2 = body2[i2] ?? "";
24748
+ if (ch2 === "{") {
24749
+ depth++;
24750
+ expr += ch2;
24751
+ i2++;
24752
+ continue;
24753
+ }
24754
+ if (ch2 === "}") {
24755
+ depth--;
24756
+ if (depth === 0) {
24757
+ i2++;
24758
+ break;
24759
+ }
24760
+ expr += ch2;
24761
+ i2++;
24762
+ continue;
24763
+ }
24764
+ expr += ch2;
24765
+ i2++;
24766
+ }
24767
+ const colonIdx = expr.indexOf(":");
24768
+ const interp = colonIdx >= 0 ? expr.substring(0, colonIdx) : expr;
24769
+ interps.push(interp);
24770
+ continue;
24771
+ }
24772
+ cur += ch;
24773
+ i2++;
24774
+ }
24775
+ literals.push(cur);
24776
+ return { literals, interps };
24777
+ }
24778
+ function findPythonAssignedHelperCall(varName, sinkLine, sourceLines) {
24779
+ if (!/^[A-Za-z_]\w*$/.test(varName))
24780
+ return null;
24781
+ const lo = Math.max(0, sinkLine - 31);
24782
+ const assignRe = new RegExp(`^\\s*${varName}\\s*=\\s*([A-Za-z_]\\w*)\\s*\\(`);
24783
+ for (let i2 = sinkLine - 2;i2 >= lo; i2--) {
24784
+ const ln = sourceLines[i2] ?? "";
24785
+ const m = ln.match(assignRe);
24786
+ if (m)
24787
+ return m[1] ?? null;
24788
+ }
24789
+ return null;
24790
+ }
24791
+ function findPythonFunctionBody(funcName, sourceLines) {
24792
+ if (!/^[A-Za-z_]\w*$/.test(funcName))
24793
+ return null;
24794
+ const sigRe = new RegExp(`^(\\s*)def\\s+${funcName}\\s*\\(`);
24795
+ for (let i2 = 0;i2 < sourceLines.length; i2++) {
24796
+ const ln = sourceLines[i2] ?? "";
24797
+ const m = ln.match(sigRe);
24798
+ if (!m)
24799
+ continue;
24800
+ const baseIndent = (m[1] ?? "").length;
24801
+ const body2 = [];
24802
+ for (let j = i2 + 1;j < sourceLines.length; j++) {
24803
+ const ln2 = sourceLines[j] ?? "";
24804
+ if (ln2.trim() === "") {
24805
+ body2.push(ln2);
24806
+ continue;
24807
+ }
24808
+ const indentMatch = ln2.match(/^(\s*)/);
24809
+ const indent = (indentMatch?.[1] ?? "").length;
24810
+ if (indent <= baseIndent)
24811
+ break;
24812
+ body2.push(ln2);
24813
+ }
24814
+ return body2;
24815
+ }
24816
+ return null;
24817
+ }
24818
+ function pythonBodyHasInlineRegexAllowlistRaise(bodyLines) {
24819
+ const stripped = bodyLines.map((ln) => ln.replace(/#.*$/, ""));
24820
+ const text = stripped.join(`
24821
+ `);
24822
+ if (!/\braise\s+/.test(text))
24823
+ return false;
24824
+ PY_INLINE_FULLMATCH_RE.lastIndex = 0;
24825
+ let m;
24826
+ while ((m = PY_INLINE_FULLMATCH_RE.exec(text)) !== null) {
24827
+ if (isImplicitlyAnchoredAllowlistRegex(m[1] ?? ""))
24828
+ return true;
24829
+ }
24830
+ PY_INLINE_MATCH_ANCHORED_RE.lastIndex = 0;
24831
+ while ((m = PY_INLINE_MATCH_ANCHORED_RE.exec(text)) !== null) {
24832
+ if (isImplicitlyAnchoredAllowlistRegex(m[1] ?? ""))
24833
+ return true;
24834
+ }
24835
+ return false;
24836
+ }
24364
24837
  function evalArithmetic(input) {
24365
24838
  let pos = 0;
24366
24839
  const len = input.length;
@@ -24776,7 +25249,10 @@ class TaintPropagationPass {
24776
25249
  const sink = sinkByLine.get(f.sink_line);
24777
25250
  if (!sink || !sink.code || !sink.method)
24778
25251
  return true;
24779
- return !isMongoValueBoundFilter(sink.code, sink.method);
25252
+ const sourceVar = f.path && f.path.length > 0 ? f.path[0].variable : null;
25253
+ const isHttpSource = typeof f.source_type === "string" && f.source_type.startsWith("http_");
25254
+ const sourceVarForFilter = isHttpSource ? sourceVar : null;
25255
+ return !isMongoValueBoundFilter(sink.code, sink.method, sourceVarForFilter);
24780
25256
  });
24781
25257
  }
24782
25258
  }
@@ -24814,7 +25290,7 @@ class TaintPropagationPass {
24814
25290
  return { flows: finalFlows };
24815
25291
  }
24816
25292
  }
24817
- function isMongoValueBoundFilter(sinkCode, sinkMethod) {
25293
+ function isMongoValueBoundFilter(sinkCode, sinkMethod, sourceVar) {
24818
25294
  if (!sinkCode || !sinkMethod)
24819
25295
  return false;
24820
25296
  const callIdx = sinkCode.indexOf(`${sinkMethod}(`);
@@ -24882,6 +25358,12 @@ function isMongoValueBoundFilter(sinkCode, sinkMethod) {
24882
25358
  return false;
24883
25359
  if (/(['"])\$[A-Za-z_]\w*\1\s*:/.test(body2))
24884
25360
  return false;
25361
+ if (sourceVar && /^[A-Za-z_]\w*$/.test(sourceVar)) {
25362
+ const escaped = sourceVar.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25363
+ const tokenRe = new RegExp(`(?<![\\p{L}\\p{N}_])${escaped}(?![\\p{L}\\p{N}_])`, "u");
25364
+ if (tokenRe.test(stripped))
25365
+ return false;
25366
+ }
24885
25367
  return true;
24886
25368
  }
24887
25369
  function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
@@ -27007,7 +27489,7 @@ class NullDerefPass {
27007
27489
  }
27008
27490
 
27009
27491
  // ../circle-ir/dist/analysis/passes/resource-leak-pass.js
27010
- function escapeRegex(s) {
27492
+ function escapeRegex2(s) {
27011
27493
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27012
27494
  }
27013
27495
  var FACTORY_METHOD_NAME_RE = /^(?:open|create|new|get|make|build)[A-Z]/;
@@ -27161,7 +27643,7 @@ class ResourceLeakPass {
27161
27643
  return false;
27162
27644
  }
27163
27645
  isReturnedToCaller(lines, variable, fromLine, toLine) {
27164
- const varRe = new RegExp(`\\breturn\\b[^;]*\\b${escapeRegex(variable)}\\b`);
27646
+ const varRe = new RegExp(`\\breturn\\b[^;]*\\b${escapeRegex2(variable)}\\b`);
27165
27647
  for (let l = fromLine;l <= toLine && l <= lines.length; l++) {
27166
27648
  if (varRe.test(lines[l - 1] ?? ""))
27167
27649
  return true;
@@ -27169,7 +27651,7 @@ class ResourceLeakPass {
27169
27651
  return false;
27170
27652
  }
27171
27653
  fieldStoredName(lines, variable, openLine, toLine) {
27172
- const thisAssignRe = new RegExp(`\\bthis\\s*\\.\\s*(\\w+)\\s*=\\s*${escapeRegex(variable)}\\b`);
27654
+ const thisAssignRe = new RegExp(`\\bthis\\s*\\.\\s*(\\w+)\\s*=\\s*${escapeRegex2(variable)}\\b`);
27173
27655
  for (let l = openLine;l <= toLine && l <= lines.length; l++) {
27174
27656
  const m = thisAssignRe.exec(lines[l - 1] ?? "");
27175
27657
  if (m)
@@ -36200,7 +36682,7 @@ var colors = {
36200
36682
  };
36201
36683
 
36202
36684
  // src/version.ts
36203
- var version = "3.108.0";
36685
+ var version = "3.111.0";
36204
36686
 
36205
36687
  // src/formatters.ts
36206
36688
  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.108.0",
3
+ "version": "3.111.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.108.0"
69
+ "circle-ir": "^3.111.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",