cognium-dev 4.9.16 → 4.9.19

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 +943 -209
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -3232,7 +3232,7 @@ var Query = class {
3232
3232
  }
3233
3233
  };
3234
3234
 
3235
- // node_modules/circle-ir/dist/core/parser.js
3235
+ // ../circle-ir/dist/core/parser.js
3236
3236
  var nodeModules = null;
3237
3237
  var moduleDir = null;
3238
3238
  async function getNodeModules() {
@@ -3472,7 +3472,7 @@ async function getDefaultLanguagePath(language) {
3472
3472
  }
3473
3473
  return `wasm/tree-sitter-${language}.wasm`;
3474
3474
  }
3475
- // node_modules/circle-ir/dist/core/extractors/meta.js
3475
+ // ../circle-ir/dist/core/extractors/meta.js
3476
3476
  function extractMeta(code, tree, filePath, language) {
3477
3477
  const loc = countLinesOfCode(code);
3478
3478
  const hash = computeHash(code);
@@ -3554,7 +3554,7 @@ function extractPackage(tree, language) {
3554
3554
  }
3555
3555
  return null;
3556
3556
  }
3557
- // node_modules/circle-ir/dist/core/extractors/types.js
3557
+ // ../circle-ir/dist/core/extractors/types.js
3558
3558
  function detectLanguage(tree) {
3559
3559
  const root = tree.rootNode;
3560
3560
  const jsNodeTypes = new Set([
@@ -5222,7 +5222,7 @@ function findChildByType(node, type) {
5222
5222
  }
5223
5223
  return null;
5224
5224
  }
5225
- // node_modules/circle-ir/dist/core/extractors/calls.js
5225
+ // ../circle-ir/dist/core/extractors/calls.js
5226
5226
  function detectLanguageFromTree(tree, cache) {
5227
5227
  const rustStructs = getNodesFromCache(tree.rootNode, "struct_item", cache);
5228
5228
  const rustImpls = getNodesFromCache(tree.rootNode, "impl_item", cache);
@@ -6055,6 +6055,14 @@ function buildResolutionContext(tree, cache) {
6055
6055
  }
6056
6056
  }
6057
6057
  }
6058
+ const resourceDecls = getNodesFromCache(tree.rootNode, "resource", cache);
6059
+ for (const res of resourceDecls) {
6060
+ const typeNode = res.childForFieldName("type");
6061
+ const nameNode = res.childForFieldName("name");
6062
+ if (typeNode && nameNode) {
6063
+ context.localVarTypes.set(getNodeText(nameNode), getNodeText(typeNode));
6064
+ }
6065
+ }
6058
6066
  const imports = getNodesFromCache(tree.rootNode, "import_declaration", cache);
6059
6067
  for (const imp of imports) {
6060
6068
  const text = getNodeText(imp);
@@ -7336,7 +7344,7 @@ function extractGoTypeLastSegment(typeNode) {
7336
7344
  }
7337
7345
  return null;
7338
7346
  }
7339
- // node_modules/circle-ir/dist/core/extractors/imports.js
7347
+ // ../circle-ir/dist/core/extractors/imports.js
7340
7348
  function detectLanguage2(tree) {
7341
7349
  const root = tree.rootNode;
7342
7350
  const jsNodeTypes = new Set([
@@ -8023,7 +8031,7 @@ function findGoChildByType(node, type) {
8023
8031
  }
8024
8032
  return null;
8025
8033
  }
8026
- // node_modules/circle-ir/dist/core/extractors/exports.js
8034
+ // ../circle-ir/dist/core/extractors/exports.js
8027
8035
  function extractExports(types) {
8028
8036
  const exports = [];
8029
8037
  for (const type of types) {
@@ -8076,7 +8084,7 @@ function getVisibilityFromModifiers(modifiers) {
8076
8084
  }
8077
8085
  return "package";
8078
8086
  }
8079
- // node_modules/circle-ir/dist/core/extractors/cfg.js
8087
+ // ../circle-ir/dist/core/extractors/cfg.js
8080
8088
  function detectLanguage3(tree) {
8081
8089
  const root = tree.rootNode;
8082
8090
  const jsNodeTypes = new Set([
@@ -8725,7 +8733,7 @@ function isGoStatement(node) {
8725
8733
  ]);
8726
8734
  return goStatementTypes.has(node.type);
8727
8735
  }
8728
- // node_modules/circle-ir/dist/core/extractors/dfg.js
8736
+ // ../circle-ir/dist/core/extractors/dfg.js
8729
8737
  function detectLanguage4(tree) {
8730
8738
  const root = tree.rootNode;
8731
8739
  const jsNodeTypes = new Set([
@@ -8796,6 +8804,7 @@ function buildCSharpDFG(tree, cache) {
8796
8804
  ];
8797
8805
  for (const method of methods) {
8798
8806
  scopeStack.push(new Map);
8807
+ const methodDefStart = defs.length;
8799
8808
  const params = method.childForFieldName("parameters");
8800
8809
  if (params) {
8801
8810
  for (const def of extractParameterDefs(params, defIdCounter)) {
@@ -8825,6 +8834,7 @@ function buildCSharpDFG(tree, cache) {
8825
8834
  }
8826
8835
  }
8827
8836
  const bodyUses = extractUses(body2, useIdCounter, scopeStack, false);
8837
+ resolveReassignedUses(defs.slice(methodDefStart), bodyUses.uses);
8828
8838
  uses.push(...bodyUses.uses);
8829
8839
  useIdCounter = bodyUses.nextId;
8830
8840
  }
@@ -9547,6 +9557,38 @@ function findReachingDef(name2, scopeStack) {
9547
9557
  }
9548
9558
  return null;
9549
9559
  }
9560
+ function resolveReassignedUses(methodDefs, methodUses) {
9561
+ const defsByVar = new Map;
9562
+ for (const def of methodDefs) {
9563
+ if (def.kind !== "local" && def.kind !== "param")
9564
+ continue;
9565
+ const existing = defsByVar.get(def.variable);
9566
+ if (existing)
9567
+ existing.push(def);
9568
+ else
9569
+ defsByVar.set(def.variable, [def]);
9570
+ }
9571
+ for (const list of defsByVar.values()) {
9572
+ if (list.length > 1)
9573
+ list.sort((a, b) => a.line - b.line || a.id - b.id);
9574
+ }
9575
+ for (const use of methodUses) {
9576
+ const list = defsByVar.get(use.variable);
9577
+ if (!list || list.length < 2)
9578
+ continue;
9579
+ let strictlyBefore;
9580
+ let sameLine;
9581
+ for (const def of list) {
9582
+ if (def.line < use.line)
9583
+ strictlyBefore = def;
9584
+ else if (def.line === use.line)
9585
+ sameLine = def;
9586
+ }
9587
+ const reaching = strictlyBefore ?? sameLine;
9588
+ if (reaching)
9589
+ use.def_id = reaching.id;
9590
+ }
9591
+ }
9550
9592
  function computeChains(defs, uses) {
9551
9593
  const chains = [];
9552
9594
  const defById = new Map;
@@ -10246,7 +10288,7 @@ function extractGoAddressableVarName(node) {
10246
10288
  }
10247
10289
  return null;
10248
10290
  }
10249
- // node_modules/circle-ir/dist/core/extractors/runtime-registrations.js
10291
+ // ../circle-ir/dist/core/extractors/runtime-registrations.js
10250
10292
  var HTTP_VERB_METHODS = new Set([
10251
10293
  "get",
10252
10294
  "post",
@@ -10994,7 +11036,7 @@ function parseDistributedSliceAttribute(attrItem) {
10994
11036
  }
10995
11037
  };
10996
11038
  }
10997
- // node_modules/circle-ir/dist/analysis/config-loader.js
11039
+ // ../circle-ir/dist/analysis/config-loader.js
10998
11040
  var DEFAULT_SOURCES = [
10999
11041
  { method: "getParameter", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
11000
11042
  { method: "getParameterValues", class: "HttpServletRequest", type: "http_param", severity: "high", return_tainted: true },
@@ -11533,8 +11575,8 @@ var DEFAULT_SINKS = [
11533
11575
  { method: "selectByExample", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
11534
11576
  { method: "exec", class: "Runtime", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
11535
11577
  { method: "start", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
11536
- { method: "ProcessBuilder", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11537
- { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11578
+ { method: "ProcessBuilder", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1, 2, 3, 4, 5, 6, 7] },
11579
+ { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1, 2, 3, 4, 5, 6, 7] },
11538
11580
  { method: "execute", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11539
11581
  { method: "CommandLine", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11540
11582
  { method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
@@ -12335,7 +12377,6 @@ var DEFAULT_SINKS = [
12335
12377
  { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12336
12378
  { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
12337
12379
  { method: "exception", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12338
- { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
12339
12380
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
12340
12381
  { method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
12341
12382
  { method: "evaluate", class: "Script", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
@@ -13097,7 +13138,33 @@ var DEFAULT_HEADER_RULES = [
13097
13138
  note: "Fires when the value is not a string literal (likely reflected from request)"
13098
13139
  }
13099
13140
  ];
13100
- // node_modules/circle-ir/dist/analysis/arg-type-resolver.js
13141
+ // ../circle-ir/dist/analysis/literal-expression.js
13142
+ function isProvablyLiteralExpression(expr) {
13143
+ let e = (expr ?? "").trim();
13144
+ if (e.length === 0)
13145
+ return false;
13146
+ e = e.replace(/\/\/.*$/, "").replace(/;\s*$/, "").trim();
13147
+ if (e.length === 0)
13148
+ return false;
13149
+ if (e.includes("${"))
13150
+ return false;
13151
+ if (/[+]\s*$/.test(e))
13152
+ return false;
13153
+ const stripped = e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\$]|\\.)*`/g, "");
13154
+ if (/["'`]/.test(stripped))
13155
+ return false;
13156
+ return /^[\s+()]*$/.test(stripped);
13157
+ }
13158
+ var DOM_ASSIGNMENT_SINK_METHODS = new Set([
13159
+ "innerHTML",
13160
+ "outerHTML",
13161
+ "cssText",
13162
+ "src",
13163
+ "href",
13164
+ "textContent"
13165
+ ]);
13166
+
13167
+ // ../circle-ir/dist/analysis/arg-type-resolver.js
13101
13168
  function findEnclosingType(callInMethod, types) {
13102
13169
  if (!callInMethod)
13103
13170
  return null;
@@ -13175,7 +13242,7 @@ function isArgvContainerType(rawType) {
13175
13242
  return /^(?:List|ArrayList|LinkedList|Collection|Iterable|Deque|Queue)\s*<\s*(?:String|CharSequence|java\.lang\.String)\b/.test(s);
13176
13243
  }
13177
13244
 
13178
- // node_modules/circle-ir/dist/analysis/taint-matcher.js
13245
+ // ../circle-ir/dist/analysis/taint-matcher.js
13179
13246
  var PYTHON_TAINTED_PATTERNS = [
13180
13247
  { pattern: /\brequest\.args\b/, sourceType: "http_param" },
13181
13248
  { pattern: /\brequest\.form\b/, sourceType: "http_body" },
@@ -14050,7 +14117,7 @@ function isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines)
14050
14117
  if (!guards)
14051
14118
  continue;
14052
14119
  for (const g of guards) {
14053
- if (g.i >= sinkIdx || checked.has(g))
14120
+ if (g.i > sinkIdx || checked.has(g))
14054
14121
  continue;
14055
14122
  checked.add(g);
14056
14123
  const block = csThenBlock(sourceLines, g.i, g.rest);
@@ -14193,6 +14260,21 @@ function isRegexReceiver(receiver, sourceLines) {
14193
14260
  const decl = new RegExp(`(?:const|let|var)\\s+${escapeRe(r)}\\s*=\\s*(?:/|(?:new\\s+)?RegExp\\s*\\()`);
14194
14261
  return sourceLines.some((l) => decl.test(l));
14195
14262
  }
14263
+ function isDeclaredFunctionReference(arg, sourceLines) {
14264
+ if (arg.literal !== null && arg.literal !== undefined)
14265
+ return false;
14266
+ if (!sourceLines)
14267
+ return false;
14268
+ const name2 = (arg.expression ?? "").trim();
14269
+ if (!/^[A-Za-z_$][\w$]*$/.test(name2))
14270
+ return false;
14271
+ const n = escapeRe(name2);
14272
+ const decls = [
14273
+ new RegExp(`\\bfunction\\s+${n}\\s*\\(`),
14274
+ new RegExp(`\\b(?:const|let|var)\\s+${n}\\s*=\\s*(?:async\\s+)?(?:function\\b|\\(|[A-Za-z_$][\\w$]*\\s*=>)`)
14275
+ ];
14276
+ return sourceLines.some((l) => decls.some((re) => re.test(l)));
14277
+ }
14196
14278
  function isFunctionCallbackArgument(arg) {
14197
14279
  if (arg.literal !== null && arg.literal !== undefined)
14198
14280
  return false;
@@ -14396,11 +14478,17 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14396
14478
  }
14397
14479
  if (pattern.type === "code_injection" && (call.method_name === "setInterval" || call.method_name === "setTimeout" || call.method_name === "setImmediate")) {
14398
14480
  const firstArg = call.arguments.find((a) => a.position === 0);
14399
- if (firstArg && isFunctionCallbackArgument(firstArg)) {
14481
+ if (firstArg && (isFunctionCallbackArgument(firstArg) || isDeclaredFunctionReference(firstArg, sourceLines))) {
14400
14482
  continue;
14401
14483
  }
14402
14484
  }
14403
- if (pattern.type === "command_injection" && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
14485
+ if (pattern.type === "xss" && (language === "javascript" || language === "typescript") && DOM_ASSIGNMENT_SINK_METHODS.has(call.method_name) && call.arguments.length > 0) {
14486
+ const rhs = call.arguments.find((a) => a.position === 0);
14487
+ if (rhs && isProvablyLiteralExpression(rhs.expression ?? "")) {
14488
+ continue;
14489
+ }
14490
+ }
14491
+ if ((pattern.type === "command_injection" || pattern.type === "sql_injection") && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
14404
14492
  continue;
14405
14493
  }
14406
14494
  if (pattern.type === "code_injection" && language === "python" && call.method_name === "compile" && call.receiver && call.receiver !== "builtins") {
@@ -15216,7 +15304,7 @@ function formatSanitizerMethod(call) {
15216
15304
  return `${call.method_name}()`;
15217
15305
  }
15218
15306
 
15219
- // node_modules/circle-ir/dist/analysis/unresolved.js
15307
+ // ../circle-ir/dist/analysis/unresolved.js
15220
15308
  function detectUnresolved(calls, types, dfg) {
15221
15309
  const unresolved = [];
15222
15310
  unresolved.push(...detectVirtualDispatch(calls));
@@ -15380,7 +15468,7 @@ function formatCallCode(call) {
15380
15468
  }
15381
15469
  return `${call.method_name}(${args2})`;
15382
15470
  }
15383
- // node_modules/circle-ir/dist/analysis/non-executable-lines.js
15471
+ // ../circle-ir/dist/analysis/non-executable-lines.js
15384
15472
  function isNonExecutableSourceLine(sourceCode, line, language) {
15385
15473
  if (!sourceCode || line < 1)
15386
15474
  return false;
@@ -15440,7 +15528,7 @@ function isNonExecutablePython(trimmed) {
15440
15528
  return false;
15441
15529
  }
15442
15530
 
15443
- // node_modules/circle-ir/dist/analysis/sanitizer-index.js
15531
+ // ../circle-ir/dist/analysis/sanitizer-index.js
15444
15532
  var SANITIZER_SET_CACHE = new WeakMap;
15445
15533
  function getSanitizesSet(san) {
15446
15534
  let s = SANITIZER_SET_CACHE.get(san);
@@ -15454,7 +15542,7 @@ function sanitizerCoversSink(san, sinkType) {
15454
15542
  return getSanitizesSet(san).has(sinkType);
15455
15543
  }
15456
15544
 
15457
- // node_modules/circle-ir/dist/analysis/dfg-walk.js
15545
+ // ../circle-ir/dist/analysis/dfg-walk.js
15458
15546
  var walkBackwardDefsMemo = new WeakMap;
15459
15547
  function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15460
15548
  const maxHops = options.maxHops ?? 32;
@@ -15507,7 +15595,7 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15507
15595
  return result;
15508
15596
  }
15509
15597
 
15510
- // node_modules/circle-ir/dist/analysis/findings.js
15598
+ // ../circle-ir/dist/analysis/findings.js
15511
15599
  function canSourceReachSink(sourceType, sinkType) {
15512
15600
  const sourceToSinkMapping = {
15513
15601
  http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "xxe", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
@@ -15538,7 +15626,7 @@ function sourceSemanticsAllowed(source, sinkType) {
15538
15626
  return true;
15539
15627
  }
15540
15628
 
15541
- // node_modules/circle-ir/dist/analysis/findings-instrumentation.js
15629
+ // ../circle-ir/dist/analysis/findings-instrumentation.js
15542
15630
  var instrumentEnabled = false;
15543
15631
  function setFindingsInstrumentation(enabled) {
15544
15632
  instrumentEnabled = enabled;
@@ -15601,7 +15689,7 @@ function emitFindingsInstrumentation(filePath, findings, taint) {
15601
15689
  };
15602
15690
  console.error(`[findings-summary] ${JSON.stringify(summary)}`);
15603
15691
  }
15604
- // node_modules/circle-ir/dist/graph/code-graph.js
15692
+ // ../circle-ir/dist/graph/code-graph.js
15605
15693
  class CodeGraph {
15606
15694
  ir;
15607
15695
  constructor(ir) {
@@ -15811,7 +15899,7 @@ class CodeGraph {
15811
15899
  return result;
15812
15900
  }
15813
15901
  }
15814
- // node_modules/circle-ir/dist/resolution/type-hierarchy.js
15902
+ // ../circle-ir/dist/resolution/type-hierarchy.js
15815
15903
  class TypeHierarchyResolver {
15816
15904
  types = new Map;
15817
15905
  nameToFqn = new Map;
@@ -16383,7 +16471,7 @@ function registerCommonLibraries(resolver) {
16383
16471
  resolver.registerFactoryReturnType("HttpClients", "createSystem", "org.apache.http.impl.client.CloseableHttpClient");
16384
16472
  resolver.registerFactoryReturnType("HttpClients", "createMinimal", "org.apache.http.impl.client.MinimalHttpClient");
16385
16473
  }
16386
- // node_modules/circle-ir/dist/resolution/symbol-table.js
16474
+ // ../circle-ir/dist/resolution/symbol-table.js
16387
16475
  class SymbolTable {
16388
16476
  exports = new Map;
16389
16477
  nameToFqns = new Map;
@@ -16606,7 +16694,7 @@ class SymbolTable {
16606
16694
  return "package";
16607
16695
  }
16608
16696
  }
16609
- // node_modules/circle-ir/dist/resolution/cross-file.js
16697
+ // ../circle-ir/dist/resolution/cross-file.js
16610
16698
  function buildFileIndex(ir) {
16611
16699
  const callsByLine = new Map;
16612
16700
  for (const c of ir.calls) {
@@ -17104,10 +17192,12 @@ class CrossFileResolver {
17104
17192
  }
17105
17193
  return callers;
17106
17194
  }
17107
- findCrossFileTaintFlows() {
17195
+ findCrossFileTaintFlows(deadline) {
17108
17196
  const flows = [];
17109
17197
  const seen = new Set;
17110
17198
  for (const [filePath, ir] of this.fileIRs) {
17199
+ if (deadline?.())
17200
+ break;
17111
17201
  for (const source of ir.taint.sources) {
17112
17202
  if (source.type === "interprocedural_param")
17113
17203
  continue;
@@ -17182,11 +17272,13 @@ class CrossFileResolver {
17182
17272
  }
17183
17273
  return flows;
17184
17274
  }
17185
- findInterproceduralTaintPaths() {
17275
+ findInterproceduralTaintPaths(deadline) {
17186
17276
  const paths = [];
17187
17277
  const seen = new Set;
17188
17278
  const methodIndex = this.buildMethodIndex();
17189
17279
  for (const [callerFile, callerIR] of this.fileIRs) {
17280
+ if (deadline?.())
17281
+ break;
17190
17282
  const callerIdx = this.getFileIndex(callerIR);
17191
17283
  for (const type of callerIR.types) {
17192
17284
  for (const method of type.methods) {
@@ -17325,7 +17417,7 @@ class CrossFileResolver {
17325
17417
  }
17326
17418
  return paths;
17327
17419
  }
17328
- findFieldBindingTaintPaths() {
17420
+ findFieldBindingTaintPaths(deadline) {
17329
17421
  const paths = [];
17330
17422
  const seen = new Set;
17331
17423
  if (this.fieldTaintInfo.size === 0)
@@ -17333,6 +17425,8 @@ class CrossFileResolver {
17333
17425
  const fieldExprRe = /^(\w+)\.(\w+)$/;
17334
17426
  const methodIndex = this.buildMethodIndex();
17335
17427
  for (const [callerFile, callerIR] of this.fileIRs) {
17428
+ if (deadline?.())
17429
+ break;
17336
17430
  const callerIdx = this.getFileIndex(callerIR);
17337
17431
  for (const type of callerIR.types) {
17338
17432
  const callerTypeFqn = callerIR.meta.package ? `${callerIR.meta.package}.${type.name}` : type.name;
@@ -17694,7 +17788,7 @@ class CrossFileResolver {
17694
17788
  return this.fieldTaintInfo.get(`${typeFqn}.${fieldName}`);
17695
17789
  }
17696
17790
  }
17697
- // node_modules/circle-ir/dist/graph/project-graph.js
17791
+ // ../circle-ir/dist/graph/project-graph.js
17698
17792
  class ProjectGraph {
17699
17793
  files = new Map;
17700
17794
  _symbolTable = null;
@@ -17746,7 +17840,7 @@ class ProjectGraph {
17746
17840
  return this._resolver;
17747
17841
  }
17748
17842
  }
17749
- // node_modules/circle-ir/dist/graph/analysis-pass.js
17843
+ // ../circle-ir/dist/graph/analysis-pass.js
17750
17844
  class AnalysisPipeline {
17751
17845
  passes = [];
17752
17846
  add(pass) {
@@ -17793,7 +17887,7 @@ class AnalysisPipeline {
17793
17887
  return { results, findings };
17794
17888
  }
17795
17889
  }
17796
- // node_modules/circle-ir/dist/analysis/taint-propagation.js
17890
+ // ../circle-ir/dist/analysis/taint-propagation.js
17797
17891
  function buildSanitizersByLine(sanitizers) {
17798
17892
  const out2 = new Map;
17799
17893
  for (const san of sanitizers) {
@@ -18068,7 +18162,7 @@ function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
18068
18162
  function buildTaintFlow(source, sink, taintInfo) {
18069
18163
  const path = [];
18070
18164
  path.push({
18071
- variable: taintInfo.variable,
18165
+ variable: source.variable ?? taintInfo.variable,
18072
18166
  line: source.line,
18073
18167
  type: "source",
18074
18168
  description: `Tainted data enters via ${source.type}`
@@ -18095,7 +18189,7 @@ function buildTaintFlow(source, sink, taintInfo) {
18095
18189
  confidence: taintInfo.confidence * 0.9
18096
18190
  };
18097
18191
  }
18098
- // node_modules/circle-ir/dist/analysis/constant-propagation/ast-utils.js
18192
+ // ../circle-ir/dist/analysis/constant-propagation/ast-utils.js
18099
18193
  function isKnown(cv) {
18100
18194
  return cv.type !== "unknown";
18101
18195
  }
@@ -18112,7 +18206,7 @@ function getNodeLine(node) {
18112
18206
  return node.startPosition.row + 1;
18113
18207
  }
18114
18208
 
18115
- // node_modules/circle-ir/dist/analysis/constant-propagation/evaluator.js
18209
+ // ../circle-ir/dist/analysis/constant-propagation/evaluator.js
18116
18210
  class ExpressionEvaluator {
18117
18211
  source;
18118
18212
  getSymbol;
@@ -18382,7 +18476,7 @@ class ExpressionEvaluator {
18382
18476
  }
18383
18477
  }
18384
18478
 
18385
- // node_modules/circle-ir/dist/analysis/constant-propagation/patterns.js
18479
+ // ../circle-ir/dist/analysis/constant-propagation/patterns.js
18386
18480
  var TAINT_PATTERNS = [
18387
18481
  "request.getParameter",
18388
18482
  "request.getHeader",
@@ -18577,7 +18671,7 @@ var PROPAGATOR_METHODS = new Set([
18577
18671
  "decodeRequestString"
18578
18672
  ]);
18579
18673
 
18580
- // node_modules/circle-ir/dist/analysis/constant-propagation/propagator.js
18674
+ // ../circle-ir/dist/analysis/constant-propagation/propagator.js
18581
18675
  class ConstantPropagator {
18582
18676
  symbols = new Map;
18583
18677
  tainted = new Set;
@@ -20434,7 +20528,7 @@ class ConstantPropagator {
20434
20528
  }
20435
20529
  }
20436
20530
 
20437
- // node_modules/circle-ir/dist/analysis/constant-propagation/index.js
20531
+ // ../circle-ir/dist/analysis/constant-propagation/index.js
20438
20532
  function analyzeConstantPropagation(tree, sourceCode, options = {}) {
20439
20533
  const propagator = new ConstantPropagator;
20440
20534
  return propagator.analyze(tree, sourceCode, options.additionalTaintPatterns ?? [], options.sanitizerMethods ?? [], options.taintedParameters ?? []);
@@ -20522,7 +20616,7 @@ function normalizeCondition(cond) {
20522
20616
  }
20523
20617
  return normalized;
20524
20618
  }
20525
- // node_modules/circle-ir/dist/utils/logger.js
20619
+ // ../circle-ir/dist/utils/logger.js
20526
20620
  var LOG_LEVELS = {
20527
20621
  trace: 0,
20528
20622
  debug: 1,
@@ -20594,7 +20688,7 @@ var logger = {
20594
20688
  }
20595
20689
  };
20596
20690
 
20597
- // node_modules/circle-ir/dist/analysis/per-file-finding-cap.js
20691
+ // ../circle-ir/dist/analysis/per-file-finding-cap.js
20598
20692
  var DEFAULT_PER_FILE_FINDING_CAP = 1000;
20599
20693
  var SATURATED_FILE_RULE_ID = "saturated-file";
20600
20694
  function applyPerFileFindingCap(filePath, findings, cap) {
@@ -20630,7 +20724,7 @@ function applyPerFileFindingCap(filePath, findings, cap) {
20630
20724
  return [advisory];
20631
20725
  }
20632
20726
 
20633
- // node_modules/circle-ir/dist/analysis/confidence-filter.js
20727
+ // ../circle-ir/dist/analysis/confidence-filter.js
20634
20728
  function applyConfidenceFilter(findings, includeSpeculative) {
20635
20729
  if (includeSpeculative)
20636
20730
  return findings;
@@ -20640,7 +20734,7 @@ function isHighConfidence(finding) {
20640
20734
  return finding.confidence === undefined || finding.confidence === "high";
20641
20735
  }
20642
20736
 
20643
- // node_modules/circle-ir/dist/analysis/library-api-surface-downgrade.js
20737
+ // ../circle-ir/dist/analysis/library-api-surface-downgrade.js
20644
20738
  var LIBRARY_API_SURFACE_TAG = "library-api-surface:caller-responsibility";
20645
20739
  function applyLibraryApiSurfaceDowngrade(findings) {
20646
20740
  return findings.map((f) => {
@@ -20657,7 +20751,7 @@ function applyLibraryApiSurfaceDowngrade(findings) {
20657
20751
  });
20658
20752
  }
20659
20753
 
20660
- // node_modules/circle-ir/dist/analysis/note-coalescer.js
20754
+ // ../circle-ir/dist/analysis/note-coalescer.js
20661
20755
  var CLICKJACKING_PAIR = new Set(["missing-x-frame-options", "missing-csp-frame-ancestors"]);
20662
20756
  function levelRank(level) {
20663
20757
  return level === "error" ? 0 : level === "warning" ? 1 : 2;
@@ -20721,7 +20815,7 @@ function coalesceNoteLevelFindings(findings) {
20721
20815
  return out2;
20722
20816
  }
20723
20817
 
20724
- // node_modules/circle-ir/dist/analysis/entry-point-detection.js
20818
+ // ../circle-ir/dist/analysis/entry-point-detection.js
20725
20819
  var TIER_1_METHOD_ANNOTATIONS = new Set([
20726
20820
  "RequestMapping",
20727
20821
  "GetMapping",
@@ -21378,7 +21472,7 @@ function methodIsRuntimeRegistrationHandler(method, regs) {
21378
21472
  return false;
21379
21473
  }
21380
21474
 
21381
- // node_modules/circle-ir/dist/analysis/require-entry-path.js
21475
+ // ../circle-ir/dist/analysis/require-entry-path.js
21382
21476
  var RULE_ID_REQUIRE_ENTRY_PATH = "require-entry-path";
21383
21477
  var MAX_VISITED_METHODS = 2000;
21384
21478
  var TAINT_FLOW_RULE_IDS = new Set([
@@ -21767,7 +21861,7 @@ function normalizeDisabled(input) {
21767
21861
  return new Set(input);
21768
21862
  }
21769
21863
 
21770
- // node_modules/circle-ir/dist/analysis/project-profile-transform.js
21864
+ // ../circle-ir/dist/analysis/project-profile-transform.js
21771
21865
  var DOWNGRADE_ELIGIBLE_RULE_IDS = new Set([
21772
21866
  "code_injection",
21773
21867
  "template_injection",
@@ -21829,7 +21923,7 @@ function applyProjectProfileTransform(findings, resolveProfile) {
21829
21923
  });
21830
21924
  }
21831
21925
 
21832
- // node_modules/circle-ir/dist/languages/registry.js
21926
+ // ../circle-ir/dist/languages/registry.js
21833
21927
  class DefaultLanguageRegistry {
21834
21928
  plugins = new Map;
21835
21929
  extensionMap = new Map;
@@ -21876,7 +21970,7 @@ function registerLanguage(plugin) {
21876
21970
  function getLanguagePlugin(language) {
21877
21971
  return getLanguageRegistry().get(language);
21878
21972
  }
21879
- // node_modules/circle-ir/dist/languages/plugins/base.js
21973
+ // ../circle-ir/dist/languages/plugins/base.js
21880
21974
  class BaseLanguagePlugin {
21881
21975
  parser = null;
21882
21976
  async initialize(parser) {
@@ -21937,7 +22031,7 @@ class BaseLanguagePlugin {
21937
22031
  }
21938
22032
  }
21939
22033
 
21940
- // node_modules/circle-ir/dist/languages/plugins/java.js
22034
+ // ../circle-ir/dist/languages/plugins/java.js
21941
22035
  class JavaPlugin extends BaseLanguagePlugin {
21942
22036
  id = "java";
21943
22037
  name = "Java";
@@ -22194,7 +22288,7 @@ class JavaPlugin extends BaseLanguagePlugin {
22194
22288
  }
22195
22289
  }
22196
22290
 
22197
- // node_modules/circle-ir/dist/languages/plugins/javascript.js
22291
+ // ../circle-ir/dist/languages/plugins/javascript.js
22198
22292
  class JavaScriptPlugin extends BaseLanguagePlugin {
22199
22293
  id = "javascript";
22200
22294
  name = "JavaScript/TypeScript";
@@ -22860,7 +22954,7 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
22860
22954
  }
22861
22955
  }
22862
22956
 
22863
- // node_modules/circle-ir/dist/languages/plugins/python.js
22957
+ // ../circle-ir/dist/languages/plugins/python.js
22864
22958
  class PythonPlugin extends BaseLanguagePlugin {
22865
22959
  id = "python";
22866
22960
  name = "Python";
@@ -23136,7 +23230,7 @@ class PythonPlugin extends BaseLanguagePlugin {
23136
23230
  }
23137
23231
  }
23138
23232
 
23139
- // node_modules/circle-ir/dist/languages/plugins/rust.js
23233
+ // ../circle-ir/dist/languages/plugins/rust.js
23140
23234
  class RustPlugin extends BaseLanguagePlugin {
23141
23235
  id = "rust";
23142
23236
  name = "Rust";
@@ -23369,7 +23463,7 @@ class RustPlugin extends BaseLanguagePlugin {
23369
23463
  }
23370
23464
  }
23371
23465
 
23372
- // node_modules/circle-ir/dist/languages/plugins/bash.js
23466
+ // ../circle-ir/dist/languages/plugins/bash.js
23373
23467
  class BashPlugin extends BaseLanguagePlugin {
23374
23468
  id = "bash";
23375
23469
  name = "Bash/Shell";
@@ -23575,7 +23669,7 @@ class BashPlugin extends BaseLanguagePlugin {
23575
23669
  }
23576
23670
  }
23577
23671
 
23578
- // node_modules/circle-ir/dist/languages/plugins/html.js
23672
+ // ../circle-ir/dist/languages/plugins/html.js
23579
23673
  class HtmlPlugin extends BaseLanguagePlugin {
23580
23674
  id = "html";
23581
23675
  name = "HTML";
@@ -23640,7 +23734,7 @@ class HtmlPlugin extends BaseLanguagePlugin {
23640
23734
  }
23641
23735
  }
23642
23736
 
23643
- // node_modules/circle-ir/dist/languages/plugins/vue.js
23737
+ // ../circle-ir/dist/languages/plugins/vue.js
23644
23738
  class VuePlugin extends BaseLanguagePlugin {
23645
23739
  id = "vue";
23646
23740
  name = "Vue";
@@ -23705,7 +23799,7 @@ class VuePlugin extends BaseLanguagePlugin {
23705
23799
  }
23706
23800
  }
23707
23801
 
23708
- // node_modules/circle-ir/dist/languages/plugins/go.js
23802
+ // ../circle-ir/dist/languages/plugins/go.js
23709
23803
  class GoPlugin extends BaseLanguagePlugin {
23710
23804
  id = "go";
23711
23805
  name = "Go";
@@ -23930,6 +24024,54 @@ class GoPlugin extends BaseLanguagePlugin {
23930
24024
  severity: "high",
23931
24025
  argPositions: [0]
23932
24026
  },
24027
+ {
24028
+ method: "Create",
24029
+ class: "os",
24030
+ type: "path_traversal",
24031
+ cwe: "CWE-22",
24032
+ severity: "high",
24033
+ argPositions: [0]
24034
+ },
24035
+ {
24036
+ method: "OpenFile",
24037
+ class: "os",
24038
+ type: "path_traversal",
24039
+ cwe: "CWE-22",
24040
+ severity: "high",
24041
+ argPositions: [0]
24042
+ },
24043
+ {
24044
+ method: "Remove",
24045
+ class: "os",
24046
+ type: "path_traversal",
24047
+ cwe: "CWE-22",
24048
+ severity: "high",
24049
+ argPositions: [0]
24050
+ },
24051
+ {
24052
+ method: "RemoveAll",
24053
+ class: "os",
24054
+ type: "path_traversal",
24055
+ cwe: "CWE-22",
24056
+ severity: "high",
24057
+ argPositions: [0]
24058
+ },
24059
+ {
24060
+ method: "ReadFile",
24061
+ class: "ioutil",
24062
+ type: "path_traversal",
24063
+ cwe: "CWE-22",
24064
+ severity: "high",
24065
+ argPositions: [0]
24066
+ },
24067
+ {
24068
+ method: "WriteFile",
24069
+ class: "ioutil",
24070
+ type: "path_traversal",
24071
+ cwe: "CWE-22",
24072
+ severity: "high",
24073
+ argPositions: [0]
24074
+ },
23933
24075
  {
23934
24076
  method: "Fprintf",
23935
24077
  class: "fmt",
@@ -24354,7 +24496,7 @@ class GoPlugin extends BaseLanguagePlugin {
24354
24496
  }
24355
24497
  }
24356
24498
 
24357
- // node_modules/circle-ir/dist/languages/plugins/csharp.js
24499
+ // ../circle-ir/dist/languages/plugins/csharp.js
24358
24500
  class CSharpPlugin extends BaseLanguagePlugin {
24359
24501
  id = "csharp";
24360
24502
  name = "C#";
@@ -24413,7 +24555,7 @@ class CSharpPlugin extends BaseLanguagePlugin {
24413
24555
  }
24414
24556
  }
24415
24557
 
24416
- // node_modules/circle-ir/dist/languages/plugins/index.js
24558
+ // ../circle-ir/dist/languages/plugins/index.js
24417
24559
  function registerBuiltinPlugins() {
24418
24560
  registerLanguage(new JavaPlugin);
24419
24561
  registerLanguage(new JavaScriptPlugin);
@@ -24425,7 +24567,7 @@ function registerBuiltinPlugins() {
24425
24567
  registerLanguage(new GoPlugin);
24426
24568
  registerLanguage(new CSharpPlugin);
24427
24569
  }
24428
- // node_modules/circle-ir/dist/analysis/passes/cross-file-pass.js
24570
+ // ../circle-ir/dist/analysis/passes/cross-file-pass.js
24429
24571
  class CrossFilePass {
24430
24572
  run(projectGraph, sourceLines, options = {}) {
24431
24573
  const resolver = projectGraph.resolver;
@@ -24436,7 +24578,8 @@ class CrossFilePass {
24436
24578
  logger.info("cross-file: starting", { files: fileCount, budgetMs });
24437
24579
  const phase1Start = Date.now();
24438
24580
  logger.debug("cross-file: phase 1/4 starting (findCrossFileTaintFlows)");
24439
- const flows = resolver.findCrossFileTaintFlows();
24581
+ const flows = resolver.findCrossFileTaintFlows(budgetExceeded);
24582
+ let phase1Truncated = budgetExceeded();
24440
24583
  logger.info("cross-file: phase 1/4 done", {
24441
24584
  flows: flows.length,
24442
24585
  elapsedMs: Date.now() - phase1Start
@@ -24486,7 +24629,7 @@ class CrossFilePass {
24486
24629
  confidence: 0.7
24487
24630
  }];
24488
24631
  });
24489
- let exceeded = false;
24632
+ let exceeded = phase1Truncated;
24490
24633
  const ipPaths = [];
24491
24634
  if (budgetExceeded()) {
24492
24635
  exceeded = true;
@@ -24498,7 +24641,7 @@ class CrossFilePass {
24498
24641
  } else {
24499
24642
  const phase2Start = Date.now();
24500
24643
  logger.debug("cross-file: phase 2/4 starting (findInterproceduralTaintPaths)");
24501
- const phase2 = resolver.findInterproceduralTaintPaths();
24644
+ const phase2 = resolver.findInterproceduralTaintPaths(budgetExceeded);
24502
24645
  ipPaths.push(...phase2);
24503
24646
  logger.info("cross-file: phase 2/4 done", {
24504
24647
  paths: phase2.length,
@@ -24515,7 +24658,7 @@ class CrossFilePass {
24515
24658
  } else if (!exceeded) {
24516
24659
  const phase3Start = Date.now();
24517
24660
  logger.debug("cross-file: phase 3/4 starting (findFieldBindingTaintPaths)");
24518
- const phase3 = resolver.findFieldBindingTaintPaths();
24661
+ const phase3 = resolver.findFieldBindingTaintPaths(budgetExceeded);
24519
24662
  ipPaths.push(...phase3);
24520
24663
  logger.info("cross-file: phase 3/4 done", {
24521
24664
  paths: phase3.length,
@@ -24751,7 +24894,7 @@ function findCrossInstanceAliasingPaths(projectGraph, _sourceLines) {
24751
24894
  return paths;
24752
24895
  }
24753
24896
 
24754
- // node_modules/circle-ir/dist/analysis/html/html-extractor.js
24897
+ // ../circle-ir/dist/analysis/html/html-extractor.js
24755
24898
  var EVENT_HANDLER_ATTRS = new Set([
24756
24899
  "onclick",
24757
24900
  "ondblclick",
@@ -24906,7 +25049,7 @@ function stripQuotes2(text) {
24906
25049
  return text;
24907
25050
  }
24908
25051
 
24909
- // node_modules/circle-ir/dist/analysis/html/html-attribute-security-pass.js
25052
+ // ../circle-ir/dist/analysis/html/html-attribute-security-pass.js
24910
25053
  function runHtmlAttributeSecurityChecks(rootNode, filePath) {
24911
25054
  const findings = [];
24912
25055
  walkForSecurityChecks(rootNode, filePath, findings);
@@ -25132,7 +25275,7 @@ function truncate(s, maxLen) {
25132
25275
  return s.length > maxLen ? s.slice(0, maxLen) + "..." : s;
25133
25276
  }
25134
25277
 
25135
- // node_modules/circle-ir/dist/analysis/html/vue-template-xss-pass.js
25278
+ // ../circle-ir/dist/analysis/html/vue-template-xss-pass.js
25136
25279
  var DANGEROUS_BINDINGS = new Set([
25137
25280
  "v-html",
25138
25281
  "v-bind:innerhtml",
@@ -25252,7 +25395,7 @@ function collectTaintedNames(blocks) {
25252
25395
  return names;
25253
25396
  }
25254
25397
 
25255
- // node_modules/circle-ir/dist/analysis/html/html-merge.js
25398
+ // ../circle-ir/dist/analysis/html/html-merge.js
25256
25399
  function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
25257
25400
  const allTypes = [];
25258
25401
  const allCalls = [];
@@ -25402,7 +25545,7 @@ function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
25402
25545
  };
25403
25546
  }
25404
25547
 
25405
- // node_modules/circle-ir/dist/analysis/passes/taint-matcher-pass.js
25548
+ // ../circle-ir/dist/analysis/passes/taint-matcher-pass.js
25406
25549
  class TaintMatcherPass {
25407
25550
  name = "taint-matcher";
25408
25551
  category = "security";
@@ -25463,7 +25606,7 @@ class TaintMatcherPass {
25463
25606
  }
25464
25607
  }
25465
25608
 
25466
- // node_modules/circle-ir/dist/analysis/passes/constant-propagation-pass.js
25609
+ // ../circle-ir/dist/analysis/passes/constant-propagation-pass.js
25467
25610
  class ConstantPropagationPass {
25468
25611
  tree;
25469
25612
  name = "constant-propagation";
@@ -25493,7 +25636,7 @@ class ConstantPropagationPass {
25493
25636
  }
25494
25637
  }
25495
25638
 
25496
- // node_modules/circle-ir/dist/analysis/passes/language-sources-pass.js
25639
+ // ../circle-ir/dist/analysis/passes/language-sources-pass.js
25497
25640
  var JS_DOM_XSS_SINKS = [
25498
25641
  { pattern: /\.innerHTML\s*=/, type: "xss", cwe: "CWE-79", severity: "critical" },
25499
25642
  { pattern: /\.outerHTML\s*=/, type: "xss", cwe: "CWE-79", severity: "critical" },
@@ -25611,6 +25754,8 @@ class LanguageSourcesPass {
25611
25754
  additionalSources.push(...findCSharpBindingAttributeSources(code, language));
25612
25755
  additionalSources.push(...findCSharpMinimalApiSources(code, language));
25613
25756
  additionalSources.push(...findGoArgvSources(code, language));
25757
+ additionalSources.push(...findGoArchiveEntrySources(code, language));
25758
+ additionalSources.push(...findGoMultipartUploadSources(code, language));
25614
25759
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
25615
25760
  for (const s of jsDOMSinks) {
25616
25761
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -25681,6 +25826,7 @@ class LanguageSourcesPass {
25681
25826
  if (language === "go") {
25682
25827
  additionalSanitizers.push(...findGoMapAllowlistGuardSanitizers(code));
25683
25828
  additionalSanitizers.push(...findGoHtmlTemplateImportSanitizers(code));
25829
+ additionalSanitizers.push(...findGoPathContainmentGuardSanitizers(code));
25684
25830
  const goMisconfigFindings = findGoPatternFindings(code, graph.ir.meta.file);
25685
25831
  for (const finding of goMisconfigFindings) {
25686
25832
  ctx.addFinding(finding);
@@ -25718,6 +25864,7 @@ class LanguageSourcesPass {
25718
25864
  additionalSanitizers.push(...findPythonUrlAllowlistRedirectSanitizers(code));
25719
25865
  additionalSanitizers.push(...findPythonDefusedXmlSanitizers(code));
25720
25866
  additionalSanitizers.push(...findPythonJinjaAutoescapeSanitizers(code));
25867
+ additionalSanitizers.push(...findCrlfWrapperFunctionSanitizers(code, "python"));
25721
25868
  const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
25722
25869
  for (const finding of pyMisconfigFindings) {
25723
25870
  ctx.addFinding(finding);
@@ -25821,12 +25968,16 @@ class LanguageSourcesPass {
25821
25968
  ctx.addFinding(finding);
25822
25969
  }
25823
25970
  }
25971
+ if (language === "csharp") {
25972
+ additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code));
25973
+ }
25824
25974
  if (language === "java") {
25825
25975
  additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
25826
25976
  additionalSanitizers.push(...findJavaPathNormalizeStartsWithGuardSanitizers(code));
25827
25977
  additionalSanitizers.push(...findJavaPathGetFileNameSanitizers(code));
25828
25978
  additionalSanitizers.push(...findJavaCanonicalPathStartsWithGuardSanitizers(code));
25829
25979
  additionalSanitizers.push(...findJavaInlineCrlfStripLogSanitizers(code));
25980
+ additionalSanitizers.push(...findCrlfWrapperFunctionSanitizers(code, "java"));
25830
25981
  additionalSanitizers.push(...findJavaArgvFormExecSanitizers(code));
25831
25982
  for (const finding of findJavaPatternFindings(code, graph.ir.meta.file)) {
25832
25983
  ctx.addFinding(finding);
@@ -26242,13 +26393,121 @@ function findGoArgvSources(sourceCode, language) {
26242
26393
  }
26243
26394
  return sources;
26244
26395
  }
26396
+ function findGoArchiveEntrySources(sourceCode, language) {
26397
+ if (language !== "go")
26398
+ return [];
26399
+ if (!/\bimport\b[\s\S]{0,400}?archive\/(?:zip|tar)|"archive\/(?:zip|tar)"/.test(sourceCode))
26400
+ return [];
26401
+ const sources = [];
26402
+ const lines = sourceCode.split(`
26403
+ `);
26404
+ const entryVars = new Set;
26405
+ const zipRange = /\bfor\b[^;{]*?,\s*([A-Za-z_]\w*)\s*:?=\s*range\s+[A-Za-z_][\w.]*\.File\b/;
26406
+ const tarNext = /\b([A-Za-z_]\w*)\s*,\s*[A-Za-z_]\w*\s*:?=\s*[A-Za-z_][\w.]*\.Next\s*\(\s*\)/;
26407
+ for (const line of lines) {
26408
+ const z = zipRange.exec(line);
26409
+ if (z)
26410
+ entryVars.add(z[1]);
26411
+ const t = tarNext.exec(line);
26412
+ if (t)
26413
+ entryVars.add(t[1]);
26414
+ }
26415
+ if (entryVars.size === 0)
26416
+ return sources;
26417
+ const assignRe = /^\s*(?:var\s+)?([A-Za-z_]\w*)\s*(?::?=|=)\s*(.+?)\s*$/;
26418
+ for (let i2 = 0;i2 < lines.length; i2++) {
26419
+ const m = assignRe.exec(lines[i2]);
26420
+ if (!m)
26421
+ continue;
26422
+ const [, lhs, rhs] = m;
26423
+ let matched = null;
26424
+ for (const v of entryVars) {
26425
+ if (new RegExp(`(?<![\\w.])${v}\\s*\\.\\s*Name\\b`).test(rhs)) {
26426
+ matched = v;
26427
+ break;
26428
+ }
26429
+ }
26430
+ if (!matched)
26431
+ continue;
26432
+ const lineNumber = i2 + 1;
26433
+ if (sources.some((sc) => sc.line === lineNumber && sc.variable === lhs))
26434
+ continue;
26435
+ sources.push({
26436
+ type: "file_input",
26437
+ location: `${lhs} derived from archive entry name ${matched}.Name (Zip/Tar Slip)`,
26438
+ severity: "high",
26439
+ line: lineNumber,
26440
+ confidence: 0.9,
26441
+ variable: lhs
26442
+ });
26443
+ }
26444
+ return sources;
26445
+ }
26446
+ function findGoMultipartUploadSources(sourceCode, language) {
26447
+ if (language !== "go")
26448
+ return [];
26449
+ if (!/\.\s*(?:FormFile|MultipartForm|MultipartReader)\b/.test(sourceCode))
26450
+ return [];
26451
+ const sources = [];
26452
+ const lines = sourceCode.split(`
26453
+ `);
26454
+ const headerVars = new Set;
26455
+ const formFileRe = /\b[A-Za-z_]\w*\s*,\s*([A-Za-z_]\w*)\s*(?:,\s*[A-Za-z_]\w*\s*)?:?=\s*[A-Za-z_][\w.]*\.\s*FormFile\s*\(/;
26456
+ for (let i2 = 0;i2 < lines.length; i2++) {
26457
+ const m = formFileRe.exec(lines[i2]);
26458
+ if (!m)
26459
+ continue;
26460
+ const v = m[1];
26461
+ if (v === "_")
26462
+ continue;
26463
+ headerVars.add(v);
26464
+ sources.push({
26465
+ type: "http_param",
26466
+ location: `${v}.Filename from r.FormFile (client-supplied upload filename)`,
26467
+ severity: "high",
26468
+ line: i2 + 1,
26469
+ confidence: 0.9,
26470
+ variable: v
26471
+ });
26472
+ }
26473
+ const assignRe = /^\s*(?:var\s+)?([A-Za-z_]\w*)\s*(?::?=|=)\s*(.+?)\s*$/;
26474
+ for (let i2 = 0;i2 < lines.length; i2++) {
26475
+ const m = assignRe.exec(lines[i2]);
26476
+ if (!m)
26477
+ continue;
26478
+ const [, lhs, rhs] = m;
26479
+ if (lhs === "_")
26480
+ continue;
26481
+ let matched = null;
26482
+ for (const v of headerVars) {
26483
+ if (new RegExp(`(?<![\\w.])${v}\\s*\\.\\s*Filename\\b`).test(rhs)) {
26484
+ matched = v;
26485
+ break;
26486
+ }
26487
+ }
26488
+ if (!matched)
26489
+ continue;
26490
+ const lineNumber = i2 + 1;
26491
+ if (sources.some((sc) => sc.line === lineNumber && sc.variable === lhs))
26492
+ continue;
26493
+ sources.push({
26494
+ type: "http_param",
26495
+ location: `${lhs} derived from upload filename ${matched}.Filename`,
26496
+ severity: "high",
26497
+ line: lineNumber,
26498
+ confidence: 0.9,
26499
+ variable: lhs
26500
+ });
26501
+ }
26502
+ return sources;
26503
+ }
26245
26504
  function findCSharpRequestSources(sourceCode, language) {
26246
26505
  if (language !== "csharp")
26247
26506
  return [];
26248
26507
  const sources = [];
26249
26508
  const lines = sourceCode.split(`
26250
26509
  `);
26251
- const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
26510
+ const assignRe = /^\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
26252
26511
  const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
26253
26512
  const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
26254
26513
  const envReadRe = /\bEnvironment\s*\.\s*GetEnvironmentVariables?\s*(?:\(|\[)/;
@@ -26954,6 +27213,12 @@ function findPythonReflectionInvocationSinks(sourceCode, taintedVars) {
26954
27213
  }
26955
27214
  return sinks;
26956
27215
  }
27216
+ function isProvablyLiteralDomAssignment(line) {
27217
+ const eq = line.indexOf("=");
27218
+ if (eq < 0)
27219
+ return false;
27220
+ return isProvablyLiteralExpression(line.slice(eq + 1));
27221
+ }
26957
27222
  function findJavaScriptDOMSinks(sourceCode, language) {
26958
27223
  if (!["javascript", "typescript"].includes(language))
26959
27224
  return [];
@@ -26982,6 +27247,9 @@ function findJavaScriptDOMSinks(sourceCode, language) {
26982
27247
  method = "cssText";
26983
27248
  else if (line.includes("style.textContent"))
26984
27249
  method = "textContent";
27250
+ if (DOM_ASSIGNMENT_SINK_METHODS.has(method) && isProvablyLiteralDomAssignment(line)) {
27251
+ continue;
27252
+ }
26985
27253
  if (method === "document.write" || method === "document.writeln") {
26986
27254
  const argMatch = line.match(/document\.write(?:ln)?\s*\(\s*([^)]*)\)/);
26987
27255
  if (argMatch) {
@@ -27747,6 +28015,47 @@ function findGoMapAllowlistGuardSanitizers(code) {
27747
28015
  }
27748
28016
  return sanitizers;
27749
28017
  }
28018
+ function findGoPathContainmentGuardSanitizers(code) {
28019
+ const sanitizers = [];
28020
+ const lines = code.split(`
28021
+ `);
28022
+ const REJECT = /\b(?:continue|break|return|panic|fmt\.Errorf|errors\.New)\b/;
28023
+ const guarded = new Map;
28024
+ for (let i2 = 0;i2 < lines.length; i2++) {
28025
+ const line = lines[i2];
28026
+ const hp = /if\s*!\s*strings\s*\.\s*HasPrefix\s*\(\s*([A-Za-z_]\w*)\s*,/.exec(line);
28027
+ if (hp && guardRejects(lines, i2, REJECT, 4)) {
28028
+ if (!guarded.has(hp[1]))
28029
+ guarded.set(hp[1], i2 + 1);
28030
+ continue;
28031
+ }
28032
+ const rel = /([A-Za-z_]\w*)\s*,\s*[A-Za-z_]\w*\s*:?=\s*filepath\s*\.\s*Rel\s*\(\s*[A-Za-z_]\w*\s*,\s*([A-Za-z_]\w*)\s*\)/.exec(line);
28033
+ if (rel) {
28034
+ const window2 = lines.slice(i2, i2 + 5).join(`
28035
+ `);
28036
+ if (/\.\./.test(window2) && REJECT.test(window2)) {
28037
+ if (!guarded.has(rel[2]))
28038
+ guarded.set(rel[2], i2 + 1);
28039
+ }
28040
+ }
28041
+ }
28042
+ if (guarded.size === 0)
28043
+ return sanitizers;
28044
+ for (const [name2, guardLine] of guarded) {
28045
+ const useRe = new RegExp(`(?<![\\w.])${name2}(?![\\w])`);
28046
+ for (let l = guardLine;l < lines.length; l++) {
28047
+ if (!useRe.test(lines[l]))
28048
+ continue;
28049
+ sanitizers.push({
28050
+ type: "go_path_containment_guard",
28051
+ method: "strings.HasPrefix",
28052
+ line: l + 1,
28053
+ sanitizes: ["path_traversal"]
28054
+ });
28055
+ }
28056
+ }
28057
+ return sanitizers;
28058
+ }
27750
28059
  function findGoHtmlTemplateImportSanitizers(code) {
27751
28060
  const sanitizers = [];
27752
28061
  const hasHtmlTemplate = /["\s]html\/template["\s]/.test(code);
@@ -28713,7 +29022,7 @@ function findJsPathResolveStartsWithGuardSanitizers(code) {
28713
29022
  for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
28714
29023
  if (!guardRe.test(lines[l]))
28715
29024
  continue;
28716
- if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
29025
+ if (guardRejects(lines, l, terminatorRe, 2)) {
28717
29026
  guardLine = l + 1;
28718
29027
  break;
28719
29028
  }
@@ -28833,7 +29142,7 @@ function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
28833
29142
  for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
28834
29143
  if (!guardRe.test(lines[l]))
28835
29144
  continue;
28836
- if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
29145
+ if (guardRejects(lines, l, terminatorRe, 2)) {
28837
29146
  guardLine = l + 1;
28838
29147
  break;
28839
29148
  }
@@ -28871,18 +29180,7 @@ function findJavaCanonicalPathStartsWithGuardSanitizers(code) {
28871
29180
  const m = guardRe.exec(lines[i2]);
28872
29181
  if (!m)
28873
29182
  continue;
28874
- let hasTerminator = terminatorRe.test(lines[i2]);
28875
- if (!hasTerminator) {
28876
- for (let j = i2 + 1;j < Math.min(lines.length, i2 + 6); j++) {
28877
- if (terminatorRe.test(lines[j])) {
28878
- hasTerminator = true;
28879
- break;
28880
- }
28881
- if (lines[j].includes("}"))
28882
- break;
28883
- }
28884
- }
28885
- if (!hasTerminator)
29183
+ if (!guardRejects(lines, i2, terminatorRe, 6))
28886
29184
  continue;
28887
29185
  const guarded = new Set([m[1]]);
28888
29186
  const mentions = (t) => {
@@ -28912,6 +29210,71 @@ function findJavaCanonicalPathStartsWithGuardSanitizers(code) {
28912
29210
  }
28913
29211
  return sanitizers;
28914
29212
  }
29213
+ function guardRejects(lines, i2, terminatorRe, window2) {
29214
+ if (terminatorRe.test(lines[i2]))
29215
+ return true;
29216
+ const opens = (lines[i2].match(/\{/g) ?? []).length;
29217
+ const closes = (lines[i2].match(/\}/g) ?? []).length;
29218
+ if (opens > 0 && closes >= opens)
29219
+ return false;
29220
+ for (let j = i2 + 1;j < Math.min(lines.length, i2 + window2); j++) {
29221
+ if (terminatorRe.test(lines[j]))
29222
+ return true;
29223
+ if (lines[j].includes("}"))
29224
+ return false;
29225
+ }
29226
+ return false;
29227
+ }
29228
+ function findCSharpFullPathStartsWithGuardSanitizers(code) {
29229
+ const sanitizers = [];
29230
+ const lines = code.split(`
29231
+ `);
29232
+ const assignRe = /^\s*(?:(?:var|string)\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
29233
+ const fullPathRe = /\bPath\s*\.\s*GetFullPath\s*\(/;
29234
+ const canonicalVars = new Set;
29235
+ for (const line of lines) {
29236
+ const a = assignRe.exec(line);
29237
+ if (a && fullPathRe.test(a[2]))
29238
+ canonicalVars.add(a[1]);
29239
+ }
29240
+ if (canonicalVars.size === 0)
29241
+ return sanitizers;
29242
+ const guardRe = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*StartsWith\s*\(/;
29243
+ const terminatorRe = /\b(throw|return)\b/;
29244
+ for (let i2 = 0;i2 < lines.length; i2++) {
29245
+ const m = guardRe.exec(lines[i2]);
29246
+ if (!m || !canonicalVars.has(m[1]))
29247
+ continue;
29248
+ if (!guardRejects(lines, i2, terminatorRe, 6))
29249
+ continue;
29250
+ const guarded = new Set([m[1]]);
29251
+ const mentions = (t) => {
29252
+ for (const n of guarded)
29253
+ if (new RegExp(`\\b${n}\\b`).test(t))
29254
+ return true;
29255
+ return false;
29256
+ };
29257
+ for (let l = 0;l < lines.length; l++) {
29258
+ const lt = lines[l];
29259
+ const a = assignRe.exec(lt);
29260
+ if (a) {
29261
+ if (mentions(a[2]))
29262
+ guarded.add(a[1]);
29263
+ else if (a[1] !== m[1])
29264
+ guarded.delete(a[1]);
29265
+ }
29266
+ if (mentions(lt)) {
29267
+ sanitizers.push({
29268
+ type: "csharp_fullpath_startswith_guard",
29269
+ method: "GetFullPath",
29270
+ line: l + 1,
29271
+ sanitizes: ["path_traversal", "external_taint_escape"]
29272
+ });
29273
+ }
29274
+ }
29275
+ }
29276
+ return sanitizers;
29277
+ }
28915
29278
  function findJavaPathGetFileNameSanitizers(code) {
28916
29279
  const sanitizers = [];
28917
29280
  const lines = code.split(`
@@ -28955,6 +29318,41 @@ function findJavaPathGetFileNameSanitizers(code) {
28955
29318
  }
28956
29319
  return sanitizers;
28957
29320
  }
29321
+ function findCrlfWrapperFunctionSanitizers(code, language) {
29322
+ const sanitizers = [];
29323
+ const lines = code.split(`
29324
+ `);
29325
+ const declRe = language === "java" ? /^\s*(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?(?:String|CharSequence)\s+([A-Za-z_]\w*)\s*\(/ : /^\s*def\s+([A-Za-z_]\w*)\s*\(/;
29326
+ const stripRe = language === "java" ? /\.\s*replace(?:All)?\s*\(\s*(?:"[^"]*\\[rnt]|'\\[rnt]')/ : /(?:re\s*\.\s*sub\s*\(\s*r?["'][^"']*\\[rnt]|\.\s*replace\s*\(\s*["']\\[rnt])/;
29327
+ const bodyWindow = 10;
29328
+ const wrappers = new Set;
29329
+ for (let i2 = 0;i2 < lines.length; i2++) {
29330
+ const m = declRe.exec(lines[i2]);
29331
+ if (!m)
29332
+ continue;
29333
+ const body2 = lines.slice(i2, Math.min(lines.length, i2 + bodyWindow)).join(`
29334
+ `);
29335
+ if (stripRe.test(body2))
29336
+ wrappers.add(m[1]);
29337
+ }
29338
+ if (wrappers.size === 0)
29339
+ return sanitizers;
29340
+ for (let i2 = 0;i2 < lines.length; i2++) {
29341
+ for (const name2 of wrappers) {
29342
+ if (declRe.test(lines[i2]) && new RegExp(`\\b${name2}\\s*\\(`).test(lines[i2]))
29343
+ continue;
29344
+ if (!new RegExp(`\\b${name2}\\s*\\(`).test(lines[i2]))
29345
+ continue;
29346
+ sanitizers.push({
29347
+ type: language === "java" ? "java_wrapper_crlf_strip" : "python_wrapper_crlf_strip",
29348
+ method: name2,
29349
+ line: i2 + 1,
29350
+ sanitizes: ["log_injection", "external_taint_escape"]
29351
+ });
29352
+ }
29353
+ }
29354
+ return sanitizers;
29355
+ }
28958
29356
  function findJavaInlineCrlfStripLogSanitizers(code) {
28959
29357
  const sanitizers = [];
28960
29358
  const lines = code.split(`
@@ -32550,7 +32948,7 @@ function findJsTemplateInjectionSstiFindings(code, file) {
32550
32948
  return findings;
32551
32949
  }
32552
32950
 
32553
- // node_modules/circle-ir/dist/analysis/passes/source-semantics-pass.js
32951
+ // ../circle-ir/dist/analysis/passes/source-semantics-pass.js
32554
32952
  var DEMO_PATH_RE = /(?:^|\/)(?:demo|example|examples|samples|integration-tests|integration_tests)(?:\/|$)/i;
32555
32953
  var CONST_STRING_ASSIGN_RE = /^\s*(?:final\s+|static\s+final\s+)?[A-Za-z_][\w.<>\[\]]*\s+[A-Za-z_]\w*\s*=\s*"[^"]*"\s*;?\s*$/;
32556
32954
  var STATIC_FINAL_RE = /^\s*(?:public\s+|private\s+|protected\s+)?static\s+final\s+/;
@@ -32638,7 +33036,7 @@ class SourceSemanticsPass {
32638
33036
  }
32639
33037
  }
32640
33038
 
32641
- // node_modules/circle-ir/dist/analysis/passes/library-profile-source-gate-pass.js
33039
+ // ../circle-ir/dist/analysis/passes/library-profile-source-gate-pass.js
32642
33040
  var SPECULATIVE_SOURCE_TYPES = new Set([
32643
33041
  "interprocedural_param",
32644
33042
  "constructor_field"
@@ -32663,7 +33061,7 @@ class LibraryProfileSourceGatePass {
32663
33061
  droppedByType: {}
32664
33062
  };
32665
33063
  }
32666
- const sources = graph.ir.taint.sources;
33064
+ const sources = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sources : graph.ir.taint.sources;
32667
33065
  if (sources.length === 0) {
32668
33066
  return {
32669
33067
  profile,
@@ -32673,17 +33071,12 @@ class LibraryProfileSourceGatePass {
32673
33071
  };
32674
33072
  }
32675
33073
  const droppedByType = {};
32676
- const kept = [];
32677
33074
  for (const src of sources) {
32678
33075
  if (SPECULATIVE_SOURCE_TYPES.has(src.type)) {
32679
33076
  droppedByType[src.type] = (droppedByType[src.type] ?? 0) + 1;
32680
- continue;
32681
33077
  }
32682
- kept.push(src);
32683
33078
  }
32684
- const dropped = sources.length - kept.length;
32685
- sources.length = 0;
32686
- sources.push(...kept);
33079
+ const dropped = 0;
32687
33080
  return {
32688
33081
  profile,
32689
33082
  applied: true,
@@ -32693,7 +33086,7 @@ class LibraryProfileSourceGatePass {
32693
33086
  }
32694
33087
  }
32695
33088
 
32696
- // node_modules/circle-ir/dist/analysis/passes/mybatis-annotation-sql-sink-pass.js
33089
+ // ../circle-ir/dist/analysis/passes/mybatis-annotation-sql-sink-pass.js
32697
33090
  var MYBATIS_SQL_ANNOTATIONS = new Set([
32698
33091
  "Select",
32699
33092
  "Update",
@@ -32920,7 +33313,7 @@ class MyBatisAnnotationSqlSinkPass {
32920
33313
  }
32921
33314
  }
32922
33315
 
32923
- // node_modules/circle-ir/dist/analysis/passes/sink-filter-pass.js
33316
+ // ../circle-ir/dist/analysis/passes/sink-filter-pass.js
32924
33317
  function escapeReSf(s) {
32925
33318
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
32926
33319
  }
@@ -33027,7 +33420,32 @@ var PROTOCOL_CLIENT_PACKAGES = [
33027
33420
  "org.eclipse.paho"
33028
33421
  ];
33029
33422
  var OS_EXEC_RECEIVER_RE = /\b(?:Runtime|ProcessBuilder|DefaultExecutor|Executor|Exec|Launcher|ProcStarter|ProcessExecutor|RuntimeUtil)\s*[.(]/;
33423
+ var JAVA_STDOUT_PRINT_RE = /\bSystem\s*\.\s*(?:out|err)\s*\.\s*(?:println|print|printf|write|append)\s*\(/;
33030
33424
  var PROCESS_BUILDER_ARGV_FORM_RE = /\bnew\s+ProcessBuilder\s*\(\s*(?:Arrays\.asList\b|List\.of\b|Collections\.singletonList\b|new\s+ArrayList\b|new\s+String\s*\[\s*\]\s*\{|"[^"]*"\s*,)/;
33425
+ var PROCESS_BUILDER_COMMAND_ARGV_FORM_RE = /\.\s*command\s*\(\s*(?:Arrays\.asList\b|List\.of\b|Collections\.singletonList\b|new\s+ArrayList\b|new\s+String\s*\[\s*\]\s*\{|"[^"]*"\s*,)/;
33426
+ var SHELL_PROGRAMS = new Set([
33427
+ "sh",
33428
+ "bash",
33429
+ "zsh",
33430
+ "ksh",
33431
+ "dash",
33432
+ "ash",
33433
+ "csh",
33434
+ "tcsh",
33435
+ "fish",
33436
+ "cmd",
33437
+ "command",
33438
+ "powershell",
33439
+ "pwsh"
33440
+ ]);
33441
+ var ARGV_FIRST_LITERAL_RE = /(?:\bnew\s+ProcessBuilder\s*\(|\.\s*command\s*\()\s*(?:Arrays\.asList\s*\(|List\.of\s*\(|Collections\.singletonList\s*\(|new\s+ArrayList\s*(?:<[^>]*>)?\s*\(\s*(?:Arrays\.asList\s*\()?|new\s+String\s*\[\s*\]\s*\{)?\s*"([^"]*)"/;
33442
+ function argvFirstLiteralIsShell(lineText) {
33443
+ const m = ARGV_FIRST_LITERAL_RE.exec(lineText);
33444
+ if (!m)
33445
+ return false;
33446
+ const base = m[1].split(/[\\/]/).pop().replace(/\.exe$/i, "").toLowerCase();
33447
+ return SHELL_PROGRAMS.has(base);
33448
+ }
33031
33449
  var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b[^;]*;\s*(?:\/\/.*|\/\*.*)?$/;
33032
33450
  var JEXL_ENGINE_TYPES = new Set(["JexlEngine", "Jexl", "JxltEngine"]);
33033
33451
  var JEXL_EXPRESSION_TYPE_RE = /(?:Jexl)?(?:Expression|Script|Template)(?:Script)?$/;
@@ -33197,10 +33615,41 @@ function jsSsrfHostGuardedLines(code) {
33197
33615
  }
33198
33616
  return covered;
33199
33617
  }
33618
+ var CSHARP_ALLOWLIST_STRIP_RE = /\bRegex\s*\.\s*Replace\s*\([^,]*,\s*@?"\[\^(?:[A-Za-z0-9_\- ]|\\w|\\d|\\s)+\]"\s*,\s*@?"[A-Za-z0-9_]?"\s*\)/;
33619
+ function isUnwrittenLocalMapKeyRead(code, source) {
33620
+ if (source.type !== "plugin_param")
33621
+ return false;
33622
+ const lines = code.split(`
33623
+ `);
33624
+ const lineText = lines[source.line - 1] ?? "";
33625
+ const read = /\b([A-Za-z_]\w*)\s*\.\s*get\s*\(\s*"([^"]*)"\s*\)/.exec(lineText);
33626
+ if (!read)
33627
+ return false;
33628
+ const [, recv, key] = read;
33629
+ const ctorRe = new RegExp(`\\b${recv}\\s*=\\s*new\\s+(?:Hash|Tree|LinkedHash|ConcurrentHash|Concurrent|Identity|Weak)?Map\\s*(?:<[^>]*>)?\\s*\\(`);
33630
+ if (!lines.some((l) => ctorRe.test(l)))
33631
+ return false;
33632
+ const escapeRe2 = new RegExp(`\\w\\s*\\([^)]*(?<![\\w.])${recv}(?![\\w.])[^)]*\\)`);
33633
+ for (const l of lines) {
33634
+ if (!escapeRe2.test(l))
33635
+ continue;
33636
+ const stripped = l.replace(new RegExp(`\\b${recv}\\s*\\.\\s*\\w+\\s*\\(`, "g"), "X(");
33637
+ if (escapeRe2.test(stripped))
33638
+ return false;
33639
+ }
33640
+ const putRe = new RegExp(`\\b${recv}\\s*\\.\\s*put\\s*\\(\\s*"${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"`);
33641
+ if (lines.some((l) => putRe.test(l)))
33642
+ return false;
33643
+ return true;
33644
+ }
33200
33645
  var CSHARP_SANITIZER_RES = [
33201
33646
  { re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: "xss" },
33202
33647
  { re: /\bHtmlEncoder\s*\.\s*Encode\s*\(/, type: "xss" },
33203
- { re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" }
33648
+ { re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" },
33649
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "ldap_injection" },
33650
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "xpath_injection" },
33651
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "command_injection" },
33652
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "sql_injection" }
33204
33653
  ];
33205
33654
  function csharpSanitizedVarsByType(code) {
33206
33655
  const lines = code.split(`
@@ -33444,6 +33893,24 @@ function hasJavaImportFromPackage(packagePrefix, sourceLines) {
33444
33893
  }
33445
33894
  return false;
33446
33895
  }
33896
+ function hasSameOriginLiteralPrefix(lineText) {
33897
+ const m = lineText.match(/\(\s*(['"`])([^'"`]*)\1\s*(?:\+|,|\))/);
33898
+ const prefix = m?.[2];
33899
+ if (prefix === undefined) {
33900
+ const t = lineText.match(/\(\s*`([^`$]*)\$\{/);
33901
+ if (!t)
33902
+ return false;
33903
+ return isSameOriginQueryPrefix(t[1]);
33904
+ }
33905
+ return isSameOriginQueryPrefix(prefix);
33906
+ }
33907
+ function isSameOriginQueryPrefix(prefix) {
33908
+ if (!prefix.startsWith("/"))
33909
+ return false;
33910
+ if (prefix.startsWith("//"))
33911
+ return false;
33912
+ return prefix.includes("?");
33913
+ }
33447
33914
 
33448
33915
  class SinkFilterPass {
33449
33916
  name = "sink-filter";
@@ -33455,7 +33922,8 @@ class SinkFilterPass {
33455
33922
  const constProp = ctx.getResult("constant-propagation");
33456
33923
  const langSources = ctx.getResult("language-sources");
33457
33924
  const mergedSources = [...taintMatcher.sources, ...langSources.additionalSources];
33458
- const sources = ctx.code && ctx.language ? mergedSources.filter((s) => !isNonExecutableSourceLine(ctx.code, s.line, ctx.language)) : mergedSources;
33925
+ const executableSources = ctx.code && ctx.language ? mergedSources.filter((s) => !isNonExecutableSourceLine(ctx.code, s.line, ctx.language)) : mergedSources;
33926
+ const sources = ctx.code ? executableSources.filter((s) => !isUnwrittenLocalMapKeyRead(ctx.code, s)) : executableSources;
33459
33927
  const sinks = [...taintMatcher.sinks];
33460
33928
  for (const s of langSources.additionalSinks) {
33461
33929
  if (!sinks.some((x) => x.line === s.line && x.cwe === s.cwe && x.type === s.type)) {
@@ -33608,6 +34076,9 @@ class SinkFilterPass {
33608
34076
  if (setHeaderMatch) {
33609
34077
  return false;
33610
34078
  }
34079
+ if (sink.type === "open_redirect" && hasSameOriginLiteralPrefix(sinkLineText)) {
34080
+ return false;
34081
+ }
33611
34082
  if (sink.method === "cookie" && sink.type === "crlf") {
33612
34083
  return false;
33613
34084
  }
@@ -33747,13 +34218,23 @@ class SinkFilterPass {
33747
34218
  filtered = filtered.filter((sink) => {
33748
34219
  if (sink.type !== "command_injection")
33749
34220
  return true;
33750
- if (sink.method !== "ProcessBuilder" && sink.method !== "start")
34221
+ if (sink.method !== "ProcessBuilder" && sink.method !== "start" && sink.method !== "command") {
33751
34222
  return true;
34223
+ }
33752
34224
  const sinkLineText = sourceLines[sink.line - 1] ?? "";
33753
- if (!/\bnew\s+ProcessBuilder\s*\(/.test(sinkLineText))
34225
+ const isCtorLine = /\bnew\s+ProcessBuilder\s*\(/.test(sinkLineText);
34226
+ const isCommandLine = /\.\s*command\s*\(/.test(sinkLineText);
34227
+ if (!isCtorLine && !isCommandLine)
33754
34228
  return true;
33755
- if (PROCESS_BUILDER_ARGV_FORM_RE.test(sinkLineText))
34229
+ if (argvFirstLiteralIsShell(sinkLineText))
34230
+ return true;
34231
+ if (isCtorLine && PROCESS_BUILDER_ARGV_FORM_RE.test(sinkLineText))
34232
+ return false;
34233
+ if (isCommandLine && PROCESS_BUILDER_COMMAND_ARGV_FORM_RE.test(sinkLineText)) {
33756
34234
  return false;
34235
+ }
34236
+ if (!isCtorLine)
34237
+ return true;
33757
34238
  const ctorCall = irCalls.find((c) => c.method_name === "ProcessBuilder" && c.location.line === sink.line && (c.receiver === null || c.receiver === undefined));
33758
34239
  if (!ctorCall || ctorCall.arguments.length === 0)
33759
34240
  return true;
@@ -34023,6 +34504,15 @@ class SinkFilterPass {
34023
34504
  if (language === "java" && !HTML_CONTENT_TYPE_RE.test(ctx.code)) {
34024
34505
  filtered = filtered.filter((sink) => !(sink.type === "xss" && sink.method === "body"));
34025
34506
  }
34507
+ if (language === "java") {
34508
+ const sourceLines = ctx.code.split(`
34509
+ `);
34510
+ filtered = filtered.filter((sink) => {
34511
+ if (sink.type !== "xss" || sink.class !== undefined)
34512
+ return true;
34513
+ return !JAVA_STDOUT_PRINT_RE.test(sourceLines[sink.line - 1] ?? "");
34514
+ });
34515
+ }
34026
34516
  if (language === "java") {
34027
34517
  const guardedLines = javaTraversalRejectGuardedLines(ctx.code);
34028
34518
  if (guardedLines.size > 0) {
@@ -34108,6 +34598,43 @@ class SinkFilterPass {
34108
34598
  return true;
34109
34599
  });
34110
34600
  }
34601
+ if (language === "csharp") {
34602
+ const sourceLines = ctx.code.split(`
34603
+ `);
34604
+ const constBaseClients = new Set;
34605
+ const declRe = /\b(?:var|HttpClient)\s+([A-Za-z_]\w*)\s*=\s*new\s+HttpClient\s*\{[^}]*\bBaseAddress\s*=\s*new\s+Uri\s*\(\s*(?:@?"[^"]*"|\$?"[^"{}]*")\s*\)/;
34606
+ for (const line of sourceLines) {
34607
+ const m = declRe.exec(line);
34608
+ if (m)
34609
+ constBaseClients.add(m[1]);
34610
+ }
34611
+ if (constBaseClients.size > 0) {
34612
+ const relativeLiteralArgRe = /\.\s*(?:Get|Post|Put|Patch|Delete|Send|GetString|GetByteArray|GetStream)\w*Async\s*\(\s*(?:@?\$?)"([^"]*)"/;
34613
+ filtered = filtered.filter((sink) => {
34614
+ if (sink.type !== "ssrf")
34615
+ return true;
34616
+ const text = sourceLines[sink.line - 1] ?? "";
34617
+ let onConstClient = false;
34618
+ for (const v of constBaseClients) {
34619
+ if (new RegExp(`(?<![\\w])${escapeRegex(v)}\\s*\\.`).test(text)) {
34620
+ onConstClient = true;
34621
+ break;
34622
+ }
34623
+ }
34624
+ if (!onConstClient)
34625
+ return true;
34626
+ const m = relativeLiteralArgRe.exec(text);
34627
+ if (!m)
34628
+ return true;
34629
+ const lit = m[1];
34630
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(lit))
34631
+ return true;
34632
+ if (lit.startsWith("//"))
34633
+ return true;
34634
+ return false;
34635
+ });
34636
+ }
34637
+ }
34111
34638
  if (["javascript", "typescript"].includes(language)) {
34112
34639
  const sourceLines = ctx.code.split(`
34113
34640
  `);
@@ -34724,7 +35251,7 @@ function isXPathQuoteEscapedOnLine(lineText, varName) {
34724
35251
  return seen > 0;
34725
35252
  }
34726
35253
 
34727
- // node_modules/circle-ir/dist/analysis/passes/sink-semantics-pass.js
35254
+ // ../circle-ir/dist/analysis/passes/sink-semantics-pass.js
34728
35255
  function buildRegistry(entries) {
34729
35256
  const registry = new Map;
34730
35257
  for (const entry of entries) {
@@ -34772,7 +35299,7 @@ class SinkSemanticsPass {
34772
35299
  }
34773
35300
  }
34774
35301
 
34775
- // node_modules/circle-ir/dist/analysis/dependency-versions.js
35302
+ // ../circle-ir/dist/analysis/dependency-versions.js
34776
35303
  function resolveFastjsonFromPom(pomXml) {
34777
35304
  if (!pomXml)
34778
35305
  return null;
@@ -34950,7 +35477,7 @@ function fileConfiguresSnakeYamlSafely(source) {
34950
35477
  return false;
34951
35478
  }
34952
35479
 
34953
- // node_modules/circle-ir/dist/analysis/passes/deserialization-safety-gate-pass.js
35480
+ // ../circle-ir/dist/analysis/passes/deserialization-safety-gate-pass.js
34954
35481
  var FASTJSON_METHODS = new Set(["parseObject", "parse"]);
34955
35482
  var FASTJSON_CLASSES = new Set(["JSON", "JSONObject"]);
34956
35483
  var JACKSON_METHODS = new Set(["readValue", "convertValue", "treeToValue"]);
@@ -35027,7 +35554,7 @@ class DeserializationSafetyGatePass {
35027
35554
  }
35028
35555
  }
35029
35556
 
35030
- // node_modules/circle-ir/dist/analysis/passes/prompt-injection-safety-gate-pass.js
35557
+ // ../circle-ir/dist/analysis/passes/prompt-injection-safety-gate-pass.js
35031
35558
  function isDelimiterLiteral(lit) {
35032
35559
  const s = lit.trim();
35033
35560
  if (s.length === 0)
@@ -35270,7 +35797,7 @@ class PromptInjectionSafetyGatePass {
35270
35797
  }
35271
35798
  }
35272
35799
 
35273
- // node_modules/circle-ir/dist/analysis/passes/speculative-prompt-param-source-pass.js
35800
+ // ../circle-ir/dist/analysis/passes/speculative-prompt-param-source-pass.js
35274
35801
  function returnOrAssignRhs(text) {
35275
35802
  const t = text.trim();
35276
35803
  const ret = t.match(/^return\s+(.+?);?$/);
@@ -35396,7 +35923,7 @@ class SpeculativePromptParamSourcePass {
35396
35923
  }
35397
35924
  }
35398
35925
 
35399
- // node_modules/circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
35926
+ // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
35400
35927
  var REFLECTION_SINK_METHODS = new Set([
35401
35928
  "forName",
35402
35929
  "newInstance",
@@ -35584,7 +36111,7 @@ class CliMainReflectionSuppressPass {
35584
36111
  }
35585
36112
  }
35586
36113
 
35587
- // node_modules/circle-ir/dist/analysis/passes/library-profile-sink-gate-pass.js
36114
+ // ../circle-ir/dist/analysis/passes/library-profile-sink-gate-pass.js
35588
36115
  var DROPPED_SINK_TYPES = new Set([
35589
36116
  "log_injection"
35590
36117
  ]);
@@ -35658,7 +36185,7 @@ class LibraryProfileCwe22PathGatePass {
35658
36185
  droppedBySourceType: {}
35659
36186
  };
35660
36187
  }
35661
- const flows = graph.ir.taint.flows;
36188
+ const flows = ctx.hasResult("interprocedural") ? ctx.getResult("interprocedural").additionalFlows : graph.ir.taint.flows;
35662
36189
  if (!flows || flows.length === 0) {
35663
36190
  return {
35664
36191
  profile,
@@ -35668,19 +36195,18 @@ class LibraryProfileCwe22PathGatePass {
35668
36195
  };
35669
36196
  }
35670
36197
  const droppedBySourceType = {};
35671
- const kept = [];
36198
+ let tagged = 0;
35672
36199
  for (const flow of flows) {
35673
36200
  if (flow.sink_type === "path_traversal" && CWE22_SPECULATIVE_SOURCE_TYPES.has(flow.source_type)) {
35674
36201
  droppedBySourceType[flow.source_type] = (droppedBySourceType[flow.source_type] ?? 0) + 1;
35675
- continue;
36202
+ const tags = flow.tags ?? [];
36203
+ if (!tags.includes(LIBRARY_API_SURFACE_TAG)) {
36204
+ flow.tags = [...tags, LIBRARY_API_SURFACE_TAG];
36205
+ }
36206
+ tagged++;
35676
36207
  }
35677
- kept.push(flow);
35678
- }
35679
- const dropped = flows.length - kept.length;
35680
- if (dropped > 0) {
35681
- flows.length = 0;
35682
- flows.push(...kept);
35683
36208
  }
36209
+ const dropped = tagged;
35684
36210
  return {
35685
36211
  profile,
35686
36212
  applied: true,
@@ -35690,7 +36216,7 @@ class LibraryProfileCwe22PathGatePass {
35690
36216
  }
35691
36217
  }
35692
36218
 
35693
- // node_modules/circle-ir/dist/analysis/passes/library-profile-xss-gate-pass.js
36219
+ // ../circle-ir/dist/analysis/passes/library-profile-xss-gate-pass.js
35694
36220
  var XSS_NON_HTML_OUTPUT_CLASSES = new Set([
35695
36221
  "StringBuilder",
35696
36222
  "StringBuffer",
@@ -35771,7 +36297,7 @@ class LibraryProfileXssGatePass {
35771
36297
  }
35772
36298
  }
35773
36299
 
35774
- // node_modules/circle-ir/dist/analysis/passes/taint-propagation-pass.js
36300
+ // ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
35775
36301
  class TaintPropagationPass {
35776
36302
  name = "taint-propagation";
35777
36303
  category = "security";
@@ -35826,6 +36352,63 @@ class TaintPropagationPass {
35826
36352
  flows.push(f);
35827
36353
  return true;
35828
36354
  };
36355
+ if (ctx.language === "python" && typeof ctx.code === "string") {
36356
+ const pyTainted = buildPythonTaintedVars(ctx.code);
36357
+ if (pyTainted.size > 0) {
36358
+ for (const v of findPythonTrustBoundaryViolations(ctx.code, pyTainted)) {
36359
+ const sink = sinks.find((sk) => sk.line === v.sinkLine && sk.type === "trust_boundary");
36360
+ if (!sink)
36361
+ continue;
36362
+ const src = sources.find((sc) => sc.line === v.sourceLine) ?? sources.filter((sc) => sc.line <= v.sinkLine).sort((a, b) => b.line - a.line)[0];
36363
+ if (!src)
36364
+ continue;
36365
+ if (constProp.unreachableLines.has(v.sinkLine))
36366
+ continue;
36367
+ pushIfNew({
36368
+ source_line: src.line,
36369
+ sink_line: v.sinkLine,
36370
+ source_type: src.type,
36371
+ sink_type: "trust_boundary",
36372
+ path: [
36373
+ { variable: src.variable ?? "session_key", line: src.line, type: "source" },
36374
+ { variable: src.variable ?? "session_key", line: v.sinkLine, type: "sink" }
36375
+ ],
36376
+ confidence: (src.confidence ?? 1) * (sink.confidence ?? 1),
36377
+ sanitized: false
36378
+ });
36379
+ }
36380
+ }
36381
+ }
36382
+ if (ctx.language === "python" && typeof ctx.code === "string") {
36383
+ const pyTainted = buildPythonTaintedVars(ctx.code);
36384
+ if (pyTainted.size > 0) {
36385
+ const xssSanitizerLines = sanitizers.filter((sa) => sa.sanitizes.includes("xss")).map((sa) => sa.line);
36386
+ for (const rx of findPythonReturnXSSSinks(ctx.code, pyTainted)) {
36387
+ if (constProp.unreachableLines.has(rx.sinkLine))
36388
+ continue;
36389
+ const sink = sinks.find((sk) => sk.line === rx.sinkLine && sk.type === "xss");
36390
+ if (!sink)
36391
+ continue;
36392
+ const src = sources.filter((sc) => sc.line <= rx.sinkLine).sort((a, b) => b.line - a.line)[0];
36393
+ if (!src)
36394
+ continue;
36395
+ if (xssSanitizerLines.some((l) => l >= src.line && l <= rx.sinkLine))
36396
+ continue;
36397
+ pushIfNew({
36398
+ source_line: src.line,
36399
+ sink_line: rx.sinkLine,
36400
+ source_type: src.type,
36401
+ sink_type: "xss",
36402
+ path: [
36403
+ { variable: src.variable ?? "response", line: src.line, type: "source" },
36404
+ { variable: src.variable ?? "response", line: rx.sinkLine, type: "sink" }
36405
+ ],
36406
+ confidence: (src.confidence ?? 1) * (sink.confidence ?? 1),
36407
+ sanitized: false
36408
+ });
36409
+ }
36410
+ }
36411
+ }
35829
36412
  const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines, types) ?? [];
35830
36413
  for (const f of arrayFlows) {
35831
36414
  pushIfNew(f);
@@ -36515,6 +37098,108 @@ function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
36515
37098
  }
36516
37099
  return false;
36517
37100
  }
37101
+ var MASKABLE_STRING_LANGUAGES = new Set(["python", "javascript", "typescript"]);
37102
+ function maskStringLiteralsForTokenScan(expr) {
37103
+ let out2 = "";
37104
+ let i2 = 0;
37105
+ const braceExprs = (body2) => {
37106
+ let acc = "";
37107
+ for (let j = 0;j < body2.length; ) {
37108
+ if (body2[j] === "{" && body2[j + 1] === "{") {
37109
+ j += 2;
37110
+ continue;
37111
+ }
37112
+ if (body2[j] === "}" && body2[j + 1] === "}") {
37113
+ j += 2;
37114
+ continue;
37115
+ }
37116
+ if (body2[j] === "{") {
37117
+ j++;
37118
+ let depth = 1;
37119
+ let inner = "";
37120
+ while (j < body2.length && depth > 0) {
37121
+ if (body2[j] === "{")
37122
+ depth++;
37123
+ else if (body2[j] === "}") {
37124
+ depth--;
37125
+ if (depth === 0)
37126
+ break;
37127
+ }
37128
+ inner += body2[j];
37129
+ j++;
37130
+ }
37131
+ j++;
37132
+ acc += " " + maskStringLiteralsForTokenScan(inner) + " ";
37133
+ continue;
37134
+ }
37135
+ j++;
37136
+ }
37137
+ return acc;
37138
+ };
37139
+ while (i2 < expr.length) {
37140
+ const ch = expr[i2];
37141
+ if (ch === "`") {
37142
+ i2++;
37143
+ let acc = "";
37144
+ while (i2 < expr.length && expr[i2] !== "`") {
37145
+ if (expr[i2] === "\\") {
37146
+ i2 += 2;
37147
+ continue;
37148
+ }
37149
+ if (expr[i2] === "$" && expr[i2 + 1] === "{") {
37150
+ i2 += 2;
37151
+ let depth = 1;
37152
+ let inner = "";
37153
+ while (i2 < expr.length && depth > 0) {
37154
+ if (expr[i2] === "{")
37155
+ depth++;
37156
+ else if (expr[i2] === "}") {
37157
+ depth--;
37158
+ if (depth === 0)
37159
+ break;
37160
+ }
37161
+ inner += expr[i2];
37162
+ i2++;
37163
+ }
37164
+ i2++;
37165
+ acc += " " + maskStringLiteralsForTokenScan(inner) + " ";
37166
+ continue;
37167
+ }
37168
+ i2++;
37169
+ }
37170
+ i2++;
37171
+ out2 += " " + acc + " ";
37172
+ continue;
37173
+ }
37174
+ if (ch === '"' || ch === "'") {
37175
+ const pm = /[A-Za-z_][A-Za-z0-9_]*$/.exec(out2);
37176
+ const prefix = pm ? pm[0] : "";
37177
+ const isPrefix = prefix.length > 0 && /^[rRbBuUfF]+$/.test(prefix);
37178
+ const isF = isPrefix && /[fF]/.test(prefix);
37179
+ if (isPrefix)
37180
+ out2 = out2.slice(0, out2.length - prefix.length) + " ";
37181
+ const triple = expr.startsWith(ch.repeat(3), i2);
37182
+ const delim = triple ? ch.repeat(3) : ch;
37183
+ i2 += delim.length;
37184
+ let body2 = "";
37185
+ while (i2 < expr.length && !expr.startsWith(delim, i2)) {
37186
+ if (expr[i2] === "\\") {
37187
+ body2 += " ";
37188
+ i2 += 2;
37189
+ continue;
37190
+ }
37191
+ body2 += expr[i2];
37192
+ i2++;
37193
+ }
37194
+ i2 += delim.length;
37195
+ out2 += isF ? " " + braceExprs(body2) + " " : " ";
37196
+ continue;
37197
+ }
37198
+ out2 += ch;
37199
+ i2++;
37200
+ }
37201
+ return out2;
37202
+ }
36518
37203
  function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
36519
37204
  const flows = [];
36520
37205
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
@@ -36904,7 +37589,8 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36904
37589
  const expr = arg.expression;
36905
37590
  if (!expr)
36906
37591
  continue;
36907
- const exprIdentMatches = expr.match(/[\p{L}\p{N}_]+/gu);
37592
+ const scannable = language !== undefined && MASKABLE_STRING_LANGUAGES.has(language) ? maskStringLiteralsForTokenScan(expr) : expr;
37593
+ const exprIdentMatches = scannable.match(/[\p{L}\p{N}_]+/gu);
36908
37594
  let candidates = complexSources.length > 0 ? complexSources.slice() : null;
36909
37595
  if (exprIdentMatches) {
36910
37596
  for (const id of new Set(exprIdentMatches)) {
@@ -37054,7 +37740,7 @@ function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
37054
37740
  return sources[0];
37055
37741
  }
37056
37742
 
37057
- // node_modules/circle-ir/dist/analysis/interprocedural.js
37743
+ // ../circle-ir/dist/analysis/interprocedural.js
37058
37744
  function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourcesOrSanitizers, sinksOrOptions, sanitizersArg, optionsArg = {}) {
37059
37745
  let graph;
37060
37746
  let sources;
@@ -37568,7 +38254,7 @@ function findTaintBridges2(result) {
37568
38254
  return bridges;
37569
38255
  }
37570
38256
 
37571
- // node_modules/circle-ir/dist/analysis/passes/interprocedural-pass.js
38257
+ // ../circle-ir/dist/analysis/passes/interprocedural-pass.js
37572
38258
  class InterproceduralPass {
37573
38259
  name = "interprocedural";
37574
38260
  category = "security";
@@ -37774,7 +38460,7 @@ class InterproceduralPass {
37774
38460
  }
37775
38461
  }
37776
38462
 
37777
- // node_modules/circle-ir/dist/analysis/passes/dead-code-pass.js
38463
+ // ../circle-ir/dist/analysis/passes/dead-code-pass.js
37778
38464
  class DeadCodePass {
37779
38465
  name = "dead-code";
37780
38466
  category = "reliability";
@@ -37850,7 +38536,7 @@ class DeadCodePass {
37850
38536
  }
37851
38537
  }
37852
38538
 
37853
- // node_modules/circle-ir/dist/analysis/passes/missing-await-pass.js
38539
+ // ../circle-ir/dist/analysis/passes/missing-await-pass.js
37854
38540
  var ASYNC_METHODS = new Set([
37855
38541
  "readFile",
37856
38542
  "writeFile",
@@ -37929,7 +38615,7 @@ class MissingAwaitPass {
37929
38615
  }
37930
38616
  }
37931
38617
 
37932
- // node_modules/circle-ir/dist/analysis/passes/n-plus-one-pass.js
38618
+ // ../circle-ir/dist/analysis/passes/n-plus-one-pass.js
37933
38619
  var HIGH_CONFIDENCE_DB_METHODS = new Set([
37934
38620
  "executeQuery",
37935
38621
  "executeUpdate",
@@ -38052,7 +38738,7 @@ class NPlusOnePass {
38052
38738
  }
38053
38739
  }
38054
38740
 
38055
- // node_modules/circle-ir/dist/analysis/passes/missing-public-doc-pass.js
38741
+ // ../circle-ir/dist/analysis/passes/missing-public-doc-pass.js
38056
38742
  var TEST_PATH_RE = /[/._](test|tests|spec|specs|__tests?__|__mocks?__)[/._]/i;
38057
38743
  var UTIL_DIR_RE = /[/](utils?|helpers?|internal|private|common|shared)[/]/i;
38058
38744
  function hasDocCommentBefore(lines, startLine) {
@@ -38159,7 +38845,7 @@ class MissingPublicDocPass {
38159
38845
  }
38160
38846
  }
38161
38847
 
38162
- // node_modules/circle-ir/dist/analysis/passes/todo-in-prod-pass.js
38848
+ // ../circle-ir/dist/analysis/passes/todo-in-prod-pass.js
38163
38849
  var TEST_PATH_RE2 = /[/._](test|tests|spec|specs|__tests?__|__mocks?__)[/._]/i;
38164
38850
  var MARKER_RE = /(?:\/\/|#|--|^\s*\*)\s*(TODO|FIXME|HACK|XXX)\b/i;
38165
38851
  function markerSeverity(marker) {
@@ -38205,7 +38891,7 @@ class TodoInProdPass {
38205
38891
  }
38206
38892
  }
38207
38893
 
38208
- // node_modules/circle-ir/dist/analysis/passes/string-concat-loop-pass.js
38894
+ // ../circle-ir/dist/analysis/passes/string-concat-loop-pass.js
38209
38895
  var CONCAT_RE = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\+=/;
38210
38896
  var NUMERIC_VAR_RE = /^(i|j|k|n|m|x|y|z|count|sum|total|size|index|len|length|num|idx|score|counter|offset|pos|col|row|result|ret|value|acc|bits|byte|bytes|flag|flags|step|delta|diff|dist|min|max|avg|mean|page|line|err|error)$/i;
38211
38897
  var NUMERIC_SUFFIX_RE = /(Count|Sum|Total|Size|Index|Length|Offset|Position|Score|Counter|Num|Amount|Val|Idx|Len|Max|Min|Avg|Delta|Diff|Step|Flag|Flags|Bits|Byte|Bytes|Calls|Items|Nodes|Edges|Blocks|Lines|Chars|Entries|Records|Rows)$/;
@@ -38268,7 +38954,7 @@ class StringConcatLoopPass {
38268
38954
  }
38269
38955
  }
38270
38956
 
38271
- // node_modules/circle-ir/dist/analysis/passes/sync-io-async-pass.js
38957
+ // ../circle-ir/dist/analysis/passes/sync-io-async-pass.js
38272
38958
  var BLOCKING_METHODS = new Set([
38273
38959
  "sleep"
38274
38960
  ]);
@@ -38329,7 +39015,7 @@ class SyncIoAsyncPass {
38329
39015
  }
38330
39016
  }
38331
39017
 
38332
- // node_modules/circle-ir/dist/analysis/passes/unchecked-return-pass.js
39018
+ // ../circle-ir/dist/analysis/passes/unchecked-return-pass.js
38333
39019
  var MUST_CHECK_HIGH = new Set([
38334
39020
  "createNewFile",
38335
39021
  "mkdir",
@@ -38402,7 +39088,7 @@ class UncheckedReturnPass {
38402
39088
  }
38403
39089
  }
38404
39090
 
38405
- // node_modules/circle-ir/dist/analysis/passes/null-deref-pass.js
39091
+ // ../circle-ir/dist/analysis/passes/null-deref-pass.js
38406
39092
  var NULL_EXPR_RE = /^\s*(null|None|undefined)\s*$/;
38407
39093
  function escRe(s) {
38408
39094
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -38484,7 +39170,7 @@ class NullDerefPass {
38484
39170
  }
38485
39171
  }
38486
39172
 
38487
- // node_modules/circle-ir/dist/analysis/passes/resource-leak-pass.js
39173
+ // ../circle-ir/dist/analysis/passes/resource-leak-pass.js
38488
39174
  function escapeRegex2(s) {
38489
39175
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38490
39176
  }
@@ -38761,7 +39447,7 @@ class ResourceLeakPass {
38761
39447
  }
38762
39448
  }
38763
39449
 
38764
- // node_modules/circle-ir/dist/graph/scope-graph.js
39450
+ // ../circle-ir/dist/graph/scope-graph.js
38765
39451
  function hasDeclKeyword(lineText, language) {
38766
39452
  switch (language) {
38767
39453
  case "java":
@@ -38801,7 +39487,7 @@ class ScopeGraph {
38801
39487
  }
38802
39488
  }
38803
39489
 
38804
- // node_modules/circle-ir/dist/analysis/passes/variable-shadowing-pass.js
39490
+ // ../circle-ir/dist/analysis/passes/variable-shadowing-pass.js
38805
39491
  var SKIP_NAMES = new Set([
38806
39492
  "let",
38807
39493
  "const",
@@ -38959,7 +39645,7 @@ class VariableShadowingPass {
38959
39645
  }
38960
39646
  }
38961
39647
 
38962
- // node_modules/circle-ir/dist/analysis/passes/leaked-global-pass.js
39648
+ // ../circle-ir/dist/analysis/passes/leaked-global-pass.js
38963
39649
  var SKIP_NAMES2 = new Set([
38964
39650
  "_",
38965
39651
  "e",
@@ -39046,7 +39732,7 @@ class LeakedGlobalPass {
39046
39732
  }
39047
39733
  }
39048
39734
 
39049
- // node_modules/circle-ir/dist/analysis/passes/unused-variable-pass.js
39735
+ // ../circle-ir/dist/analysis/passes/unused-variable-pass.js
39050
39736
  var SKIP_NAMES3 = new Set([
39051
39737
  "_",
39052
39738
  "unused",
@@ -39191,7 +39877,7 @@ class UnusedVariablePass {
39191
39877
  }
39192
39878
  }
39193
39879
 
39194
- // node_modules/circle-ir/dist/analysis/passes/dependency-fan-out-pass.js
39880
+ // ../circle-ir/dist/analysis/passes/dependency-fan-out-pass.js
39195
39881
  var DEFAULT_THRESHOLD = 20;
39196
39882
 
39197
39883
  class DependencyFanOutPass {
@@ -39223,7 +39909,7 @@ class DependencyFanOutPass {
39223
39909
  }
39224
39910
  }
39225
39911
 
39226
- // node_modules/circle-ir/dist/analysis/passes/stale-doc-ref-pass.js
39912
+ // ../circle-ir/dist/analysis/passes/stale-doc-ref-pass.js
39227
39913
  var DOC_BLOCK_RE = /\/\*\*([\s\S]*?)\*\//g;
39228
39914
  var LINK_RE = /\{@link\s+([\w.#]+)/g;
39229
39915
  var SEE_RE = /@see\s+([\w.#]+)/g;
@@ -39302,7 +39988,7 @@ class StaleDocRefPass {
39302
39988
  }
39303
39989
  }
39304
39990
 
39305
- // node_modules/circle-ir/dist/analysis/passes/infinite-loop-pass.js
39991
+ // ../circle-ir/dist/analysis/passes/infinite-loop-pass.js
39306
39992
  var EXIT_KEYWORDS = /\b(return|throw|raise|break|System\.exit|process\.exit|os\._exit|exit!\()\b/;
39307
39993
  var ITERATOR_LOOP_PATTERNS = [
39308
39994
  /\bfor\s*\([^)]*\s+of\s+/,
@@ -39421,7 +40107,7 @@ class InfiniteLoopPass {
39421
40107
  }
39422
40108
  }
39423
40109
 
39424
- // node_modules/circle-ir/dist/analysis/passes/deep-inheritance-pass.js
40110
+ // ../circle-ir/dist/analysis/passes/deep-inheritance-pass.js
39425
40111
  var DEPTH_THRESHOLD = 5;
39426
40112
  var CYCLE_GUARD = 20;
39427
40113
 
@@ -39484,7 +40170,7 @@ class DeepInheritancePass {
39484
40170
  }
39485
40171
  }
39486
40172
 
39487
- // node_modules/circle-ir/dist/analysis/passes/redundant-loop-pass.js
40173
+ // ../circle-ir/dist/analysis/passes/redundant-loop-pass.js
39488
40174
  var LENGTH_PATTERN = /\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\.\s*(?:length|size\(\)|count\(\))/g;
39489
40175
  var LENGTH_PATTERN_METHODS = /\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\.\s*(?:size\(\)|count\(\))/g;
39490
40176
  var OBJECT_STATIC_PATTERN = /\bObject\s*\.\s*(?:keys|values|entries)\s*\(\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*\)/g;
@@ -39607,7 +40293,7 @@ class RedundantLoopPass {
39607
40293
  }
39608
40294
  }
39609
40295
 
39610
- // node_modules/circle-ir/dist/analysis/passes/unbounded-collection-pass.js
40296
+ // ../circle-ir/dist/analysis/passes/unbounded-collection-pass.js
39611
40297
  var GROW_METHODS = {
39612
40298
  java: new Set(["add", "put", "offer", "push", "addAll", "addFirst", "addLast", "enqueue", "insert"]),
39613
40299
  javascript: new Set(["push", "set", "add", "unshift", "append", "prepend"]),
@@ -39731,7 +40417,7 @@ class UnboundedCollectionPass {
39731
40417
  }
39732
40418
  }
39733
40419
 
39734
- // node_modules/circle-ir/dist/analysis/passes/serial-await-pass.js
40420
+ // ../circle-ir/dist/analysis/passes/serial-await-pass.js
39735
40421
  var AWAIT_ASSIGN_RE = /(?:const|let|var)?\s*(\w+)\s*=\s*await\s/;
39736
40422
  var AWAIT_RE = /\bawait\s/;
39737
40423
 
@@ -39827,7 +40513,7 @@ class SerialAwaitPass {
39827
40513
  }
39828
40514
  }
39829
40515
 
39830
- // node_modules/circle-ir/dist/analysis/passes/react-inline-jsx-pass.js
40516
+ // ../circle-ir/dist/analysis/passes/react-inline-jsx-pass.js
39831
40517
  var JSX_COMPONENT_RE = /<[A-Z][A-Za-z0-9]*/;
39832
40518
  var INLINE_OBJECT_RE = /\s([A-Za-z][A-Za-z0-9_]*)=\{\{/g;
39833
40519
  var INLINE_ARROW_RE = /\s([A-Za-z][A-Za-z0-9_]*)=\{(?:\(|[A-Za-z_$]).*?=>/g;
@@ -39931,7 +40617,7 @@ class ReactInlineJsxPass {
39931
40617
  }
39932
40618
  }
39933
40619
 
39934
- // node_modules/circle-ir/dist/graph/exception-flow-graph.js
40620
+ // ../circle-ir/dist/graph/exception-flow-graph.js
39935
40621
  class ExceptionFlowGraph2 {
39936
40622
  pairs;
39937
40623
  catchEntryIds;
@@ -39982,7 +40668,7 @@ class ExceptionFlowGraph2 {
39982
40668
  }
39983
40669
  }
39984
40670
 
39985
- // node_modules/circle-ir/dist/analysis/passes/swallowed-exception-pass.js
40671
+ // ../circle-ir/dist/analysis/passes/swallowed-exception-pass.js
39986
40672
  var MEANINGFUL_ACTION_RE = /\b(throw|raise|log|logger|console\.(error|warn|log|debug|info)|System\.(out|err)\.|print(?:ln|f)?|warn|error|debug|info|fatal|LOGGER|LOG|logging\.(warning|error|debug|info|critical))\b|\breturn\s+\S/;
39987
40673
 
39988
40674
  class SwallowedExceptionPass {
@@ -40080,7 +40766,7 @@ class SwallowedExceptionPass {
40080
40766
  }
40081
40767
  }
40082
40768
 
40083
- // node_modules/circle-ir/dist/analysis/passes/broad-catch-pass.js
40769
+ // ../circle-ir/dist/analysis/passes/broad-catch-pass.js
40084
40770
  var JAVA_BROAD_RE = /catch\s*\(\s*(Exception|Throwable|RuntimeException|Error)\s/;
40085
40771
  var PYTHON_BROAD_RE = /^\s*except\s*:|except\s+(Exception|BaseException)\b/;
40086
40772
 
@@ -40136,7 +40822,7 @@ class BroadCatchPass {
40136
40822
  }
40137
40823
  }
40138
40824
 
40139
- // node_modules/circle-ir/dist/analysis/passes/unhandled-exception-pass.js
40825
+ // ../circle-ir/dist/analysis/passes/unhandled-exception-pass.js
40140
40826
  var JS_THROW_RE = /^\s*throw\s+/;
40141
40827
  var PYTHON_RAISE_RE = /^\s*raise\b/;
40142
40828
  function isValidationThrow(lines, throwLine) {
@@ -40306,7 +40992,7 @@ class UnhandledExceptionPass {
40306
40992
  }
40307
40993
  }
40308
40994
 
40309
- // node_modules/circle-ir/dist/analysis/passes/double-close-pass.js
40995
+ // ../circle-ir/dist/analysis/passes/double-close-pass.js
40310
40996
  var RESOURCE_CTORS2 = new Set([
40311
40997
  "FileInputStream",
40312
40998
  "FileOutputStream",
@@ -40421,7 +41107,7 @@ class DoubleClosePass {
40421
41107
  }
40422
41108
  }
40423
41109
 
40424
- // node_modules/circle-ir/dist/analysis/passes/use-after-close-pass.js
41110
+ // ../circle-ir/dist/analysis/passes/use-after-close-pass.js
40425
41111
  var RESOURCE_CTORS3 = new Set([
40426
41112
  "FileInputStream",
40427
41113
  "FileOutputStream",
@@ -40529,7 +41215,7 @@ class UseAfterClosePass {
40529
41215
  }
40530
41216
  }
40531
41217
 
40532
- // node_modules/circle-ir/dist/graph/dominator-graph.js
41218
+ // ../circle-ir/dist/graph/dominator-graph.js
40533
41219
  function computeRPO(cfg, entryId) {
40534
41220
  const outgoing = new Map;
40535
41221
  for (const edge of cfg.edges) {
@@ -40694,7 +41380,7 @@ class DominatorGraph2 {
40694
41380
  }
40695
41381
  }
40696
41382
 
40697
- // node_modules/circle-ir/dist/analysis/passes/cleanup-verify-pass.js
41383
+ // ../circle-ir/dist/analysis/passes/cleanup-verify-pass.js
40698
41384
  var RESOURCE_CTORS4 = new Set([
40699
41385
  "FileInputStream",
40700
41386
  "FileOutputStream",
@@ -40819,7 +41505,7 @@ class CleanupVerifyPass {
40819
41505
  }
40820
41506
  }
40821
41507
 
40822
- // node_modules/circle-ir/dist/analysis/passes/missing-override-pass.js
41508
+ // ../circle-ir/dist/analysis/passes/missing-override-pass.js
40823
41509
  class MissingOverridePass {
40824
41510
  name = "missing-override";
40825
41511
  category = "maintainability";
@@ -40906,7 +41592,7 @@ class MissingOverridePass {
40906
41592
  }
40907
41593
  }
40908
41594
 
40909
- // node_modules/circle-ir/dist/analysis/passes/unused-interface-method-pass.js
41595
+ // ../circle-ir/dist/analysis/passes/unused-interface-method-pass.js
40910
41596
  class UnusedInterfaceMethodPass {
40911
41597
  name = "unused-interface-method";
40912
41598
  category = "maintainability";
@@ -40949,7 +41635,7 @@ class UnusedInterfaceMethodPass {
40949
41635
  }
40950
41636
  }
40951
41637
 
40952
- // node_modules/circle-ir/dist/analysis/passes/blocking-main-thread-pass.js
41638
+ // ../circle-ir/dist/analysis/passes/blocking-main-thread-pass.js
40953
41639
  var HTTP_DECORATORS = new Set([
40954
41640
  "Get",
40955
41641
  "Post",
@@ -40988,6 +41674,18 @@ var CRYPTO_BLOCKING_METHODS = new Set([
40988
41674
  "deriveKeySync"
40989
41675
  ]);
40990
41676
  var SYNC_SUFFIX_RE2 = /Sync$/;
41677
+ var ROUTER_METHODS = new Set([
41678
+ "get",
41679
+ "post",
41680
+ "put",
41681
+ "patch",
41682
+ "delete",
41683
+ "all",
41684
+ "options",
41685
+ "head",
41686
+ "use"
41687
+ ]);
41688
+ var FN_ARG_PARAMS_RE = /^(?:async\s+)?(?:function\s*[\w$]*\s*)?\(([^)]*)\)\s*(?:=>|\{)|^(?:async\s+)?([A-Za-z_$][\w$]*)\s*=>/;
40991
41689
 
40992
41690
  class BlockingMainThreadPass {
40993
41691
  name = "blocking-main-thread";
@@ -41010,8 +41708,10 @@ class BlockingMainThreadPass {
41010
41708
  }
41011
41709
  }
41012
41710
  }
41013
- if (handlerRanges.length === 0)
41711
+ const inlineHandlers = this.collectInlineHandlerNames(graph.ir.calls);
41712
+ if (handlerRanges.length === 0 && inlineHandlers.size === 0) {
41014
41713
  return { blockingInHandlers: [] };
41714
+ }
41015
41715
  const blockingInHandlers = [];
41016
41716
  for (const call of graph.ir.calls) {
41017
41717
  const name2 = call.method_name;
@@ -41020,7 +41720,8 @@ class BlockingMainThreadPass {
41020
41720
  if (!isCrypto && !isSyncSuffix)
41021
41721
  continue;
41022
41722
  const line = call.location.line;
41023
- const range = handlerRanges.find((r) => line >= r.start && line <= r.end);
41723
+ const enclosing = call.in_method ?? null;
41724
+ const range = handlerRanges.find((r) => line >= r.start && line <= r.end) ?? (enclosing && inlineHandlers.has(enclosing) ? { start: line, end: line, name: enclosing } : undefined);
41024
41725
  if (!range)
41025
41726
  continue;
41026
41727
  const reason = isCrypto ? "crypto" : "sync-suffix";
@@ -41042,6 +41743,24 @@ class BlockingMainThreadPass {
41042
41743
  }
41043
41744
  return { blockingInHandlers };
41044
41745
  }
41746
+ collectInlineHandlerNames(calls) {
41747
+ const names = new Set;
41748
+ for (const call of calls) {
41749
+ if (!ROUTER_METHODS.has(call.method_name))
41750
+ continue;
41751
+ for (const arg of call.arguments) {
41752
+ const match = FN_ARG_PARAMS_RE.exec((arg.expression ?? "").trim());
41753
+ if (!match)
41754
+ continue;
41755
+ const params = (match[1] ?? match[2] ?? "").split(",").map((p) => p.trim().toLowerCase()).filter((p) => p.length > 0);
41756
+ if (params.some((p) => HANDLER_PARAM_NAMES.has(p))) {
41757
+ names.add(`${call.method_name}_handler`);
41758
+ break;
41759
+ }
41760
+ }
41761
+ }
41762
+ return names;
41763
+ }
41045
41764
  isRequestHandler(method) {
41046
41765
  if (method.annotations.some((a) => HTTP_DECORATORS.has(a)))
41047
41766
  return true;
@@ -41052,7 +41771,7 @@ class BlockingMainThreadPass {
41052
41771
  }
41053
41772
  }
41054
41773
 
41055
- // node_modules/circle-ir/dist/analysis/passes/excessive-allocation-pass.js
41774
+ // ../circle-ir/dist/analysis/passes/excessive-allocation-pass.js
41056
41775
  var ALLOC_PATTERNS = {
41057
41776
  javascript: /\bnew\s+(Array|Map|Set|Object|WeakMap|WeakSet|Error|RegExp|Date|Buffer|Uint8Array|Int8Array|Float32Array|ArrayBuffer)\s*[(<]|\bArray\.from\s*\(|\bstructuredClone\s*\(|\bObject\.create\s*\(/,
41058
41777
  typescript: /\bnew\s+(Array|Map|Set|Object|WeakMap|WeakSet|Error|RegExp|Date|Buffer|Uint8Array|Int8Array|Float32Array|ArrayBuffer)\s*[(<]|\bArray\.from\s*\(|\bstructuredClone\s*\(|\bObject\.create\s*\(/,
@@ -41119,7 +41838,7 @@ class ExcessiveAllocationPass {
41119
41838
  }
41120
41839
  }
41121
41840
 
41122
- // node_modules/circle-ir/dist/analysis/passes/missing-stream-pass.js
41841
+ // ../circle-ir/dist/analysis/passes/missing-stream-pass.js
41123
41842
  var JS_WHOLE_LOAD_RE = /\b(?:readFileSync|fs\.readFile\b|response\.text\b|response\.json\b|res\.text\b|res\.json\b|body\.text\b|body\.json\b)\s*\(/;
41124
41843
  var JS_STREAM_RE = /\.pipe\s*\(|\.on\s*\(\s*['"]data['"]|for\s+await\s*\(|\bcreateReadStream\b|\bstream\b/i;
41125
41844
  var JAVA_WHOLE_READ_RE = /\bFiles\.readAllBytes\s*\(|\bFiles\.readAllLines\s*\(|\bFiles\.readString\s*\(|\bnew\s+BufferedReader\s*\(|\bFileInputStream\b/;
@@ -41266,7 +41985,7 @@ class MissingStreamPass {
41266
41985
  }
41267
41986
  }
41268
41987
 
41269
- // node_modules/circle-ir/dist/analysis/passes/god-class-pass.js
41988
+ // ../circle-ir/dist/analysis/passes/god-class-pass.js
41270
41989
  var WMC_THRESHOLD = 47;
41271
41990
  var LCOM2_THRESHOLD = 0.8;
41272
41991
  var CBO_THRESHOLD = 14;
@@ -41432,7 +42151,7 @@ class GodClassPass {
41432
42151
  }
41433
42152
  }
41434
42153
 
41435
- // node_modules/circle-ir/dist/analysis/passes/naming-convention-pass.js
42154
+ // ../circle-ir/dist/analysis/passes/naming-convention-pass.js
41436
42155
  var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
41437
42156
  var CAMEL_CASE_RE = /^[a-z][a-zA-Z0-9]*$/;
41438
42157
  var SNAKE_CASE_RE = /^[a-z_][a-z0-9_]*$/;
@@ -41572,7 +42291,7 @@ class NamingConventionPass {
41572
42291
  }
41573
42292
  }
41574
42293
 
41575
- // node_modules/circle-ir/dist/analysis/passes/security-headers-pass.js
42294
+ // ../circle-ir/dist/analysis/passes/security-headers-pass.js
41576
42295
  var HEADER_WRITE_METHODS = new Set([
41577
42296
  "setHeader",
41578
42297
  "addHeader",
@@ -42033,7 +42752,7 @@ function mapReturnValueToCorsRule(returnValue) {
42033
42752
  };
42034
42753
  }
42035
42754
 
42036
- // node_modules/circle-ir/dist/analysis/passes/_fp-allowlists.js
42755
+ // ../circle-ir/dist/analysis/passes/_fp-allowlists.js
42037
42756
  var PEM_BODY_RE = /[A-Za-z0-9+/]{30,}/;
42038
42757
  function pemHasInlineBody(lines, hitLineIdx) {
42039
42758
  const end = Math.min(hitLineIdx + 5, lines.length);
@@ -42060,7 +42779,7 @@ function isProtocolMandatedCryptoFile(file, code) {
42060
42779
  return false;
42061
42780
  }
42062
42781
 
42063
- // node_modules/circle-ir/dist/analysis/passes/scan-secrets-pass.js
42782
+ // ../circle-ir/dist/analysis/passes/scan-secrets-pass.js
42064
42783
  function applyDemoDowngrade(demoPath, severity, level) {
42065
42784
  if (!demoPath)
42066
42785
  return { severity, level };
@@ -42638,7 +43357,7 @@ class ScanSecretsPass {
42638
43357
  }
42639
43358
  }
42640
43359
 
42641
- // node_modules/circle-ir/dist/analysis/passes/python-receiver-taint-format-pass.js
43360
+ // ../circle-ir/dist/analysis/passes/python-receiver-taint-format-pass.js
42642
43361
  class PythonReceiverTaintFormatPass {
42643
43362
  name = "python-receiver-taint-format";
42644
43363
  category = "security";
@@ -42690,7 +43409,7 @@ class PythonReceiverTaintFormatPass {
42690
43409
  }
42691
43410
  }
42692
43411
 
42693
- // node_modules/circle-ir/dist/analysis/passes/spring4shell-pass.js
43412
+ // ../circle-ir/dist/analysis/passes/spring4shell-pass.js
42694
43413
  var CONTROLLER_ANNOTATIONS = new Set([
42695
43414
  "Controller",
42696
43415
  "RestController",
@@ -42898,7 +43617,7 @@ function isPotentialPojo(type) {
42898
43617
  return first >= 65 && first <= 90;
42899
43618
  }
42900
43619
 
42901
- // node_modules/circle-ir/dist/analysis/passes/insecure-cookie-pass.js
43620
+ // ../circle-ir/dist/analysis/passes/insecure-cookie-pass.js
42902
43621
  var COOKIE_RESPONSE_RECEIVERS = new Set([
42903
43622
  "res",
42904
43623
  "response",
@@ -43134,7 +43853,7 @@ class InsecureCookiePass {
43134
43853
  }
43135
43854
  }
43136
43855
 
43137
- // node_modules/circle-ir/dist/analysis/passes/weak-hash-pass.js
43856
+ // ../circle-ir/dist/analysis/passes/weak-hash-pass.js
43138
43857
  var WEAK_HASH_NAMES = new Set([
43139
43858
  "md2",
43140
43859
  "md4",
@@ -43354,7 +44073,7 @@ class WeakHashPass {
43354
44073
  }
43355
44074
  }
43356
44075
 
43357
- // node_modules/circle-ir/dist/analysis/path-classification.js
44076
+ // ../circle-ir/dist/analysis/path-classification.js
43358
44077
  var TEST_PATH_PATTERNS = [
43359
44078
  /(^|\/)test\//i,
43360
44079
  /(^|\/)tests\//i,
@@ -43391,7 +44110,7 @@ function isTestPath(filepath) {
43391
44110
  return false;
43392
44111
  }
43393
44112
 
43394
- // node_modules/circle-ir/dist/analysis/passes/weak-crypto-pass.js
44113
+ // ../circle-ir/dist/analysis/passes/weak-crypto-pass.js
43395
44114
  var WEAK_CIPHER_BASES = new Set([
43396
44115
  "des",
43397
44116
  "3des",
@@ -43896,7 +44615,7 @@ class WeakCryptoPass {
43896
44615
  }
43897
44616
  }
43898
44617
 
43899
- // node_modules/circle-ir/dist/analysis/passes/weak-random-pass.js
44618
+ // ../circle-ir/dist/analysis/passes/weak-random-pass.js
43900
44619
  var JAVA_RANDOM_METHODS = new Set([
43901
44620
  "nextInt",
43902
44621
  "nextLong",
@@ -44069,7 +44788,7 @@ class WeakRandomPass {
44069
44788
  }
44070
44789
  }
44071
44790
 
44072
- // node_modules/circle-ir/dist/analysis/passes/_credential-helpers.js
44791
+ // ../circle-ir/dist/analysis/passes/_credential-helpers.js
44073
44792
  var CRED_KEYWORD_RE2 = /(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key|credential)/i;
44074
44793
  function isCredentialIdentifier(name2) {
44075
44794
  if (!name2)
@@ -44162,7 +44881,7 @@ function priorHashOf(varName, priorCalls) {
44162
44881
  return false;
44163
44882
  }
44164
44883
 
44165
- // node_modules/circle-ir/dist/analysis/passes/weak-password-hash-pass.js
44884
+ // ../circle-ir/dist/analysis/passes/weak-password-hash-pass.js
44166
44885
  var FAST_HASH_NAMES = new Set([
44167
44886
  "sha224",
44168
44887
  "sha-224",
@@ -44321,7 +45040,7 @@ class WeakPasswordHashPass {
44321
45040
  }
44322
45041
  }
44323
45042
 
44324
- // node_modules/circle-ir/dist/analysis/passes/weak-password-encoding-pass.js
45043
+ // ../circle-ir/dist/analysis/passes/weak-password-encoding-pass.js
44325
45044
  function isBasicAuthContext(call, code) {
44326
45045
  const line = call.location.line;
44327
45046
  if (line < 1)
@@ -44433,7 +45152,7 @@ class WeakPasswordEncodingPass {
44433
45152
  }
44434
45153
  }
44435
45154
 
44436
- // node_modules/circle-ir/dist/analysis/passes/info-disclosure-stacktrace-pass.js
45155
+ // ../circle-ir/dist/analysis/passes/info-disclosure-stacktrace-pass.js
44437
45156
  var RESPONSE_RECEIVER_RE = /^(res|response|w|writer|ctx|c)$/i;
44438
45157
  var LOGGER_RECEIVER_RE = /^(log|logger|slog|console|pino|winston|sentry)$/i;
44439
45158
  var RESPONSE_SEND_METHODS = new Set([
@@ -44619,7 +45338,7 @@ class InfoDisclosureStacktracePass {
44619
45338
  }
44620
45339
  }
44621
45340
 
44622
- // node_modules/circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
45341
+ // ../circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
44623
45342
  var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
44624
45343
  var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
44625
45344
  function lineWindow(lines, startLine, endLine) {
@@ -44770,7 +45489,7 @@ class UnrestrictedFileUploadPass {
44770
45489
  }
44771
45490
  }
44772
45491
 
44773
- // node_modules/circle-ir/dist/analysis/passes/missing-sanitizer-gate-pass.js
45492
+ // ../circle-ir/dist/analysis/passes/missing-sanitizer-gate-pass.js
44774
45493
  var HTML_OUTPUT_SINKS = new Set([
44775
45494
  "addAttribute",
44776
45495
  "setAttribute",
@@ -44918,7 +45637,7 @@ function methodAcceptsHtmlShapedParam(method) {
44918
45637
  return false;
44919
45638
  }
44920
45639
 
44921
- // node_modules/circle-ir/dist/analysis/passes/insecure-deserialization-config-pass.js
45640
+ // ../circle-ir/dist/analysis/passes/insecure-deserialization-config-pass.js
44922
45641
  var ANY_TYPE_PERMISSION_RE = /\bAnyTypePermission\b/;
44923
45642
 
44924
45643
  class InsecureDeserializationConfigPass {
@@ -44996,7 +45715,7 @@ class InsecureDeserializationConfigPass {
44996
45715
  }
44997
45716
  var INSECURE_TYPE_NAME_HANDLING_RE = /\bTypeNameHandling\s*=\s*(?:Newtonsoft\.Json\.)?TypeNameHandling\.(All|Auto|Objects|Arrays)\b/;
44998
45717
 
44999
- // node_modules/circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
45718
+ // ../circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
45000
45719
  function isWriteStorageCall(call, language) {
45001
45720
  const method = call.method_name ?? "";
45002
45721
  const receiver = call.receiver ?? "";
@@ -45107,7 +45826,7 @@ class PlaintextPasswordStoragePass {
45107
45826
  }
45108
45827
  }
45109
45828
 
45110
- // node_modules/circle-ir/dist/analysis/passes/cleartext-credential-transport-pass.js
45829
+ // ../circle-ir/dist/analysis/passes/cleartext-credential-transport-pass.js
45111
45830
  var LOCALHOST_RE = /^(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::\d+)?$/i;
45112
45831
  function isInsecureHttpUrl(urlLiteral) {
45113
45832
  if (!urlLiteral)
@@ -45235,7 +45954,7 @@ class CleartextCredentialTransportPass {
45235
45954
  }
45236
45955
  }
45237
45956
 
45238
- // node_modules/circle-ir/dist/analysis/passes/tls-verify-disabled-pass.js
45957
+ // ../circle-ir/dist/analysis/passes/tls-verify-disabled-pass.js
45239
45958
  var PY_HTTP_METHODS = new Set([
45240
45959
  "get",
45241
45960
  "post",
@@ -45448,7 +46167,7 @@ class TlsVerifyDisabledPass {
45448
46167
  }
45449
46168
  }
45450
46169
 
45451
- // node_modules/circle-ir/dist/analysis/passes/module-side-effect-pass.js
46170
+ // ../circle-ir/dist/analysis/passes/module-side-effect-pass.js
45452
46171
  var JS_EXEC_METHODS = new Set([
45453
46172
  "exec",
45454
46173
  "spawn",
@@ -45679,7 +46398,7 @@ class ModuleSideEffectPass {
45679
46398
  }
45680
46399
  }
45681
46400
 
45682
- // node_modules/circle-ir/dist/analysis/passes/cache-no-vary-pass.js
46401
+ // ../circle-ir/dist/analysis/passes/cache-no-vary-pass.js
45683
46402
  function isSharedCacheable(value) {
45684
46403
  const v = value.toLowerCase();
45685
46404
  if (/\b(private|no-store|no-cache)\b/.test(v))
@@ -45966,7 +46685,7 @@ class CacheNoVaryPass {
45966
46685
  }
45967
46686
  }
45968
46687
 
45969
- // node_modules/circle-ir/dist/analysis/passes/jwt-verify-disabled-pass.js
46688
+ // ../circle-ir/dist/analysis/passes/jwt-verify-disabled-pass.js
45970
46689
  var PY_VERIFY_SIGNATURE_FALSE_RE = /["']verify_signature["']\s*:\s*False\b/;
45971
46690
  var PY_VERIFY_KW_FALSE_RE = /\bverify\s*=\s*False\b/;
45972
46691
  var PY_ALG_NONE_RE = /\balgorithms\s*=\s*[\[\(]\s*["']none["']/i;
@@ -46091,7 +46810,7 @@ class JwtVerifyDisabledPass {
46091
46810
  }
46092
46811
  }
46093
46812
 
46094
- // node_modules/circle-ir/dist/analysis/passes/csrf-protection-disabled-pass.js
46813
+ // ../circle-ir/dist/analysis/passes/csrf-protection-disabled-pass.js
46095
46814
  var JAVA_CSRF_DISABLE_RE = /\.csrf\s*\([^)]*\)\s*\.\s*disable\b/;
46096
46815
  var JAVA_CSRF_LAMBDA_DISABLE_RE = /\bcsrf\s*\(\s*\w+\s*->\s*\w+\s*\.\s*disable\s*\(/;
46097
46816
  var JAVA_CSRF_METHODREF_RE = /\bcsrf\s*\(\s*[\w.]+::disable\s*\)/;
@@ -46227,7 +46946,7 @@ class CsrfProtectionDisabledPass {
46227
46946
  }
46228
46947
  }
46229
46948
 
46230
- // node_modules/circle-ir/dist/analysis/passes/xml-entity-expansion-pass.js
46949
+ // ../circle-ir/dist/analysis/passes/xml-entity-expansion-pass.js
46231
46950
  var JAVA_FACTORIES = new Set([
46232
46951
  "SAXParserFactory",
46233
46952
  "DocumentBuilderFactory",
@@ -46367,7 +47086,7 @@ class XmlEntityExpansionPass {
46367
47086
  }
46368
47087
  }
46369
47088
 
46370
- // node_modules/circle-ir/dist/analysis/passes/mass-assignment-pass.js
47089
+ // ../circle-ir/dist/analysis/passes/mass-assignment-pass.js
46371
47090
  var PY_KWARGS_SPLAT_RE = /\*\*\s*(?:request|self\.request|flask\.request|ctx|self)\s*\.\s*(?:form|args|values|json|get_json\s*\(\s*\)|files|data)/;
46372
47091
  var JS_OBJECT_SPREAD_RE = /\{\s*\.\.\.\s*(?:req|request|ctx|context)(?:\.request)?\s*\.\s*(?:body|query|params|form)\b/;
46373
47092
 
@@ -46451,7 +47170,7 @@ class MassAssignmentPass {
46451
47170
  }
46452
47171
  }
46453
47172
 
46454
- // node_modules/circle-ir/dist/graph/import-graph.js
47173
+ // ../circle-ir/dist/graph/import-graph.js
46455
47174
  function dirname(filePath) {
46456
47175
  const idx = filePath.lastIndexOf("/");
46457
47176
  return idx >= 0 ? filePath.slice(0, idx) : "";
@@ -46584,7 +47303,7 @@ class ImportGraph2 {
46584
47303
  }
46585
47304
  }
46586
47305
 
46587
- // node_modules/circle-ir/dist/analysis/passes/circular-dependency-pass.js
47306
+ // ../circle-ir/dist/analysis/passes/circular-dependency-pass.js
46588
47307
  class CircularDependencyPass {
46589
47308
  run(_projectGraph, importGraph) {
46590
47309
  const findings = [];
@@ -46611,7 +47330,7 @@ class CircularDependencyPass {
46611
47330
  }
46612
47331
  }
46613
47332
 
46614
- // node_modules/circle-ir/dist/analysis/passes/orphan-module-pass.js
47333
+ // ../circle-ir/dist/analysis/passes/orphan-module-pass.js
46615
47334
  class OrphanModulePass {
46616
47335
  run(_projectGraph, importGraph) {
46617
47336
  const findings = [];
@@ -46635,7 +47354,7 @@ class OrphanModulePass {
46635
47354
  }
46636
47355
  }
46637
47356
 
46638
- // node_modules/circle-ir/dist/analysis/metrics/passes/size-metrics-pass.js
47357
+ // ../circle-ir/dist/analysis/metrics/passes/size-metrics-pass.js
46639
47358
  class SizeMetricsPass {
46640
47359
  name = "size-metrics";
46641
47360
  run(ctx) {
@@ -46691,7 +47410,7 @@ class SizeMetricsPass {
46691
47410
  }
46692
47411
  }
46693
47412
 
46694
- // node_modules/circle-ir/dist/analysis/metrics/passes/complexity-metrics-pass.js
47413
+ // ../circle-ir/dist/analysis/metrics/passes/complexity-metrics-pass.js
46695
47414
  class ComplexityMetricsPass {
46696
47415
  name = "complexity-metrics";
46697
47416
  run(ctx) {
@@ -46755,7 +47474,7 @@ class ComplexityMetricsPass {
46755
47474
  }
46756
47475
  }
46757
47476
 
46758
- // node_modules/circle-ir/dist/analysis/metrics/passes/halstead-metrics-pass.js
47477
+ // ../circle-ir/dist/analysis/metrics/passes/halstead-metrics-pass.js
46759
47478
  var KEYWORDS = new Set([
46760
47479
  "if",
46761
47480
  "else",
@@ -46910,7 +47629,7 @@ class HalsteadMetricsPass {
46910
47629
  }
46911
47630
  }
46912
47631
 
46913
- // node_modules/circle-ir/dist/analysis/metrics/passes/data-flow-metrics-pass.js
47632
+ // ../circle-ir/dist/analysis/metrics/passes/data-flow-metrics-pass.js
46914
47633
  class DataFlowMetricsPass {
46915
47634
  name = "data-flow-metrics";
46916
47635
  run(ctx) {
@@ -46928,7 +47647,7 @@ class DataFlowMetricsPass {
46928
47647
  }
46929
47648
  }
46930
47649
 
46931
- // node_modules/circle-ir/dist/analysis/metrics/passes/coupling-metrics-pass.js
47650
+ // ../circle-ir/dist/analysis/metrics/passes/coupling-metrics-pass.js
46932
47651
  class CouplingMetricsPass {
46933
47652
  name = "coupling-metrics";
46934
47653
  run(ctx) {
@@ -47006,7 +47725,7 @@ class CouplingMetricsPass {
47006
47725
  }
47007
47726
  }
47008
47727
 
47009
- // node_modules/circle-ir/dist/analysis/metrics/passes/inheritance-metrics-pass.js
47728
+ // ../circle-ir/dist/analysis/metrics/passes/inheritance-metrics-pass.js
47010
47729
  class InheritanceMetricsPass {
47011
47730
  name = "inheritance-metrics";
47012
47731
  run(ctx) {
@@ -47066,7 +47785,7 @@ class InheritanceMetricsPass {
47066
47785
  }
47067
47786
  }
47068
47787
 
47069
- // node_modules/circle-ir/dist/analysis/metrics/passes/cohesion-metrics-pass.js
47788
+ // ../circle-ir/dist/analysis/metrics/passes/cohesion-metrics-pass.js
47070
47789
  class CohesionMetricsPass {
47071
47790
  name = "cohesion-metrics";
47072
47791
  run(ctx) {
@@ -47149,7 +47868,7 @@ class CohesionMetricsPass {
47149
47868
  }
47150
47869
  }
47151
47870
 
47152
- // node_modules/circle-ir/dist/analysis/metrics/passes/documentation-metrics-pass.js
47871
+ // ../circle-ir/dist/analysis/metrics/passes/documentation-metrics-pass.js
47153
47872
  class DocumentationMetricsPass {
47154
47873
  name = "documentation-metrics";
47155
47874
  run(ctx) {
@@ -47202,7 +47921,7 @@ class DocumentationMetricsPass {
47202
47921
  }
47203
47922
  }
47204
47923
 
47205
- // node_modules/circle-ir/dist/analysis/metrics/passes/composite-metrics-pass.js
47924
+ // ../circle-ir/dist/analysis/metrics/passes/composite-metrics-pass.js
47206
47925
  class CompositeMetricsPass {
47207
47926
  name = "composite-metrics";
47208
47927
  run(ctx) {
@@ -47261,7 +47980,7 @@ class CompositeMetricsPass {
47261
47980
  }
47262
47981
  }
47263
47982
 
47264
- // node_modules/circle-ir/dist/analysis/metrics/metric-runner.js
47983
+ // ../circle-ir/dist/analysis/metrics/metric-runner.js
47265
47984
  class MetricRunner {
47266
47985
  passes = [
47267
47986
  new SizeMetricsPass,
@@ -47283,7 +48002,7 @@ class MetricRunner {
47283
48002
  return accumulated;
47284
48003
  }
47285
48004
  }
47286
- // node_modules/circle-ir/dist/analyzer.js
48005
+ // ../circle-ir/dist/analyzer.js
47287
48006
  var initialized = false;
47288
48007
  async function initAnalyzer(options = {}) {
47289
48008
  if (initialized)
@@ -47599,8 +48318,6 @@ async function analyze(code, filePath, language, options = {}) {
47599
48318
  pipeline.add(new LanguageSourcesPass);
47600
48319
  if (!disabledPasses.has("source-semantics"))
47601
48320
  pipeline.add(new SourceSemanticsPass);
47602
- if (!disabledPasses.has("library-profile-source-gate"))
47603
- pipeline.add(new LibraryProfileSourceGatePass);
47604
48321
  if (!disabledPasses.has("mybatis-annotation-sql-sink"))
47605
48322
  pipeline.add(new MyBatisAnnotationSqlSinkPass);
47606
48323
  pipeline.add(new SinkFilterPass);
@@ -47617,6 +48334,8 @@ async function analyze(code, filePath, language, options = {}) {
47617
48334
  pipeline.add(new LibraryProfileSinkGatePass);
47618
48335
  if (!disabledPasses.has("library-profile-xss-gate"))
47619
48336
  pipeline.add(new LibraryProfileXssGatePass);
48337
+ if (!disabledPasses.has("library-profile-source-gate"))
48338
+ pipeline.add(new LibraryProfileSourceGatePass);
47620
48339
  pipeline.add(new TaintPropagationPass);
47621
48340
  pipeline.add(new InterproceduralPass({
47622
48341
  enableEntryPointGate: options.enableEntryPointGate ?? true
@@ -47880,7 +48599,19 @@ async function analyzeProject(files, options = {}) {
47880
48599
  const fileAnalyses = [];
47881
48600
  const projectGraph = new ProjectGraph;
47882
48601
  const sourceLinesByFile = new Map;
48602
+ const perFileBudgetMs = options.perFileBudgetMs ?? 0;
48603
+ const perFileStart = Date.now();
48604
+ let perFileBudgetExceeded = false;
47883
48605
  for (const { code, filePath, language } of files) {
48606
+ if (perFileBudgetMs > 0 && Date.now() - perFileStart > perFileBudgetMs) {
48607
+ perFileBudgetExceeded = true;
48608
+ logger.warn("analyzeProject: per-file budget exceeded, skipping remaining files", {
48609
+ analysed: fileAnalyses.length,
48610
+ total: files.length,
48611
+ budgetMs: perFileBudgetMs
48612
+ });
48613
+ break;
48614
+ }
47884
48615
  const ir = await analyze(code, filePath, language, options);
47885
48616
  fileAnalyses.push({ file: filePath, analysis: ir });
47886
48617
  projectGraph.addFile(filePath, new CodeGraph(ir));
@@ -47936,6 +48667,9 @@ async function analyzeProject(files, options = {}) {
47936
48667
  if (crossFileResult.budgetExceeded) {
47937
48668
  projectAnalysis.cross_file_budget_exceeded = true;
47938
48669
  }
48670
+ if (perFileBudgetExceeded) {
48671
+ projectAnalysis.per_file_budget_exceeded = true;
48672
+ }
47939
48673
  return projectAnalysis;
47940
48674
  }
47941
48675
  function deriveProjectName(paths) {
@@ -47955,7 +48689,7 @@ function deriveProjectRoot(paths) {
47955
48689
  }
47956
48690
  return common.join("/") || "/";
47957
48691
  }
47958
- // node_modules/circle-ir/dist/analysis/sbom.js
48692
+ // ../circle-ir/dist/analysis/sbom.js
47959
48693
  function purlVersion(spec) {
47960
48694
  const stripped = spec.trim().replace(/^[\s^~=<>]+/, "");
47961
48695
  if (/^v?[0-9][\w.\-+]*$/.test(stripped))
@@ -49011,7 +49745,7 @@ var colors = {
49011
49745
  };
49012
49746
 
49013
49747
  // src/version.ts
49014
- var version = "4.9.16";
49748
+ var version = "4.9.19";
49015
49749
 
49016
49750
  // src/formatters.ts
49017
49751
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -49564,8 +50298,8 @@ function generateSarifResults(results, crossFileData) {
49564
50298
 
49565
50299
  // src/build-info.ts
49566
50300
  var buildInfo = {
49567
- gitSha: "e4c3d1b",
49568
- builtAt: "2026-09-18T20:18:22.063Z"
50301
+ gitSha: "edf0335",
50302
+ builtAt: "2026-09-19T15:34:15.780Z"
49569
50303
  };
49570
50304
 
49571
50305
  // src/utils/args.ts