cognium-dev 3.108.0 → 3.112.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 +522 -21
  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] },
@@ -11046,7 +11051,17 @@ var DEFAULT_SINKS = [
11046
11051
  { method: "execute", class: "Pool", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11047
11052
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11048
11053
  { method: "raw", type: "sql_injection", cwe: "CWE-89", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11054
+ { method: "all", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11055
+ { method: "run", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11056
+ { method: "each", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11057
+ { method: "get", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11058
+ { method: "exec", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
11049
11059
  { method: "setAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
11060
+ { method: "bypassSecurityTrustHtml", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11061
+ { method: "bypassSecurityTrustScript", type: "xss", cwe: "CWE-79", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11062
+ { method: "bypassSecurityTrustStyle", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11063
+ { method: "bypassSecurityTrustUrl", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11064
+ { method: "bypassSecurityTrustResourceUrl", type: "xss", cwe: "CWE-79", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11050
11065
  { method: "send", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11051
11066
  { method: "write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11052
11067
  { method: "end", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -11057,6 +11072,8 @@ var DEFAULT_SINKS = [
11057
11072
  { method: "runInContext", class: "vm", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11058
11073
  { method: "runInNewContext", class: "vm", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11059
11074
  { method: "runInThisContext", class: "vm", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11075
+ { method: "Script", class: "constructor", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11076
+ { method: "setImmediate", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11060
11077
  { method: "parse", class: "protobuf", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11061
11078
  { method: "parse", class: "protobufjs", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
11062
11079
  { method: "parse", class: "Root", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
@@ -11102,6 +11119,8 @@ var DEFAULT_SINKS = [
11102
11119
  { method: "request", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
11103
11120
  { method: "get", class: "got", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
11104
11121
  { method: "post", class: "got", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
11122
+ { method: "got", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11123
+ { method: "request", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
11105
11124
  { method: "get", class: "superagent", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
11106
11125
  { method: "post", class: "superagent", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
11107
11126
  { method: "default", class: "node-fetch", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
@@ -11211,6 +11230,8 @@ var DEFAULT_SINKS = [
11211
11230
  { method: "delete_one", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11212
11231
  { method: "delete_many", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11213
11232
  { method: "aggregate", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
11233
+ { method: "find", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11234
+ { method: "aggregate", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11214
11235
  { method: "find_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
11215
11236
  { method: "update_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
11216
11237
  { method: "update_many", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
@@ -11221,18 +11242,24 @@ var DEFAULT_SINKS = [
11221
11242
  { method: "from_string", class: "Template", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11222
11243
  { method: "Template", class: "jinja2", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11223
11244
  { 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] },
11245
+ { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11246
+ { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11247
+ { method: "warn", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11248
+ { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11249
+ { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11250
+ { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11251
+ { method: "fatal", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11252
+ { method: "exception", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11253
+ { method: "log", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11254
+ { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11255
+ { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11256
+ { method: "warn", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11257
+ { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11258
+ { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11259
+ { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11260
+ { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11261
+ { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11262
+ { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11236
11263
  { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11237
11264
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11238
11265
  { method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
@@ -11663,10 +11690,61 @@ function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy,
11663
11690
  const sourceLines = code !== undefined ? code.split(`
11664
11691
  `) : undefined;
11665
11692
  const sources = findSources(calls, types, config.sources, sourceLines, language);
11666
- const sinks = findSinks(calls, config.sinks, typeHierarchy, language, sourceLines);
11693
+ const sinkPatterns = expandPromisifyAliases(config.sinks, sourceLines, language);
11694
+ const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines);
11667
11695
  const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
11668
11696
  return { sources, sinks, sanitizers };
11669
11697
  }
11698
+ function expandPromisifyAliases(patterns, sourceLines, language) {
11699
+ if (!sourceLines || sourceLines.length === 0)
11700
+ return patterns;
11701
+ if (language !== "javascript" && language !== "typescript")
11702
+ return patterns;
11703
+ 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*\)/;
11704
+ const innerToPattern = new Map;
11705
+ for (const p of patterns) {
11706
+ if (p.languages && !p.languages.includes(language))
11707
+ continue;
11708
+ if (p.class !== undefined)
11709
+ continue;
11710
+ if (!innerToPattern.has(p.method))
11711
+ innerToPattern.set(p.method, p);
11712
+ }
11713
+ for (const p of patterns) {
11714
+ if (p.languages && !p.languages.includes(language))
11715
+ continue;
11716
+ if (innerToPattern.has(p.method))
11717
+ continue;
11718
+ innerToPattern.set(p.method, p);
11719
+ }
11720
+ const aliasPatterns = [];
11721
+ const seenAliases = new Set;
11722
+ for (const line of sourceLines) {
11723
+ const m = PROMISIFY_RE.exec(line);
11724
+ if (!m)
11725
+ continue;
11726
+ const aliasName = m[1];
11727
+ const innerName = m[2];
11728
+ if (!aliasName || !innerName)
11729
+ continue;
11730
+ if (seenAliases.has(aliasName))
11731
+ continue;
11732
+ const template = innerToPattern.get(innerName);
11733
+ if (!template)
11734
+ continue;
11735
+ seenAliases.add(aliasName);
11736
+ aliasPatterns.push({
11737
+ method: aliasName,
11738
+ type: template.type,
11739
+ cwe: template.cwe,
11740
+ severity: template.severity,
11741
+ arg_positions: template.arg_positions,
11742
+ languages: [language],
11743
+ note: `util.promisify alias of ${innerName} — cognium-dev #187`
11744
+ });
11745
+ }
11746
+ return aliasPatterns.length === 0 ? patterns : patterns.concat(aliasPatterns);
11747
+ }
11670
11748
  function attachSourceLineCode(sources, sinks, code) {
11671
11749
  const lines = code.split(`
11672
11750
  `);
@@ -12047,6 +12125,46 @@ function isSafeGoExecCommandCall(call, pattern, language) {
12047
12125
  return false;
12048
12126
  return true;
12049
12127
  }
12128
+ function isSafeJSChildProcessCall(call, pattern, language) {
12129
+ if (language !== "javascript" && language !== "typescript")
12130
+ return false;
12131
+ if (pattern.type !== "command_injection")
12132
+ return false;
12133
+ const m = call.method_name;
12134
+ if (m !== "spawn" && m !== "spawnSync" && m !== "execFile" && m !== "execFileSync")
12135
+ return false;
12136
+ const programArg = call.arguments.find((a) => a.position === 0);
12137
+ if (!programArg)
12138
+ return false;
12139
+ let program;
12140
+ if (programArg.literal !== null && programArg.literal !== undefined) {
12141
+ program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
12142
+ } else {
12143
+ const expr = (programArg.expression ?? "").trim();
12144
+ if (!(expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'"))) {
12145
+ return false;
12146
+ }
12147
+ const stripped = expr.slice(1, -1);
12148
+ program = stripped.split("/").pop() ?? stripped;
12149
+ }
12150
+ const SHELL_PROGRAMS = new Set([
12151
+ "sh",
12152
+ "bash",
12153
+ "zsh",
12154
+ "dash",
12155
+ "ash",
12156
+ "ksh",
12157
+ "cmd",
12158
+ "cmd.exe",
12159
+ "powershell",
12160
+ "pwsh",
12161
+ "powershell.exe",
12162
+ "pwsh.exe"
12163
+ ]);
12164
+ if (SHELL_PROGRAMS.has(program))
12165
+ return false;
12166
+ return true;
12167
+ }
12050
12168
  function isSafeRustCommandCall(call, pattern, language) {
12051
12169
  if (language !== "rust")
12052
12170
  return false;
@@ -12283,6 +12401,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12283
12401
  if (isSafeRustCommandCall(call, pattern, language)) {
12284
12402
  continue;
12285
12403
  }
12404
+ if (isSafeJSChildProcessCall(call, pattern, language)) {
12405
+ continue;
12406
+ }
12286
12407
  if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
12287
12408
  continue;
12288
12409
  }
@@ -12298,7 +12419,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12298
12419
  }
12299
12420
  }
12300
12421
  }
12301
- if (pattern.type === "code_injection" && (call.method_name === "setInterval" || call.method_name === "setTimeout")) {
12422
+ if (pattern.type === "code_injection" && (call.method_name === "setInterval" || call.method_name === "setTimeout" || call.method_name === "setImmediate")) {
12302
12423
  const firstArg = call.arguments.find((a) => a.position === 0);
12303
12424
  if (firstArg && isFunctionCallbackArgument(firstArg)) {
12304
12425
  continue;
@@ -12602,6 +12723,11 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
12602
12723
  if (typeHierarchy && typeHierarchy.couldBeType(call.receiver, pattern.class)) {
12603
12724
  return true;
12604
12725
  }
12726
+ const resolvedTarget = call.resolution?.target;
12727
+ const expectedClassMethodTail = `${pattern.class}.${pattern.method}`;
12728
+ if (resolvedTarget && (resolvedTarget === expectedClassMethodTail || resolvedTarget.endsWith("." + expectedClassMethodTail))) {
12729
+ return true;
12730
+ }
12605
12731
  if (pattern.allow_unresolved_receiver && !call.receiver_type && !call.receiver_type_fqn && call.receiver.includes(".")) {
12606
12732
  return true;
12607
12733
  }
@@ -12706,6 +12832,9 @@ function receiverMightBeClass(receiver, className) {
12706
12832
  return true;
12707
12833
  }
12708
12834
  }
12835
+ if (className === "Logger" && /\.getLogger\(.*\)$/.test(receiver)) {
12836
+ return true;
12837
+ }
12709
12838
  }
12710
12839
  if (receiver.includes("::") && receiver.endsWith(")")) {
12711
12840
  const scopedMatch = receiver.match(/^(\w+)::(\w+)\(.*\)$/);
@@ -23569,6 +23698,23 @@ var JS_XSS_SANITIZERS = [
23569
23698
  /\btrustAsHtml\s*\(/,
23570
23699
  /\bbypassSecurityTrust/
23571
23700
  ];
23701
+ var JS_LOG_INJECTION_SANITIZERS = [
23702
+ /\bstripCrlf\s*\(/,
23703
+ /\bstripCRLF\s*\(/,
23704
+ /\bremoveNewlines\s*\(/,
23705
+ /\bsanitizeLogValue\s*\(/,
23706
+ /\.replace\(\s*\/\[\s*\\r\s*\\n\s*\]\/[gimsu]*\s*,\s*['"`]['"`]\s*\)/,
23707
+ /\.replace\(\s*\/\\r\\n\/[gimsu]*\s*,\s*['"`]['"`]\s*\)/
23708
+ ];
23709
+ var PY_BUILTIN_LDAP_SANITIZERS = ["escape_filter_chars", "filter_format"];
23710
+ var PY_LDAP_METACHARS = ["(", ")", "=", "*", "\\"];
23711
+ var PY_DEF_RE = /^\s*def\s+([A-Za-z_]\w*)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*:\s*$/;
23712
+ var PY_LDAP_STRIP_RETURN_RE = /^\s*return\s+re\.sub\(\s*r?["']\[([^"'\]]+)\]["']\s*,\s*r?["']["']\s*,\s*([A-Za-z_]\w*)\s*\)\s*$/;
23713
+ var PY_XML_PARSER_HARDENED_RE = /\bXMLParser\s*\([^)]*\bresolve_entities\s*=\s*False\b[^)]*\)/;
23714
+ var PY_SQL_EXEC_METHODS = new Set(["execute", "executemany"]);
23715
+ var PY_INLINE_FULLMATCH_RE = /\bre\s*\.\s*fullmatch\s*\(\s*r?"((?:[^"\\]|\\.)*)"/g;
23716
+ var PY_INLINE_MATCH_ANCHORED_RE = /\bre\s*\.\s*match\s*\(\s*r?"\^((?:[^"\\]|\\.)*)\$"/g;
23717
+ var PY_BIND_PLACEHOLDER_RE = /\?|%s|:[A-Za-z_]\w*/;
23572
23718
  var DATA_PARSER_TYPES = new Set([
23573
23719
  "Parser",
23574
23720
  "CommandLine",
@@ -24358,9 +24504,355 @@ class SinkFilterPass {
24358
24504
  return true;
24359
24505
  });
24360
24506
  }
24507
+ if (["javascript", "typescript"].includes(language)) {
24508
+ const sourceLines = ctx.code.split(`
24509
+ `);
24510
+ filtered = filtered.filter((sink) => {
24511
+ if (sink.type !== "log_injection")
24512
+ return true;
24513
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
24514
+ if (JS_LOG_INJECTION_SANITIZERS.some((p) => p.test(sinkLineText)))
24515
+ return false;
24516
+ const varNames = extractSourceIdentifiers(sinkLineText);
24517
+ for (const v of varNames) {
24518
+ if (isAssignedFromSanitizerPattern(sourceLines, sink.line, v, JS_LOG_INJECTION_SANITIZERS)) {
24519
+ return false;
24520
+ }
24521
+ }
24522
+ return true;
24523
+ });
24524
+ }
24525
+ if (language === "python") {
24526
+ const sourceLines = ctx.code.split(`
24527
+ `);
24528
+ const wrappers = findPythonLdapStripWrappers(sourceLines);
24529
+ const allLdapSanitizers = [...PY_BUILTIN_LDAP_SANITIZERS, ...wrappers];
24530
+ if (allLdapSanitizers.length > 0) {
24531
+ const ldapPatterns = allLdapSanitizers.map((name2) => new RegExp(`\\b${escapeRegex(name2)}\\s*\\(`));
24532
+ filtered = filtered.filter((sink) => {
24533
+ if (sink.type !== "ldap_injection")
24534
+ return true;
24535
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
24536
+ if (ldapPatterns.some((p) => p.test(sinkLineText)))
24537
+ return false;
24538
+ const varNames = extractSourceIdentifiers(sinkLineText);
24539
+ for (const v of varNames) {
24540
+ if (isAssignedFromSanitizerPattern(sourceLines, sink.line, v, ldapPatterns)) {
24541
+ return false;
24542
+ }
24543
+ }
24544
+ return true;
24545
+ });
24546
+ }
24547
+ }
24548
+ if (language === "python") {
24549
+ const sourceLines = ctx.code.split(`
24550
+ `);
24551
+ filtered = filtered.filter((sink) => {
24552
+ if (sink.type !== "xxe")
24553
+ return true;
24554
+ const lookback = 30;
24555
+ const lo = Math.max(0, sink.line - 1 - lookback);
24556
+ for (let i2 = sink.line - 2;i2 >= lo; i2--) {
24557
+ const ln = sourceLines[i2] ?? "";
24558
+ if (PY_XML_PARSER_HARDENED_RE.test(ln))
24559
+ return false;
24560
+ if (/^\s*def\s+\w+\s*\(/.test(ln))
24561
+ break;
24562
+ }
24563
+ return true;
24564
+ });
24565
+ }
24566
+ if (language === "python") {
24567
+ const sourceLines = ctx.code.split(`
24568
+ `);
24569
+ filtered = filtered.filter((sink) => {
24570
+ if (sink.type !== "sql_injection")
24571
+ return true;
24572
+ const method = sink.method ?? "";
24573
+ if (!PY_SQL_EXEC_METHODS.has(method))
24574
+ return true;
24575
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
24576
+ const fstrMatch = sinkLineText.match(/\bf"((?:[^"\\]|\\.)*)"|\bf'((?:[^'\\]|\\.)*)'/);
24577
+ if (!fstrMatch)
24578
+ return true;
24579
+ const fstrBody = fstrMatch[1] ?? fstrMatch[2] ?? "";
24580
+ const { literals, interps } = splitPyFstringTokens(fstrBody);
24581
+ if (interps.length === 0)
24582
+ return true;
24583
+ if (!literals.some((lit) => PY_BIND_PLACEHOLDER_RE.test(lit)))
24584
+ return true;
24585
+ let sawHelperWithGuard = false;
24586
+ for (const interp of interps) {
24587
+ const t = interp.trim();
24588
+ if (t === "")
24589
+ return true;
24590
+ if (/^-?\d+$/.test(t) || /^"(?:[^"\\]|\\.)*"$/.test(t) || /^'(?:[^'\\]|\\.)*'$/.test(t)) {
24591
+ continue;
24592
+ }
24593
+ let helperName = null;
24594
+ const directCall = t.match(/^([A-Za-z_]\w*)\s*\(/);
24595
+ if (directCall) {
24596
+ helperName = directCall[1] ?? null;
24597
+ } else if (/^[A-Za-z_]\w*$/.test(t)) {
24598
+ helperName = findPythonAssignedHelperCall(t, sink.line, sourceLines);
24599
+ if (helperName === null)
24600
+ return true;
24601
+ } else {
24602
+ return true;
24603
+ }
24604
+ const body2 = findPythonFunctionBody(helperName, sourceLines);
24605
+ if (body2 && pythonBodyHasInlineRegexAllowlistRaise(body2)) {
24606
+ sawHelperWithGuard = true;
24607
+ }
24608
+ }
24609
+ if (!sawHelperWithGuard)
24610
+ return true;
24611
+ return false;
24612
+ });
24613
+ }
24361
24614
  return { sources, sinks: filtered, sanitizers };
24362
24615
  }
24363
24616
  }
24617
+ function escapeRegex(s) {
24618
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24619
+ }
24620
+ var RESERVED_KEYWORDS = new Set([
24621
+ "if",
24622
+ "else",
24623
+ "return",
24624
+ "const",
24625
+ "let",
24626
+ "var",
24627
+ "function",
24628
+ "class",
24629
+ "new",
24630
+ "this",
24631
+ "for",
24632
+ "while",
24633
+ "do",
24634
+ "break",
24635
+ "continue",
24636
+ "switch",
24637
+ "case",
24638
+ "default",
24639
+ "try",
24640
+ "catch",
24641
+ "finally",
24642
+ "throw",
24643
+ "typeof",
24644
+ "instanceof",
24645
+ "in",
24646
+ "of",
24647
+ "await",
24648
+ "async",
24649
+ "import",
24650
+ "export",
24651
+ "from",
24652
+ "as",
24653
+ "void",
24654
+ "null",
24655
+ "undefined",
24656
+ "true",
24657
+ "false",
24658
+ "super",
24659
+ "extends",
24660
+ "static",
24661
+ "def",
24662
+ "lambda",
24663
+ "yield",
24664
+ "pass",
24665
+ "with",
24666
+ "global",
24667
+ "nonlocal",
24668
+ "and",
24669
+ "or",
24670
+ "not",
24671
+ "is",
24672
+ "None",
24673
+ "True",
24674
+ "False",
24675
+ "raise",
24676
+ "except",
24677
+ "elif",
24678
+ "self",
24679
+ "cls",
24680
+ "print",
24681
+ "console",
24682
+ "req",
24683
+ "res",
24684
+ "request",
24685
+ "response"
24686
+ ]);
24687
+ function extractSourceIdentifiers(text) {
24688
+ const out2 = [];
24689
+ const re = /\b([A-Za-z_]\w+)\b/g;
24690
+ let m;
24691
+ while ((m = re.exec(text)) !== null) {
24692
+ const name2 = m[1];
24693
+ if (!RESERVED_KEYWORDS.has(name2))
24694
+ out2.push(name2);
24695
+ }
24696
+ return out2;
24697
+ }
24698
+ function isAssignedFromSanitizerPattern(sourceLines, sinkLine, varName, sanitizerPatterns, lookback = 30) {
24699
+ const escapedVar = escapeRegex(varName);
24700
+ const assignRe = new RegExp(`(?:\\b(?:const|let|var|final|String)\\s+)?\\b${escapedVar}\\s*=\\s*(.+)`);
24701
+ const fnBoundaryRe = /^\s*(?:def\s+\w+\s*\(|function\s+\w+\s*\(|\w+\s*[:=]\s*(?:async\s+)?\(?[^)]*\)?\s*=>)/;
24702
+ const lo = Math.max(0, sinkLine - 1 - lookback);
24703
+ for (let i2 = sinkLine - 2;i2 >= lo; i2--) {
24704
+ const ln = sourceLines[i2] ?? "";
24705
+ const m = ln.match(assignRe);
24706
+ if (m) {
24707
+ const rhs = (m[1] ?? "").trim();
24708
+ return sanitizerPatterns.some((p) => p.test(rhs));
24709
+ }
24710
+ if (fnBoundaryRe.test(ln))
24711
+ break;
24712
+ }
24713
+ return false;
24714
+ }
24715
+ function findPythonLdapStripWrappers(sourceLines) {
24716
+ const out2 = [];
24717
+ for (let i2 = 0;i2 < sourceLines.length; i2++) {
24718
+ const defMatch = sourceLines[i2].match(PY_DEF_RE);
24719
+ if (!defMatch)
24720
+ continue;
24721
+ const fnName = defMatch[1];
24722
+ const param = defMatch[2];
24723
+ for (let j = i2 + 1;j < Math.min(i2 + 4, sourceLines.length); j++) {
24724
+ const body2 = sourceLines[j];
24725
+ if (/^\s*$/.test(body2) || /^\s*#/.test(body2))
24726
+ continue;
24727
+ const ret = body2.match(PY_LDAP_STRIP_RETURN_RE);
24728
+ if (!ret)
24729
+ break;
24730
+ const charClass = ret[1];
24731
+ const argName = ret[2];
24732
+ if (argName !== param)
24733
+ break;
24734
+ const metaCount = PY_LDAP_METACHARS.filter((c) => charClass.includes(c)).length;
24735
+ if (metaCount >= 3)
24736
+ out2.push(fnName);
24737
+ break;
24738
+ }
24739
+ }
24740
+ return out2;
24741
+ }
24742
+ function splitPyFstringTokens(body2) {
24743
+ const literals = [];
24744
+ const interps = [];
24745
+ let i2 = 0;
24746
+ let cur = "";
24747
+ while (i2 < body2.length) {
24748
+ const ch = body2[i2] ?? "";
24749
+ if (ch === "{" && body2[i2 + 1] === "{") {
24750
+ cur += "{";
24751
+ i2 += 2;
24752
+ continue;
24753
+ }
24754
+ if (ch === "}" && body2[i2 + 1] === "}") {
24755
+ cur += "}";
24756
+ i2 += 2;
24757
+ continue;
24758
+ }
24759
+ if (ch === "{") {
24760
+ literals.push(cur);
24761
+ cur = "";
24762
+ i2++;
24763
+ let depth = 1;
24764
+ let expr = "";
24765
+ while (i2 < body2.length && depth > 0) {
24766
+ const ch2 = body2[i2] ?? "";
24767
+ if (ch2 === "{") {
24768
+ depth++;
24769
+ expr += ch2;
24770
+ i2++;
24771
+ continue;
24772
+ }
24773
+ if (ch2 === "}") {
24774
+ depth--;
24775
+ if (depth === 0) {
24776
+ i2++;
24777
+ break;
24778
+ }
24779
+ expr += ch2;
24780
+ i2++;
24781
+ continue;
24782
+ }
24783
+ expr += ch2;
24784
+ i2++;
24785
+ }
24786
+ const colonIdx = expr.indexOf(":");
24787
+ const interp = colonIdx >= 0 ? expr.substring(0, colonIdx) : expr;
24788
+ interps.push(interp);
24789
+ continue;
24790
+ }
24791
+ cur += ch;
24792
+ i2++;
24793
+ }
24794
+ literals.push(cur);
24795
+ return { literals, interps };
24796
+ }
24797
+ function findPythonAssignedHelperCall(varName, sinkLine, sourceLines) {
24798
+ if (!/^[A-Za-z_]\w*$/.test(varName))
24799
+ return null;
24800
+ const lo = Math.max(0, sinkLine - 31);
24801
+ const assignRe = new RegExp(`^\\s*${varName}\\s*=\\s*([A-Za-z_]\\w*)\\s*\\(`);
24802
+ for (let i2 = sinkLine - 2;i2 >= lo; i2--) {
24803
+ const ln = sourceLines[i2] ?? "";
24804
+ const m = ln.match(assignRe);
24805
+ if (m)
24806
+ return m[1] ?? null;
24807
+ }
24808
+ return null;
24809
+ }
24810
+ function findPythonFunctionBody(funcName, sourceLines) {
24811
+ if (!/^[A-Za-z_]\w*$/.test(funcName))
24812
+ return null;
24813
+ const sigRe = new RegExp(`^(\\s*)def\\s+${funcName}\\s*\\(`);
24814
+ for (let i2 = 0;i2 < sourceLines.length; i2++) {
24815
+ const ln = sourceLines[i2] ?? "";
24816
+ const m = ln.match(sigRe);
24817
+ if (!m)
24818
+ continue;
24819
+ const baseIndent = (m[1] ?? "").length;
24820
+ const body2 = [];
24821
+ for (let j = i2 + 1;j < sourceLines.length; j++) {
24822
+ const ln2 = sourceLines[j] ?? "";
24823
+ if (ln2.trim() === "") {
24824
+ body2.push(ln2);
24825
+ continue;
24826
+ }
24827
+ const indentMatch = ln2.match(/^(\s*)/);
24828
+ const indent = (indentMatch?.[1] ?? "").length;
24829
+ if (indent <= baseIndent)
24830
+ break;
24831
+ body2.push(ln2);
24832
+ }
24833
+ return body2;
24834
+ }
24835
+ return null;
24836
+ }
24837
+ function pythonBodyHasInlineRegexAllowlistRaise(bodyLines) {
24838
+ const stripped = bodyLines.map((ln) => ln.replace(/#.*$/, ""));
24839
+ const text = stripped.join(`
24840
+ `);
24841
+ if (!/\braise\s+/.test(text))
24842
+ return false;
24843
+ PY_INLINE_FULLMATCH_RE.lastIndex = 0;
24844
+ let m;
24845
+ while ((m = PY_INLINE_FULLMATCH_RE.exec(text)) !== null) {
24846
+ if (isImplicitlyAnchoredAllowlistRegex(m[1] ?? ""))
24847
+ return true;
24848
+ }
24849
+ PY_INLINE_MATCH_ANCHORED_RE.lastIndex = 0;
24850
+ while ((m = PY_INLINE_MATCH_ANCHORED_RE.exec(text)) !== null) {
24851
+ if (isImplicitlyAnchoredAllowlistRegex(m[1] ?? ""))
24852
+ return true;
24853
+ }
24854
+ return false;
24855
+ }
24364
24856
  function evalArithmetic(input) {
24365
24857
  let pos = 0;
24366
24858
  const len = input.length;
@@ -24776,7 +25268,10 @@ class TaintPropagationPass {
24776
25268
  const sink = sinkByLine.get(f.sink_line);
24777
25269
  if (!sink || !sink.code || !sink.method)
24778
25270
  return true;
24779
- return !isMongoValueBoundFilter(sink.code, sink.method);
25271
+ const sourceVar = f.path && f.path.length > 0 ? f.path[0].variable : null;
25272
+ const isHttpSource = typeof f.source_type === "string" && f.source_type.startsWith("http_");
25273
+ const sourceVarForFilter = isHttpSource ? sourceVar : null;
25274
+ return !isMongoValueBoundFilter(sink.code, sink.method, sourceVarForFilter);
24780
25275
  });
24781
25276
  }
24782
25277
  }
@@ -24814,7 +25309,7 @@ class TaintPropagationPass {
24814
25309
  return { flows: finalFlows };
24815
25310
  }
24816
25311
  }
24817
- function isMongoValueBoundFilter(sinkCode, sinkMethod) {
25312
+ function isMongoValueBoundFilter(sinkCode, sinkMethod, sourceVar) {
24818
25313
  if (!sinkCode || !sinkMethod)
24819
25314
  return false;
24820
25315
  const callIdx = sinkCode.indexOf(`${sinkMethod}(`);
@@ -24882,6 +25377,12 @@ function isMongoValueBoundFilter(sinkCode, sinkMethod) {
24882
25377
  return false;
24883
25378
  if (/(['"])\$[A-Za-z_]\w*\1\s*:/.test(body2))
24884
25379
  return false;
25380
+ if (sourceVar && /^[A-Za-z_]\w*$/.test(sourceVar)) {
25381
+ const escaped = sourceVar.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25382
+ const tokenRe = new RegExp(`(?<![\\p{L}\\p{N}_])${escaped}(?![\\p{L}\\p{N}_])`, "u");
25383
+ if (tokenRe.test(stripped))
25384
+ return false;
25385
+ }
24885
25386
  return true;
24886
25387
  }
24887
25388
  function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
@@ -27007,7 +27508,7 @@ class NullDerefPass {
27007
27508
  }
27008
27509
 
27009
27510
  // ../circle-ir/dist/analysis/passes/resource-leak-pass.js
27010
- function escapeRegex(s) {
27511
+ function escapeRegex2(s) {
27011
27512
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27012
27513
  }
27013
27514
  var FACTORY_METHOD_NAME_RE = /^(?:open|create|new|get|make|build)[A-Z]/;
@@ -27161,7 +27662,7 @@ class ResourceLeakPass {
27161
27662
  return false;
27162
27663
  }
27163
27664
  isReturnedToCaller(lines, variable, fromLine, toLine) {
27164
- const varRe = new RegExp(`\\breturn\\b[^;]*\\b${escapeRegex(variable)}\\b`);
27665
+ const varRe = new RegExp(`\\breturn\\b[^;]*\\b${escapeRegex2(variable)}\\b`);
27165
27666
  for (let l = fromLine;l <= toLine && l <= lines.length; l++) {
27166
27667
  if (varRe.test(lines[l - 1] ?? ""))
27167
27668
  return true;
@@ -27169,7 +27670,7 @@ class ResourceLeakPass {
27169
27670
  return false;
27170
27671
  }
27171
27672
  fieldStoredName(lines, variable, openLine, toLine) {
27172
- const thisAssignRe = new RegExp(`\\bthis\\s*\\.\\s*(\\w+)\\s*=\\s*${escapeRegex(variable)}\\b`);
27673
+ const thisAssignRe = new RegExp(`\\bthis\\s*\\.\\s*(\\w+)\\s*=\\s*${escapeRegex2(variable)}\\b`);
27173
27674
  for (let l = openLine;l <= toLine && l <= lines.length; l++) {
27174
27675
  const m = thisAssignRe.exec(lines[l - 1] ?? "");
27175
27676
  if (m)
@@ -36200,7 +36701,7 @@ var colors = {
36200
36701
  };
36201
36702
 
36202
36703
  // src/version.ts
36203
- var version = "3.108.0";
36704
+ var version = "3.112.0";
36204
36705
 
36205
36706
  // src/formatters.ts
36206
36707
  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.112.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.112.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",