circle-ir 3.107.0 → 3.110.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.
@@ -12195,21 +12195,32 @@ var DEFAULT_SINKS = [
12195
12195
  { method: "from_string", class: "Template", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
12196
12196
  { method: "Template", class: "jinja2", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
12197
12197
  { method: "Template", class: "mako", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
12198
- // Python Log Injection
12199
- { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12200
- { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12201
- { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12202
- { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12203
- { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12198
+ // Python Log Injection — cognium-dev #193: positions [0, 1, 2, 3, 4]
12199
+ // cover both the format string (arg 0) and Python `logging`'s positional
12200
+ // *args (1..N), which get rendered into the log line via `%` substitution
12201
+ // (e.g. `log.warning("user=%s", user)` taints via arg 1). Five-arg cap
12202
+ // matches the established explicit-enumeration pattern used elsewhere in
12203
+ // this file.
12204
+ { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12205
+ { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12206
+ { method: "warn", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12207
+ { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12208
+ { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12209
+ { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12210
+ { method: "fatal", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12211
+ { method: "exception", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12212
+ { method: "log", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
12204
12213
  // Python `logging` module top-level functions (e.g. logging.info(...))
12205
12214
  // — cognium-dev#104 Sprint 22: OOP fixtures use `import logging; logging.info(self.msg)`.
12206
- { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12207
- { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12208
- { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12209
- { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12210
- { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12211
- { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1] },
12212
- { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
12215
+ { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12216
+ { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12217
+ { method: "warn", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12218
+ { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12219
+ { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12220
+ { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12221
+ { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12222
+ { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
12223
+ { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12213
12224
  // =========================================================================
12214
12225
  // Java CWE-Bench Enhancement Patterns (Collection/Builder)
12215
12226
  // =========================================================================
@@ -13859,6 +13870,9 @@ function receiverMightBeClass(receiver, className) {
13859
13870
  return true;
13860
13871
  }
13861
13872
  }
13873
+ if (className === "Logger" && /\.getLogger\(.*\)$/.test(receiver)) {
13874
+ return true;
13875
+ }
13862
13876
  }
13863
13877
  if (receiver.includes("::") && receiver.endsWith(")")) {
13864
13878
  const scopedMatch = receiver.match(/^(\w+)::(\w+)\(.*\)$/);
@@ -24415,6 +24429,25 @@ var JS_XSS_SANITIZERS = [
24415
24429
  /\bbypassSecurityTrust/
24416
24430
  // Angular
24417
24431
  ];
24432
+ var JS_LOG_INJECTION_SANITIZERS = [
24433
+ /\bstripCrlf\s*\(/,
24434
+ /\bstripCRLF\s*\(/,
24435
+ /\bremoveNewlines\s*\(/,
24436
+ /\bsanitizeLogValue\s*\(/,
24437
+ // Inline CRLF-stripping regex literal: .replace(/[\r\n]/g, '')
24438
+ /\.replace\(\s*\/\[\s*\\r\s*\\n\s*\]\/[gimsu]*\s*,\s*['"`]['"`]\s*\)/,
24439
+ // Inline CRLF sequence: .replace(/\r\n/g, '')
24440
+ /\.replace\(\s*\/\\r\\n\/[gimsu]*\s*,\s*['"`]['"`]\s*\)/
24441
+ ];
24442
+ var PY_BUILTIN_LDAP_SANITIZERS = ["escape_filter_chars", "filter_format"];
24443
+ var PY_LDAP_METACHARS = ["(", ")", "=", "*", "\\"];
24444
+ var PY_DEF_RE = /^\s*def\s+([A-Za-z_]\w*)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*:\s*$/;
24445
+ var PY_LDAP_STRIP_RETURN_RE = /^\s*return\s+re\.sub\(\s*r?["']\[([^"'\]]+)\]["']\s*,\s*r?["']["']\s*,\s*([A-Za-z_]\w*)\s*\)\s*$/;
24446
+ var PY_XML_PARSER_HARDENED_RE = /\bXMLParser\s*\([^)]*\bresolve_entities\s*=\s*False\b[^)]*\)/;
24447
+ var PY_SQL_EXEC_METHODS = /* @__PURE__ */ new Set(["execute", "executemany"]);
24448
+ var PY_INLINE_FULLMATCH_RE = /\bre\s*\.\s*fullmatch\s*\(\s*r?"((?:[^"\\]|\\.)*)"/g;
24449
+ var PY_INLINE_MATCH_ANCHORED_RE = /\bre\s*\.\s*match\s*\(\s*r?"\^((?:[^"\\]|\\.)*)\$"/g;
24450
+ var PY_BIND_PLACEHOLDER_RE = /\?|%s|:[A-Za-z_]\w*/;
24418
24451
  var DATA_PARSER_TYPES = /* @__PURE__ */ new Set([
24419
24452
  "Parser",
24420
24453
  // commonmark, airline, picocli, jcommander
@@ -25082,19 +25115,22 @@ var SinkFilterPass = class {
25082
25115
  const callArgs = extractJavaCallArgs(method, sinkLineText);
25083
25116
  if (!callArgs || callArgs.length === 0) return true;
25084
25117
  const sqlVar = callArgs[0]?.trim() ?? "";
25085
- if (!/^[A-Za-z_]\w*$/.test(sqlVar)) return true;
25086
- const lo = Math.max(0, sink.line - 31);
25087
- const assignRe = new RegExp(
25088
- JAVA_SQL_ASSIGN_RE_TEMPLATE.replace("SQLVAR", sqlVar)
25089
- );
25090
25118
  let rhs = null;
25091
- for (let i2 = sink.line - 2; i2 >= lo; i2--) {
25092
- const ln = sourceLines[i2] ?? "";
25093
- const m = ln.match(assignRe);
25094
- if (m) {
25095
- rhs = m[1] ?? null;
25096
- break;
25119
+ if (/^[A-Za-z_]\w*$/.test(sqlVar)) {
25120
+ const lo = Math.max(0, sink.line - 31);
25121
+ const assignRe = new RegExp(
25122
+ JAVA_SQL_ASSIGN_RE_TEMPLATE.replace("SQLVAR", sqlVar)
25123
+ );
25124
+ for (let i2 = sink.line - 2; i2 >= lo; i2--) {
25125
+ const ln = sourceLines[i2] ?? "";
25126
+ const m = ln.match(assignRe);
25127
+ if (m) {
25128
+ rhs = m[1] ?? null;
25129
+ break;
25130
+ }
25097
25131
  }
25132
+ } else if (/"[^"]*"/.test(sqlVar) && sqlVar.includes("+")) {
25133
+ rhs = sqlVar;
25098
25134
  }
25099
25135
  if (!rhs) return true;
25100
25136
  const tokens = splitJavaConcatTokens(rhs);
@@ -25122,9 +25158,329 @@ var SinkFilterPass = class {
25122
25158
  return true;
25123
25159
  });
25124
25160
  }
25161
+ if (["javascript", "typescript"].includes(language)) {
25162
+ const sourceLines = ctx.code.split("\n");
25163
+ filtered = filtered.filter((sink) => {
25164
+ if (sink.type !== "log_injection") return true;
25165
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
25166
+ if (JS_LOG_INJECTION_SANITIZERS.some((p) => p.test(sinkLineText))) return false;
25167
+ const varNames = extractSourceIdentifiers(sinkLineText);
25168
+ for (const v of varNames) {
25169
+ if (isAssignedFromSanitizerPattern(sourceLines, sink.line, v, JS_LOG_INJECTION_SANITIZERS)) {
25170
+ return false;
25171
+ }
25172
+ }
25173
+ return true;
25174
+ });
25175
+ }
25176
+ if (language === "python") {
25177
+ const sourceLines = ctx.code.split("\n");
25178
+ const wrappers = findPythonLdapStripWrappers(sourceLines);
25179
+ const allLdapSanitizers = [...PY_BUILTIN_LDAP_SANITIZERS, ...wrappers];
25180
+ if (allLdapSanitizers.length > 0) {
25181
+ const ldapPatterns = allLdapSanitizers.map(
25182
+ (name2) => new RegExp(`\\b${escapeRegex(name2)}\\s*\\(`)
25183
+ );
25184
+ filtered = filtered.filter((sink) => {
25185
+ if (sink.type !== "ldap_injection") return true;
25186
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
25187
+ if (ldapPatterns.some((p) => p.test(sinkLineText))) return false;
25188
+ const varNames = extractSourceIdentifiers(sinkLineText);
25189
+ for (const v of varNames) {
25190
+ if (isAssignedFromSanitizerPattern(sourceLines, sink.line, v, ldapPatterns)) {
25191
+ return false;
25192
+ }
25193
+ }
25194
+ return true;
25195
+ });
25196
+ }
25197
+ }
25198
+ if (language === "python") {
25199
+ const sourceLines = ctx.code.split("\n");
25200
+ filtered = filtered.filter((sink) => {
25201
+ if (sink.type !== "xxe") return true;
25202
+ const lookback = 30;
25203
+ const lo = Math.max(0, sink.line - 1 - lookback);
25204
+ for (let i2 = sink.line - 2; i2 >= lo; i2--) {
25205
+ const ln = sourceLines[i2] ?? "";
25206
+ if (PY_XML_PARSER_HARDENED_RE.test(ln)) return false;
25207
+ if (/^\s*def\s+\w+\s*\(/.test(ln)) break;
25208
+ }
25209
+ return true;
25210
+ });
25211
+ }
25212
+ if (language === "python") {
25213
+ const sourceLines = ctx.code.split("\n");
25214
+ filtered = filtered.filter((sink) => {
25215
+ if (sink.type !== "sql_injection") return true;
25216
+ const method = sink.method ?? "";
25217
+ if (!PY_SQL_EXEC_METHODS.has(method)) return true;
25218
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
25219
+ const fstrMatch = sinkLineText.match(/\bf"((?:[^"\\]|\\.)*)"|\bf'((?:[^'\\]|\\.)*)'/);
25220
+ if (!fstrMatch) return true;
25221
+ const fstrBody = fstrMatch[1] ?? fstrMatch[2] ?? "";
25222
+ const { literals, interps } = splitPyFstringTokens(fstrBody);
25223
+ if (interps.length === 0) return true;
25224
+ if (!literals.some((lit) => PY_BIND_PLACEHOLDER_RE.test(lit))) return true;
25225
+ let sawHelperWithGuard = false;
25226
+ for (const interp of interps) {
25227
+ const t = interp.trim();
25228
+ if (t === "") return true;
25229
+ if (/^-?\d+$/.test(t) || /^"(?:[^"\\]|\\.)*"$/.test(t) || /^'(?:[^'\\]|\\.)*'$/.test(t)) {
25230
+ continue;
25231
+ }
25232
+ let helperName = null;
25233
+ const directCall = t.match(/^([A-Za-z_]\w*)\s*\(/);
25234
+ if (directCall) {
25235
+ helperName = directCall[1] ?? null;
25236
+ } else if (/^[A-Za-z_]\w*$/.test(t)) {
25237
+ helperName = findPythonAssignedHelperCall(t, sink.line, sourceLines);
25238
+ if (helperName === null) return true;
25239
+ } else {
25240
+ return true;
25241
+ }
25242
+ const body2 = findPythonFunctionBody(helperName, sourceLines);
25243
+ if (body2 && pythonBodyHasInlineRegexAllowlistRaise(body2)) {
25244
+ sawHelperWithGuard = true;
25245
+ }
25246
+ }
25247
+ if (!sawHelperWithGuard) return true;
25248
+ return false;
25249
+ });
25250
+ }
25125
25251
  return { sources, sinks: filtered, sanitizers };
25126
25252
  }
25127
25253
  };
25254
+ function escapeRegex(s) {
25255
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25256
+ }
25257
+ var RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
25258
+ // JS/TS
25259
+ "if",
25260
+ "else",
25261
+ "return",
25262
+ "const",
25263
+ "let",
25264
+ "var",
25265
+ "function",
25266
+ "class",
25267
+ "new",
25268
+ "this",
25269
+ "for",
25270
+ "while",
25271
+ "do",
25272
+ "break",
25273
+ "continue",
25274
+ "switch",
25275
+ "case",
25276
+ "default",
25277
+ "try",
25278
+ "catch",
25279
+ "finally",
25280
+ "throw",
25281
+ "typeof",
25282
+ "instanceof",
25283
+ "in",
25284
+ "of",
25285
+ "await",
25286
+ "async",
25287
+ "import",
25288
+ "export",
25289
+ "from",
25290
+ "as",
25291
+ "void",
25292
+ "null",
25293
+ "undefined",
25294
+ "true",
25295
+ "false",
25296
+ "super",
25297
+ "extends",
25298
+ "static",
25299
+ // Python
25300
+ "def",
25301
+ "lambda",
25302
+ "yield",
25303
+ "pass",
25304
+ "with",
25305
+ "global",
25306
+ "nonlocal",
25307
+ "and",
25308
+ "or",
25309
+ "not",
25310
+ "is",
25311
+ "None",
25312
+ "True",
25313
+ "False",
25314
+ "raise",
25315
+ "except",
25316
+ "elif",
25317
+ "self",
25318
+ "cls",
25319
+ "print",
25320
+ // Common stdlib roots that show up everywhere and would balloon scans.
25321
+ "console",
25322
+ "req",
25323
+ "res",
25324
+ "request",
25325
+ "response"
25326
+ ]);
25327
+ function extractSourceIdentifiers(text) {
25328
+ const out2 = [];
25329
+ const re = /\b([A-Za-z_]\w+)\b/g;
25330
+ let m;
25331
+ while ((m = re.exec(text)) !== null) {
25332
+ const name2 = m[1];
25333
+ if (!RESERVED_KEYWORDS.has(name2)) out2.push(name2);
25334
+ }
25335
+ return out2;
25336
+ }
25337
+ function isAssignedFromSanitizerPattern(sourceLines, sinkLine, varName, sanitizerPatterns, lookback = 30) {
25338
+ const escapedVar = escapeRegex(varName);
25339
+ const assignRe = new RegExp(
25340
+ `(?:\\b(?:const|let|var|final|String)\\s+)?\\b${escapedVar}\\s*=\\s*(.+)`
25341
+ );
25342
+ const fnBoundaryRe = /^\s*(?:def\s+\w+\s*\(|function\s+\w+\s*\(|\w+\s*[:=]\s*(?:async\s+)?\(?[^)]*\)?\s*=>)/;
25343
+ const lo = Math.max(0, sinkLine - 1 - lookback);
25344
+ for (let i2 = sinkLine - 2; i2 >= lo; i2--) {
25345
+ const ln = sourceLines[i2] ?? "";
25346
+ const m = ln.match(assignRe);
25347
+ if (m) {
25348
+ const rhs = (m[1] ?? "").trim();
25349
+ return sanitizerPatterns.some((p) => p.test(rhs));
25350
+ }
25351
+ if (fnBoundaryRe.test(ln)) break;
25352
+ }
25353
+ return false;
25354
+ }
25355
+ function findPythonLdapStripWrappers(sourceLines) {
25356
+ const out2 = [];
25357
+ for (let i2 = 0; i2 < sourceLines.length; i2++) {
25358
+ const defMatch = sourceLines[i2].match(PY_DEF_RE);
25359
+ if (!defMatch) continue;
25360
+ const fnName = defMatch[1];
25361
+ const param = defMatch[2];
25362
+ for (let j = i2 + 1; j < Math.min(i2 + 4, sourceLines.length); j++) {
25363
+ const body2 = sourceLines[j];
25364
+ if (/^\s*$/.test(body2) || /^\s*#/.test(body2)) continue;
25365
+ const ret = body2.match(PY_LDAP_STRIP_RETURN_RE);
25366
+ if (!ret) break;
25367
+ const charClass = ret[1];
25368
+ const argName = ret[2];
25369
+ if (argName !== param) break;
25370
+ const metaCount = PY_LDAP_METACHARS.filter((c) => charClass.includes(c)).length;
25371
+ if (metaCount >= 3) out2.push(fnName);
25372
+ break;
25373
+ }
25374
+ }
25375
+ return out2;
25376
+ }
25377
+ function splitPyFstringTokens(body2) {
25378
+ const literals = [];
25379
+ const interps = [];
25380
+ let i2 = 0;
25381
+ let cur = "";
25382
+ while (i2 < body2.length) {
25383
+ const ch = body2[i2] ?? "";
25384
+ if (ch === "{" && body2[i2 + 1] === "{") {
25385
+ cur += "{";
25386
+ i2 += 2;
25387
+ continue;
25388
+ }
25389
+ if (ch === "}" && body2[i2 + 1] === "}") {
25390
+ cur += "}";
25391
+ i2 += 2;
25392
+ continue;
25393
+ }
25394
+ if (ch === "{") {
25395
+ literals.push(cur);
25396
+ cur = "";
25397
+ i2++;
25398
+ let depth = 1;
25399
+ let expr = "";
25400
+ while (i2 < body2.length && depth > 0) {
25401
+ const ch2 = body2[i2] ?? "";
25402
+ if (ch2 === "{") {
25403
+ depth++;
25404
+ expr += ch2;
25405
+ i2++;
25406
+ continue;
25407
+ }
25408
+ if (ch2 === "}") {
25409
+ depth--;
25410
+ if (depth === 0) {
25411
+ i2++;
25412
+ break;
25413
+ }
25414
+ expr += ch2;
25415
+ i2++;
25416
+ continue;
25417
+ }
25418
+ expr += ch2;
25419
+ i2++;
25420
+ }
25421
+ const colonIdx = expr.indexOf(":");
25422
+ const interp = colonIdx >= 0 ? expr.substring(0, colonIdx) : expr;
25423
+ interps.push(interp);
25424
+ continue;
25425
+ }
25426
+ cur += ch;
25427
+ i2++;
25428
+ }
25429
+ literals.push(cur);
25430
+ return { literals, interps };
25431
+ }
25432
+ function findPythonAssignedHelperCall(varName, sinkLine, sourceLines) {
25433
+ if (!/^[A-Za-z_]\w*$/.test(varName)) return null;
25434
+ const lo = Math.max(0, sinkLine - 31);
25435
+ const assignRe = new RegExp(
25436
+ `^\\s*${varName}\\s*=\\s*([A-Za-z_]\\w*)\\s*\\(`
25437
+ );
25438
+ for (let i2 = sinkLine - 2; i2 >= lo; i2--) {
25439
+ const ln = sourceLines[i2] ?? "";
25440
+ const m = ln.match(assignRe);
25441
+ if (m) return m[1] ?? null;
25442
+ }
25443
+ return null;
25444
+ }
25445
+ function findPythonFunctionBody(funcName, sourceLines) {
25446
+ if (!/^[A-Za-z_]\w*$/.test(funcName)) return null;
25447
+ const sigRe = new RegExp(`^(\\s*)def\\s+${funcName}\\s*\\(`);
25448
+ for (let i2 = 0; i2 < sourceLines.length; i2++) {
25449
+ const ln = sourceLines[i2] ?? "";
25450
+ const m = ln.match(sigRe);
25451
+ if (!m) continue;
25452
+ const baseIndent = (m[1] ?? "").length;
25453
+ const body2 = [];
25454
+ for (let j = i2 + 1; j < sourceLines.length; j++) {
25455
+ const ln2 = sourceLines[j] ?? "";
25456
+ if (ln2.trim() === "") {
25457
+ body2.push(ln2);
25458
+ continue;
25459
+ }
25460
+ const indentMatch = ln2.match(/^(\s*)/);
25461
+ const indent = (indentMatch?.[1] ?? "").length;
25462
+ if (indent <= baseIndent) break;
25463
+ body2.push(ln2);
25464
+ }
25465
+ return body2;
25466
+ }
25467
+ return null;
25468
+ }
25469
+ function pythonBodyHasInlineRegexAllowlistRaise(bodyLines) {
25470
+ const stripped = bodyLines.map((ln) => ln.replace(/#.*$/, ""));
25471
+ const text = stripped.join("\n");
25472
+ if (!/\braise\s+/.test(text)) return false;
25473
+ PY_INLINE_FULLMATCH_RE.lastIndex = 0;
25474
+ let m;
25475
+ while ((m = PY_INLINE_FULLMATCH_RE.exec(text)) !== null) {
25476
+ if (isImplicitlyAnchoredAllowlistRegex(m[1] ?? "")) return true;
25477
+ }
25478
+ PY_INLINE_MATCH_ANCHORED_RE.lastIndex = 0;
25479
+ while ((m = PY_INLINE_MATCH_ANCHORED_RE.exec(text)) !== null) {
25480
+ if (isImplicitlyAnchoredAllowlistRegex(m[1] ?? "")) return true;
25481
+ }
25482
+ return false;
25483
+ }
25128
25484
  function evalArithmetic(input) {
25129
25485
  let pos = 0;
25130
25486
  const len = input.length;
@@ -27168,7 +27524,7 @@ var NullDerefPass = class {
27168
27524
  };
27169
27525
 
27170
27526
  // src/analysis/passes/resource-leak-pass.ts
27171
- function escapeRegex(s) {
27527
+ function escapeRegex2(s) {
27172
27528
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27173
27529
  }
27174
27530
  var FACTORY_METHOD_NAME_RE = /^(?:open|create|new|get|make|build)[A-Z]/;
@@ -27331,7 +27687,7 @@ var ResourceLeakPass = class {
27331
27687
  * ownership to the caller (caller is responsible for close).
27332
27688
  */
27333
27689
  isReturnedToCaller(lines, variable, fromLine, toLine) {
27334
- const varRe = new RegExp(`\\breturn\\b[^;]*\\b${escapeRegex(variable)}\\b`);
27690
+ const varRe = new RegExp(`\\breturn\\b[^;]*\\b${escapeRegex2(variable)}\\b`);
27335
27691
  for (let l = fromLine; l <= toLine && l <= lines.length; l++) {
27336
27692
  if (varRe.test(lines[l - 1] ?? "")) return true;
27337
27693
  }
@@ -27344,7 +27700,7 @@ var ResourceLeakPass = class {
27344
27700
  */
27345
27701
  fieldStoredName(lines, variable, openLine, toLine) {
27346
27702
  const thisAssignRe = new RegExp(
27347
- `\\bthis\\s*\\.\\s*(\\w+)\\s*=\\s*${escapeRegex(variable)}\\b`
27703
+ `\\bthis\\s*\\.\\s*(\\w+)\\s*=\\s*${escapeRegex2(variable)}\\b`
27348
27704
  );
27349
27705
  for (let l = openLine; l <= toLine && l <= lines.length; l++) {
27350
27706
  const m = thisAssignRe.exec(lines[l - 1] ?? "");
@@ -11577,21 +11577,32 @@ var DEFAULT_SINKS = [
11577
11577
  { method: "from_string", class: "Template", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11578
11578
  { method: "Template", class: "jinja2", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11579
11579
  { method: "Template", class: "mako", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11580
- // Python Log Injection
11581
- { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11582
- { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11583
- { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11584
- { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11585
- { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11580
+ // Python Log Injection — cognium-dev #193: positions [0, 1, 2, 3, 4]
11581
+ // cover both the format string (arg 0) and Python `logging`'s positional
11582
+ // *args (1..N), which get rendered into the log line via `%` substitution
11583
+ // (e.g. `log.warning("user=%s", user)` taints via arg 1). Five-arg cap
11584
+ // matches the established explicit-enumeration pattern used elsewhere in
11585
+ // this file.
11586
+ { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11587
+ { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11588
+ { method: "warn", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11589
+ { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11590
+ { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11591
+ { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11592
+ { method: "fatal", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11593
+ { method: "exception", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11594
+ { method: "log", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11586
11595
  // Python `logging` module top-level functions (e.g. logging.info(...))
11587
11596
  // — cognium-dev#104 Sprint 22: OOP fixtures use `import logging; logging.info(self.msg)`.
11588
- { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11589
- { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11590
- { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11591
- { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11592
- { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11593
- { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1] },
11594
- { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11597
+ { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11598
+ { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11599
+ { method: "warn", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11600
+ { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11601
+ { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11602
+ { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11603
+ { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11604
+ { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11605
+ { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11595
11606
  // =========================================================================
11596
11607
  // Java CWE-Bench Enhancement Patterns (Collection/Builder)
11597
11608
  // =========================================================================
@@ -13154,6 +13165,9 @@ function receiverMightBeClass(receiver, className) {
13154
13165
  return true;
13155
13166
  }
13156
13167
  }
13168
+ if (className === "Logger" && /\.getLogger\(.*\)$/.test(receiver)) {
13169
+ return true;
13170
+ }
13157
13171
  }
13158
13172
  if (receiver.includes("::") && receiver.endsWith(")")) {
13159
13173
  const scopedMatch = receiver.match(/^(\w+)::(\w+)\(.*\)$/);
@@ -11511,21 +11511,32 @@ var DEFAULT_SINKS = [
11511
11511
  { method: "from_string", class: "Template", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11512
11512
  { method: "Template", class: "jinja2", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11513
11513
  { method: "Template", class: "mako", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
11514
- // Python Log Injection
11515
- { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11516
- { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11517
- { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11518
- { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11519
- { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11514
+ // Python Log Injection — cognium-dev #193: positions [0, 1, 2, 3, 4]
11515
+ // cover both the format string (arg 0) and Python `logging`'s positional
11516
+ // *args (1..N), which get rendered into the log line via `%` substitution
11517
+ // (e.g. `log.warning("user=%s", user)` taints via arg 1). Five-arg cap
11518
+ // matches the established explicit-enumeration pattern used elsewhere in
11519
+ // this file.
11520
+ { method: "info", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11521
+ { method: "warning", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11522
+ { method: "warn", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11523
+ { method: "error", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11524
+ { method: "debug", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11525
+ { method: "critical", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11526
+ { method: "fatal", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11527
+ { method: "exception", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11528
+ { method: "log", class: "logger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11520
11529
  // Python `logging` module top-level functions (e.g. logging.info(...))
11521
11530
  // — cognium-dev#104 Sprint 22: OOP fixtures use `import logging; logging.info(self.msg)`.
11522
- { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11523
- { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11524
- { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11525
- { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11526
- { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11527
- { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1] },
11528
- { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0] },
11531
+ { method: "info", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11532
+ { method: "warning", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11533
+ { method: "warn", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11534
+ { method: "error", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11535
+ { method: "debug", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11536
+ { method: "critical", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11537
+ { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11538
+ { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
11539
+ { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
11529
11540
  // =========================================================================
11530
11541
  // Java CWE-Bench Enhancement Patterns (Collection/Builder)
11531
11542
  // =========================================================================
@@ -13088,6 +13099,9 @@ function receiverMightBeClass(receiver, className) {
13088
13099
  return true;
13089
13100
  }
13090
13101
  }
13102
+ if (className === "Logger" && /\.getLogger\(.*\)$/.test(receiver)) {
13103
+ return true;
13104
+ }
13091
13105
  }
13092
13106
  if (receiver.includes("::") && receiver.endsWith(")")) {
13093
13107
  const scopedMatch = receiver.match(/^(\w+)::(\w+)\(.*\)$/);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.107.0",
3
+ "version": "3.110.0",
4
4
  "description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",