cognium-dev 4.9.17 → 4.9.20

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 -216
  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 },
@@ -11086,12 +11128,6 @@ var DEFAULT_SOURCES = [
11086
11128
  { method: "getBody", class: "Exchange", type: "http_body", severity: "high", return_tainted: true },
11087
11129
  { method: "getIn", class: "Exchange", type: "http_body", severity: "high", return_tainted: true },
11088
11130
  { method: "getHeader", class: "Message", type: "http_header", severity: "high", return_tainted: true },
11089
- { method: "getFileName", type: "file_input", severity: "high", return_tainted: true },
11090
- { method: "getName", class: "File", type: "file_input", severity: "high", return_tainted: true },
11091
- { method: "getPath", class: "File", type: "file_input", severity: "high", return_tainted: true },
11092
- { method: "getAbsolutePath", class: "File", type: "file_input", severity: "high", return_tainted: true },
11093
- { method: "toString", class: "Path", type: "file_input", severity: "medium", return_tainted: true },
11094
- { method: "getFileName", class: "Path", type: "file_input", severity: "high", return_tainted: true },
11095
11131
  { method: "getOriginalFilename", class: "MultipartFile", type: "file_input", severity: "high", return_tainted: true },
11096
11132
  { method: "getName", class: "MultipartFile", type: "file_input", severity: "high", return_tainted: true },
11097
11133
  { method: "getSubmittedFileName", class: "Part", type: "file_input", severity: "high", return_tainted: true },
@@ -11162,7 +11198,6 @@ var DEFAULT_SOURCES = [
11162
11198
  { method: "refreshUsersAndGroups", type: "io_input", severity: "high", return_tainted: true },
11163
11199
  { method: "getScriptPath", type: "io_input", severity: "critical", return_tainted: true },
11164
11200
  { method: "getFilePathSuffix", type: "io_input", severity: "high", return_tainted: true },
11165
- { method: "getPath", type: "file_input", severity: "high", return_tainted: true },
11166
11201
  { method: "contentAsString", type: "file_input", severity: "critical", return_tainted: true },
11167
11202
  { method: "readAsString", type: "file_input", severity: "critical", return_tainted: true },
11168
11203
  { method: "content", type: "file_input", severity: "high", return_tainted: true },
@@ -11533,8 +11568,8 @@ var DEFAULT_SINKS = [
11533
11568
  { method: "selectByExample", class: "*Mapper", type: "mybatis_mapper_call", cwe: "CWE-89", severity: "medium", arg_positions: [0], languages: ["java"] },
11534
11569
  { method: "exec", class: "Runtime", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
11535
11570
  { 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] },
11571
+ { method: "ProcessBuilder", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1, 2, 3, 4, 5, 6, 7] },
11572
+ { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1, 2, 3, 4, 5, 6, 7] },
11538
11573
  { method: "execute", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11539
11574
  { method: "CommandLine", class: "constructor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11540
11575
  { method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
@@ -12335,7 +12370,6 @@ var DEFAULT_SINKS = [
12335
12370
  { method: "fatal", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0, 1, 2, 3, 4] },
12336
12371
  { method: "log", class: "logging", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1, 2, 3, 4] },
12337
12372
  { 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
12373
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
12340
12374
  { method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
12341
12375
  { method: "evaluate", class: "Script", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
@@ -13097,7 +13131,33 @@ var DEFAULT_HEADER_RULES = [
13097
13131
  note: "Fires when the value is not a string literal (likely reflected from request)"
13098
13132
  }
13099
13133
  ];
13100
- // node_modules/circle-ir/dist/analysis/arg-type-resolver.js
13134
+ // ../circle-ir/dist/analysis/literal-expression.js
13135
+ function isProvablyLiteralExpression(expr) {
13136
+ let e = (expr ?? "").trim();
13137
+ if (e.length === 0)
13138
+ return false;
13139
+ e = e.replace(/\/\/.*$/, "").replace(/;\s*$/, "").trim();
13140
+ if (e.length === 0)
13141
+ return false;
13142
+ if (e.includes("${"))
13143
+ return false;
13144
+ if (/[+]\s*$/.test(e))
13145
+ return false;
13146
+ const stripped = e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\$]|\\.)*`/g, "");
13147
+ if (/["'`]/.test(stripped))
13148
+ return false;
13149
+ return /^[\s+()]*$/.test(stripped);
13150
+ }
13151
+ var DOM_ASSIGNMENT_SINK_METHODS = new Set([
13152
+ "innerHTML",
13153
+ "outerHTML",
13154
+ "cssText",
13155
+ "src",
13156
+ "href",
13157
+ "textContent"
13158
+ ]);
13159
+
13160
+ // ../circle-ir/dist/analysis/arg-type-resolver.js
13101
13161
  function findEnclosingType(callInMethod, types) {
13102
13162
  if (!callInMethod)
13103
13163
  return null;
@@ -13175,7 +13235,7 @@ function isArgvContainerType(rawType) {
13175
13235
  return /^(?:List|ArrayList|LinkedList|Collection|Iterable|Deque|Queue)\s*<\s*(?:String|CharSequence|java\.lang\.String)\b/.test(s);
13176
13236
  }
13177
13237
 
13178
- // node_modules/circle-ir/dist/analysis/taint-matcher.js
13238
+ // ../circle-ir/dist/analysis/taint-matcher.js
13179
13239
  var PYTHON_TAINTED_PATTERNS = [
13180
13240
  { pattern: /\brequest\.args\b/, sourceType: "http_param" },
13181
13241
  { pattern: /\brequest\.form\b/, sourceType: "http_body" },
@@ -14050,7 +14110,7 @@ function isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines)
14050
14110
  if (!guards)
14051
14111
  continue;
14052
14112
  for (const g of guards) {
14053
- if (g.i >= sinkIdx || checked.has(g))
14113
+ if (g.i > sinkIdx || checked.has(g))
14054
14114
  continue;
14055
14115
  checked.add(g);
14056
14116
  const block = csThenBlock(sourceLines, g.i, g.rest);
@@ -14193,6 +14253,21 @@ function isRegexReceiver(receiver, sourceLines) {
14193
14253
  const decl = new RegExp(`(?:const|let|var)\\s+${escapeRe(r)}\\s*=\\s*(?:/|(?:new\\s+)?RegExp\\s*\\()`);
14194
14254
  return sourceLines.some((l) => decl.test(l));
14195
14255
  }
14256
+ function isDeclaredFunctionReference(arg, sourceLines) {
14257
+ if (arg.literal !== null && arg.literal !== undefined)
14258
+ return false;
14259
+ if (!sourceLines)
14260
+ return false;
14261
+ const name2 = (arg.expression ?? "").trim();
14262
+ if (!/^[A-Za-z_$][\w$]*$/.test(name2))
14263
+ return false;
14264
+ const n = escapeRe(name2);
14265
+ const decls = [
14266
+ new RegExp(`\\bfunction\\s+${n}\\s*\\(`),
14267
+ new RegExp(`\\b(?:const|let|var)\\s+${n}\\s*=\\s*(?:async\\s+)?(?:function\\b|\\(|[A-Za-z_$][\\w$]*\\s*=>)`)
14268
+ ];
14269
+ return sourceLines.some((l) => decls.some((re) => re.test(l)));
14270
+ }
14196
14271
  function isFunctionCallbackArgument(arg) {
14197
14272
  if (arg.literal !== null && arg.literal !== undefined)
14198
14273
  return false;
@@ -14396,11 +14471,17 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14396
14471
  }
14397
14472
  if (pattern.type === "code_injection" && (call.method_name === "setInterval" || call.method_name === "setTimeout" || call.method_name === "setImmediate")) {
14398
14473
  const firstArg = call.arguments.find((a) => a.position === 0);
14399
- if (firstArg && isFunctionCallbackArgument(firstArg)) {
14474
+ if (firstArg && (isFunctionCallbackArgument(firstArg) || isDeclaredFunctionReference(firstArg, sourceLines))) {
14400
14475
  continue;
14401
14476
  }
14402
14477
  }
14403
- if (pattern.type === "command_injection" && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
14478
+ if (pattern.type === "xss" && (language === "javascript" || language === "typescript") && DOM_ASSIGNMENT_SINK_METHODS.has(call.method_name) && call.arguments.length > 0) {
14479
+ const rhs = call.arguments.find((a) => a.position === 0);
14480
+ if (rhs && isProvablyLiteralExpression(rhs.expression ?? "")) {
14481
+ continue;
14482
+ }
14483
+ }
14484
+ if ((pattern.type === "command_injection" || pattern.type === "sql_injection") && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
14404
14485
  continue;
14405
14486
  }
14406
14487
  if (pattern.type === "code_injection" && language === "python" && call.method_name === "compile" && call.receiver && call.receiver !== "builtins") {
@@ -15216,7 +15297,7 @@ function formatSanitizerMethod(call) {
15216
15297
  return `${call.method_name}()`;
15217
15298
  }
15218
15299
 
15219
- // node_modules/circle-ir/dist/analysis/unresolved.js
15300
+ // ../circle-ir/dist/analysis/unresolved.js
15220
15301
  function detectUnresolved(calls, types, dfg) {
15221
15302
  const unresolved = [];
15222
15303
  unresolved.push(...detectVirtualDispatch(calls));
@@ -15380,7 +15461,7 @@ function formatCallCode(call) {
15380
15461
  }
15381
15462
  return `${call.method_name}(${args2})`;
15382
15463
  }
15383
- // node_modules/circle-ir/dist/analysis/non-executable-lines.js
15464
+ // ../circle-ir/dist/analysis/non-executable-lines.js
15384
15465
  function isNonExecutableSourceLine(sourceCode, line, language) {
15385
15466
  if (!sourceCode || line < 1)
15386
15467
  return false;
@@ -15440,7 +15521,7 @@ function isNonExecutablePython(trimmed) {
15440
15521
  return false;
15441
15522
  }
15442
15523
 
15443
- // node_modules/circle-ir/dist/analysis/sanitizer-index.js
15524
+ // ../circle-ir/dist/analysis/sanitizer-index.js
15444
15525
  var SANITIZER_SET_CACHE = new WeakMap;
15445
15526
  function getSanitizesSet(san) {
15446
15527
  let s = SANITIZER_SET_CACHE.get(san);
@@ -15454,7 +15535,7 @@ function sanitizerCoversSink(san, sinkType) {
15454
15535
  return getSanitizesSet(san).has(sinkType);
15455
15536
  }
15456
15537
 
15457
- // node_modules/circle-ir/dist/analysis/dfg-walk.js
15538
+ // ../circle-ir/dist/analysis/dfg-walk.js
15458
15539
  var walkBackwardDefsMemo = new WeakMap;
15459
15540
  function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15460
15541
  const maxHops = options.maxHops ?? 32;
@@ -15507,7 +15588,7 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15507
15588
  return result;
15508
15589
  }
15509
15590
 
15510
- // node_modules/circle-ir/dist/analysis/findings.js
15591
+ // ../circle-ir/dist/analysis/findings.js
15511
15592
  function canSourceReachSink(sourceType, sinkType) {
15512
15593
  const sourceToSinkMapping = {
15513
15594
  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 +15619,7 @@ function sourceSemanticsAllowed(source, sinkType) {
15538
15619
  return true;
15539
15620
  }
15540
15621
 
15541
- // node_modules/circle-ir/dist/analysis/findings-instrumentation.js
15622
+ // ../circle-ir/dist/analysis/findings-instrumentation.js
15542
15623
  var instrumentEnabled = false;
15543
15624
  function setFindingsInstrumentation(enabled) {
15544
15625
  instrumentEnabled = enabled;
@@ -15601,7 +15682,7 @@ function emitFindingsInstrumentation(filePath, findings, taint) {
15601
15682
  };
15602
15683
  console.error(`[findings-summary] ${JSON.stringify(summary)}`);
15603
15684
  }
15604
- // node_modules/circle-ir/dist/graph/code-graph.js
15685
+ // ../circle-ir/dist/graph/code-graph.js
15605
15686
  class CodeGraph {
15606
15687
  ir;
15607
15688
  constructor(ir) {
@@ -15811,7 +15892,7 @@ class CodeGraph {
15811
15892
  return result;
15812
15893
  }
15813
15894
  }
15814
- // node_modules/circle-ir/dist/resolution/type-hierarchy.js
15895
+ // ../circle-ir/dist/resolution/type-hierarchy.js
15815
15896
  class TypeHierarchyResolver {
15816
15897
  types = new Map;
15817
15898
  nameToFqn = new Map;
@@ -16383,7 +16464,7 @@ function registerCommonLibraries(resolver) {
16383
16464
  resolver.registerFactoryReturnType("HttpClients", "createSystem", "org.apache.http.impl.client.CloseableHttpClient");
16384
16465
  resolver.registerFactoryReturnType("HttpClients", "createMinimal", "org.apache.http.impl.client.MinimalHttpClient");
16385
16466
  }
16386
- // node_modules/circle-ir/dist/resolution/symbol-table.js
16467
+ // ../circle-ir/dist/resolution/symbol-table.js
16387
16468
  class SymbolTable {
16388
16469
  exports = new Map;
16389
16470
  nameToFqns = new Map;
@@ -16606,7 +16687,7 @@ class SymbolTable {
16606
16687
  return "package";
16607
16688
  }
16608
16689
  }
16609
- // node_modules/circle-ir/dist/resolution/cross-file.js
16690
+ // ../circle-ir/dist/resolution/cross-file.js
16610
16691
  function buildFileIndex(ir) {
16611
16692
  const callsByLine = new Map;
16612
16693
  for (const c of ir.calls) {
@@ -17104,10 +17185,12 @@ class CrossFileResolver {
17104
17185
  }
17105
17186
  return callers;
17106
17187
  }
17107
- findCrossFileTaintFlows() {
17188
+ findCrossFileTaintFlows(deadline) {
17108
17189
  const flows = [];
17109
17190
  const seen = new Set;
17110
17191
  for (const [filePath, ir] of this.fileIRs) {
17192
+ if (deadline?.())
17193
+ break;
17111
17194
  for (const source of ir.taint.sources) {
17112
17195
  if (source.type === "interprocedural_param")
17113
17196
  continue;
@@ -17182,11 +17265,13 @@ class CrossFileResolver {
17182
17265
  }
17183
17266
  return flows;
17184
17267
  }
17185
- findInterproceduralTaintPaths() {
17268
+ findInterproceduralTaintPaths(deadline) {
17186
17269
  const paths = [];
17187
17270
  const seen = new Set;
17188
17271
  const methodIndex = this.buildMethodIndex();
17189
17272
  for (const [callerFile, callerIR] of this.fileIRs) {
17273
+ if (deadline?.())
17274
+ break;
17190
17275
  const callerIdx = this.getFileIndex(callerIR);
17191
17276
  for (const type of callerIR.types) {
17192
17277
  for (const method of type.methods) {
@@ -17325,7 +17410,7 @@ class CrossFileResolver {
17325
17410
  }
17326
17411
  return paths;
17327
17412
  }
17328
- findFieldBindingTaintPaths() {
17413
+ findFieldBindingTaintPaths(deadline) {
17329
17414
  const paths = [];
17330
17415
  const seen = new Set;
17331
17416
  if (this.fieldTaintInfo.size === 0)
@@ -17333,6 +17418,8 @@ class CrossFileResolver {
17333
17418
  const fieldExprRe = /^(\w+)\.(\w+)$/;
17334
17419
  const methodIndex = this.buildMethodIndex();
17335
17420
  for (const [callerFile, callerIR] of this.fileIRs) {
17421
+ if (deadline?.())
17422
+ break;
17336
17423
  const callerIdx = this.getFileIndex(callerIR);
17337
17424
  for (const type of callerIR.types) {
17338
17425
  const callerTypeFqn = callerIR.meta.package ? `${callerIR.meta.package}.${type.name}` : type.name;
@@ -17694,7 +17781,7 @@ class CrossFileResolver {
17694
17781
  return this.fieldTaintInfo.get(`${typeFqn}.${fieldName}`);
17695
17782
  }
17696
17783
  }
17697
- // node_modules/circle-ir/dist/graph/project-graph.js
17784
+ // ../circle-ir/dist/graph/project-graph.js
17698
17785
  class ProjectGraph {
17699
17786
  files = new Map;
17700
17787
  _symbolTable = null;
@@ -17746,7 +17833,7 @@ class ProjectGraph {
17746
17833
  return this._resolver;
17747
17834
  }
17748
17835
  }
17749
- // node_modules/circle-ir/dist/graph/analysis-pass.js
17836
+ // ../circle-ir/dist/graph/analysis-pass.js
17750
17837
  class AnalysisPipeline {
17751
17838
  passes = [];
17752
17839
  add(pass) {
@@ -17793,7 +17880,7 @@ class AnalysisPipeline {
17793
17880
  return { results, findings };
17794
17881
  }
17795
17882
  }
17796
- // node_modules/circle-ir/dist/analysis/taint-propagation.js
17883
+ // ../circle-ir/dist/analysis/taint-propagation.js
17797
17884
  function buildSanitizersByLine(sanitizers) {
17798
17885
  const out2 = new Map;
17799
17886
  for (const san of sanitizers) {
@@ -18068,7 +18155,7 @@ function checkSanitized(fromLine, toLine, sinkType, sanitizersByLine, ctx) {
18068
18155
  function buildTaintFlow(source, sink, taintInfo) {
18069
18156
  const path = [];
18070
18157
  path.push({
18071
- variable: taintInfo.variable,
18158
+ variable: source.variable ?? taintInfo.variable,
18072
18159
  line: source.line,
18073
18160
  type: "source",
18074
18161
  description: `Tainted data enters via ${source.type}`
@@ -18095,7 +18182,7 @@ function buildTaintFlow(source, sink, taintInfo) {
18095
18182
  confidence: taintInfo.confidence * 0.9
18096
18183
  };
18097
18184
  }
18098
- // node_modules/circle-ir/dist/analysis/constant-propagation/ast-utils.js
18185
+ // ../circle-ir/dist/analysis/constant-propagation/ast-utils.js
18099
18186
  function isKnown(cv) {
18100
18187
  return cv.type !== "unknown";
18101
18188
  }
@@ -18112,7 +18199,7 @@ function getNodeLine(node) {
18112
18199
  return node.startPosition.row + 1;
18113
18200
  }
18114
18201
 
18115
- // node_modules/circle-ir/dist/analysis/constant-propagation/evaluator.js
18202
+ // ../circle-ir/dist/analysis/constant-propagation/evaluator.js
18116
18203
  class ExpressionEvaluator {
18117
18204
  source;
18118
18205
  getSymbol;
@@ -18382,7 +18469,7 @@ class ExpressionEvaluator {
18382
18469
  }
18383
18470
  }
18384
18471
 
18385
- // node_modules/circle-ir/dist/analysis/constant-propagation/patterns.js
18472
+ // ../circle-ir/dist/analysis/constant-propagation/patterns.js
18386
18473
  var TAINT_PATTERNS = [
18387
18474
  "request.getParameter",
18388
18475
  "request.getHeader",
@@ -18577,7 +18664,7 @@ var PROPAGATOR_METHODS = new Set([
18577
18664
  "decodeRequestString"
18578
18665
  ]);
18579
18666
 
18580
- // node_modules/circle-ir/dist/analysis/constant-propagation/propagator.js
18667
+ // ../circle-ir/dist/analysis/constant-propagation/propagator.js
18581
18668
  class ConstantPropagator {
18582
18669
  symbols = new Map;
18583
18670
  tainted = new Set;
@@ -20434,7 +20521,7 @@ class ConstantPropagator {
20434
20521
  }
20435
20522
  }
20436
20523
 
20437
- // node_modules/circle-ir/dist/analysis/constant-propagation/index.js
20524
+ // ../circle-ir/dist/analysis/constant-propagation/index.js
20438
20525
  function analyzeConstantPropagation(tree, sourceCode, options = {}) {
20439
20526
  const propagator = new ConstantPropagator;
20440
20527
  return propagator.analyze(tree, sourceCode, options.additionalTaintPatterns ?? [], options.sanitizerMethods ?? [], options.taintedParameters ?? []);
@@ -20522,7 +20609,7 @@ function normalizeCondition(cond) {
20522
20609
  }
20523
20610
  return normalized;
20524
20611
  }
20525
- // node_modules/circle-ir/dist/utils/logger.js
20612
+ // ../circle-ir/dist/utils/logger.js
20526
20613
  var LOG_LEVELS = {
20527
20614
  trace: 0,
20528
20615
  debug: 1,
@@ -20594,7 +20681,7 @@ var logger = {
20594
20681
  }
20595
20682
  };
20596
20683
 
20597
- // node_modules/circle-ir/dist/analysis/per-file-finding-cap.js
20684
+ // ../circle-ir/dist/analysis/per-file-finding-cap.js
20598
20685
  var DEFAULT_PER_FILE_FINDING_CAP = 1000;
20599
20686
  var SATURATED_FILE_RULE_ID = "saturated-file";
20600
20687
  function applyPerFileFindingCap(filePath, findings, cap) {
@@ -20630,7 +20717,7 @@ function applyPerFileFindingCap(filePath, findings, cap) {
20630
20717
  return [advisory];
20631
20718
  }
20632
20719
 
20633
- // node_modules/circle-ir/dist/analysis/confidence-filter.js
20720
+ // ../circle-ir/dist/analysis/confidence-filter.js
20634
20721
  function applyConfidenceFilter(findings, includeSpeculative) {
20635
20722
  if (includeSpeculative)
20636
20723
  return findings;
@@ -20640,7 +20727,7 @@ function isHighConfidence(finding) {
20640
20727
  return finding.confidence === undefined || finding.confidence === "high";
20641
20728
  }
20642
20729
 
20643
- // node_modules/circle-ir/dist/analysis/library-api-surface-downgrade.js
20730
+ // ../circle-ir/dist/analysis/library-api-surface-downgrade.js
20644
20731
  var LIBRARY_API_SURFACE_TAG = "library-api-surface:caller-responsibility";
20645
20732
  function applyLibraryApiSurfaceDowngrade(findings) {
20646
20733
  return findings.map((f) => {
@@ -20657,7 +20744,7 @@ function applyLibraryApiSurfaceDowngrade(findings) {
20657
20744
  });
20658
20745
  }
20659
20746
 
20660
- // node_modules/circle-ir/dist/analysis/note-coalescer.js
20747
+ // ../circle-ir/dist/analysis/note-coalescer.js
20661
20748
  var CLICKJACKING_PAIR = new Set(["missing-x-frame-options", "missing-csp-frame-ancestors"]);
20662
20749
  function levelRank(level) {
20663
20750
  return level === "error" ? 0 : level === "warning" ? 1 : 2;
@@ -20721,7 +20808,7 @@ function coalesceNoteLevelFindings(findings) {
20721
20808
  return out2;
20722
20809
  }
20723
20810
 
20724
- // node_modules/circle-ir/dist/analysis/entry-point-detection.js
20811
+ // ../circle-ir/dist/analysis/entry-point-detection.js
20725
20812
  var TIER_1_METHOD_ANNOTATIONS = new Set([
20726
20813
  "RequestMapping",
20727
20814
  "GetMapping",
@@ -21378,7 +21465,7 @@ function methodIsRuntimeRegistrationHandler(method, regs) {
21378
21465
  return false;
21379
21466
  }
21380
21467
 
21381
- // node_modules/circle-ir/dist/analysis/require-entry-path.js
21468
+ // ../circle-ir/dist/analysis/require-entry-path.js
21382
21469
  var RULE_ID_REQUIRE_ENTRY_PATH = "require-entry-path";
21383
21470
  var MAX_VISITED_METHODS = 2000;
21384
21471
  var TAINT_FLOW_RULE_IDS = new Set([
@@ -21767,7 +21854,7 @@ function normalizeDisabled(input) {
21767
21854
  return new Set(input);
21768
21855
  }
21769
21856
 
21770
- // node_modules/circle-ir/dist/analysis/project-profile-transform.js
21857
+ // ../circle-ir/dist/analysis/project-profile-transform.js
21771
21858
  var DOWNGRADE_ELIGIBLE_RULE_IDS = new Set([
21772
21859
  "code_injection",
21773
21860
  "template_injection",
@@ -21829,7 +21916,7 @@ function applyProjectProfileTransform(findings, resolveProfile) {
21829
21916
  });
21830
21917
  }
21831
21918
 
21832
- // node_modules/circle-ir/dist/languages/registry.js
21919
+ // ../circle-ir/dist/languages/registry.js
21833
21920
  class DefaultLanguageRegistry {
21834
21921
  plugins = new Map;
21835
21922
  extensionMap = new Map;
@@ -21876,7 +21963,7 @@ function registerLanguage(plugin) {
21876
21963
  function getLanguagePlugin(language) {
21877
21964
  return getLanguageRegistry().get(language);
21878
21965
  }
21879
- // node_modules/circle-ir/dist/languages/plugins/base.js
21966
+ // ../circle-ir/dist/languages/plugins/base.js
21880
21967
  class BaseLanguagePlugin {
21881
21968
  parser = null;
21882
21969
  async initialize(parser) {
@@ -21937,7 +22024,7 @@ class BaseLanguagePlugin {
21937
22024
  }
21938
22025
  }
21939
22026
 
21940
- // node_modules/circle-ir/dist/languages/plugins/java.js
22027
+ // ../circle-ir/dist/languages/plugins/java.js
21941
22028
  class JavaPlugin extends BaseLanguagePlugin {
21942
22029
  id = "java";
21943
22030
  name = "Java";
@@ -22194,7 +22281,7 @@ class JavaPlugin extends BaseLanguagePlugin {
22194
22281
  }
22195
22282
  }
22196
22283
 
22197
- // node_modules/circle-ir/dist/languages/plugins/javascript.js
22284
+ // ../circle-ir/dist/languages/plugins/javascript.js
22198
22285
  class JavaScriptPlugin extends BaseLanguagePlugin {
22199
22286
  id = "javascript";
22200
22287
  name = "JavaScript/TypeScript";
@@ -22860,7 +22947,7 @@ class JavaScriptPlugin extends BaseLanguagePlugin {
22860
22947
  }
22861
22948
  }
22862
22949
 
22863
- // node_modules/circle-ir/dist/languages/plugins/python.js
22950
+ // ../circle-ir/dist/languages/plugins/python.js
22864
22951
  class PythonPlugin extends BaseLanguagePlugin {
22865
22952
  id = "python";
22866
22953
  name = "Python";
@@ -23136,7 +23223,7 @@ class PythonPlugin extends BaseLanguagePlugin {
23136
23223
  }
23137
23224
  }
23138
23225
 
23139
- // node_modules/circle-ir/dist/languages/plugins/rust.js
23226
+ // ../circle-ir/dist/languages/plugins/rust.js
23140
23227
  class RustPlugin extends BaseLanguagePlugin {
23141
23228
  id = "rust";
23142
23229
  name = "Rust";
@@ -23369,7 +23456,7 @@ class RustPlugin extends BaseLanguagePlugin {
23369
23456
  }
23370
23457
  }
23371
23458
 
23372
- // node_modules/circle-ir/dist/languages/plugins/bash.js
23459
+ // ../circle-ir/dist/languages/plugins/bash.js
23373
23460
  class BashPlugin extends BaseLanguagePlugin {
23374
23461
  id = "bash";
23375
23462
  name = "Bash/Shell";
@@ -23575,7 +23662,7 @@ class BashPlugin extends BaseLanguagePlugin {
23575
23662
  }
23576
23663
  }
23577
23664
 
23578
- // node_modules/circle-ir/dist/languages/plugins/html.js
23665
+ // ../circle-ir/dist/languages/plugins/html.js
23579
23666
  class HtmlPlugin extends BaseLanguagePlugin {
23580
23667
  id = "html";
23581
23668
  name = "HTML";
@@ -23640,7 +23727,7 @@ class HtmlPlugin extends BaseLanguagePlugin {
23640
23727
  }
23641
23728
  }
23642
23729
 
23643
- // node_modules/circle-ir/dist/languages/plugins/vue.js
23730
+ // ../circle-ir/dist/languages/plugins/vue.js
23644
23731
  class VuePlugin extends BaseLanguagePlugin {
23645
23732
  id = "vue";
23646
23733
  name = "Vue";
@@ -23705,7 +23792,7 @@ class VuePlugin extends BaseLanguagePlugin {
23705
23792
  }
23706
23793
  }
23707
23794
 
23708
- // node_modules/circle-ir/dist/languages/plugins/go.js
23795
+ // ../circle-ir/dist/languages/plugins/go.js
23709
23796
  class GoPlugin extends BaseLanguagePlugin {
23710
23797
  id = "go";
23711
23798
  name = "Go";
@@ -23930,6 +24017,54 @@ class GoPlugin extends BaseLanguagePlugin {
23930
24017
  severity: "high",
23931
24018
  argPositions: [0]
23932
24019
  },
24020
+ {
24021
+ method: "Create",
24022
+ class: "os",
24023
+ type: "path_traversal",
24024
+ cwe: "CWE-22",
24025
+ severity: "high",
24026
+ argPositions: [0]
24027
+ },
24028
+ {
24029
+ method: "OpenFile",
24030
+ class: "os",
24031
+ type: "path_traversal",
24032
+ cwe: "CWE-22",
24033
+ severity: "high",
24034
+ argPositions: [0]
24035
+ },
24036
+ {
24037
+ method: "Remove",
24038
+ class: "os",
24039
+ type: "path_traversal",
24040
+ cwe: "CWE-22",
24041
+ severity: "high",
24042
+ argPositions: [0]
24043
+ },
24044
+ {
24045
+ method: "RemoveAll",
24046
+ class: "os",
24047
+ type: "path_traversal",
24048
+ cwe: "CWE-22",
24049
+ severity: "high",
24050
+ argPositions: [0]
24051
+ },
24052
+ {
24053
+ method: "ReadFile",
24054
+ class: "ioutil",
24055
+ type: "path_traversal",
24056
+ cwe: "CWE-22",
24057
+ severity: "high",
24058
+ argPositions: [0]
24059
+ },
24060
+ {
24061
+ method: "WriteFile",
24062
+ class: "ioutil",
24063
+ type: "path_traversal",
24064
+ cwe: "CWE-22",
24065
+ severity: "high",
24066
+ argPositions: [0]
24067
+ },
23933
24068
  {
23934
24069
  method: "Fprintf",
23935
24070
  class: "fmt",
@@ -24354,7 +24489,7 @@ class GoPlugin extends BaseLanguagePlugin {
24354
24489
  }
24355
24490
  }
24356
24491
 
24357
- // node_modules/circle-ir/dist/languages/plugins/csharp.js
24492
+ // ../circle-ir/dist/languages/plugins/csharp.js
24358
24493
  class CSharpPlugin extends BaseLanguagePlugin {
24359
24494
  id = "csharp";
24360
24495
  name = "C#";
@@ -24413,7 +24548,7 @@ class CSharpPlugin extends BaseLanguagePlugin {
24413
24548
  }
24414
24549
  }
24415
24550
 
24416
- // node_modules/circle-ir/dist/languages/plugins/index.js
24551
+ // ../circle-ir/dist/languages/plugins/index.js
24417
24552
  function registerBuiltinPlugins() {
24418
24553
  registerLanguage(new JavaPlugin);
24419
24554
  registerLanguage(new JavaScriptPlugin);
@@ -24425,7 +24560,7 @@ function registerBuiltinPlugins() {
24425
24560
  registerLanguage(new GoPlugin);
24426
24561
  registerLanguage(new CSharpPlugin);
24427
24562
  }
24428
- // node_modules/circle-ir/dist/analysis/passes/cross-file-pass.js
24563
+ // ../circle-ir/dist/analysis/passes/cross-file-pass.js
24429
24564
  class CrossFilePass {
24430
24565
  run(projectGraph, sourceLines, options = {}) {
24431
24566
  const resolver = projectGraph.resolver;
@@ -24436,7 +24571,8 @@ class CrossFilePass {
24436
24571
  logger.info("cross-file: starting", { files: fileCount, budgetMs });
24437
24572
  const phase1Start = Date.now();
24438
24573
  logger.debug("cross-file: phase 1/4 starting (findCrossFileTaintFlows)");
24439
- const flows = resolver.findCrossFileTaintFlows();
24574
+ const flows = resolver.findCrossFileTaintFlows(budgetExceeded);
24575
+ let phase1Truncated = budgetExceeded();
24440
24576
  logger.info("cross-file: phase 1/4 done", {
24441
24577
  flows: flows.length,
24442
24578
  elapsedMs: Date.now() - phase1Start
@@ -24486,7 +24622,7 @@ class CrossFilePass {
24486
24622
  confidence: 0.7
24487
24623
  }];
24488
24624
  });
24489
- let exceeded = false;
24625
+ let exceeded = phase1Truncated;
24490
24626
  const ipPaths = [];
24491
24627
  if (budgetExceeded()) {
24492
24628
  exceeded = true;
@@ -24498,7 +24634,7 @@ class CrossFilePass {
24498
24634
  } else {
24499
24635
  const phase2Start = Date.now();
24500
24636
  logger.debug("cross-file: phase 2/4 starting (findInterproceduralTaintPaths)");
24501
- const phase2 = resolver.findInterproceduralTaintPaths();
24637
+ const phase2 = resolver.findInterproceduralTaintPaths(budgetExceeded);
24502
24638
  ipPaths.push(...phase2);
24503
24639
  logger.info("cross-file: phase 2/4 done", {
24504
24640
  paths: phase2.length,
@@ -24515,7 +24651,7 @@ class CrossFilePass {
24515
24651
  } else if (!exceeded) {
24516
24652
  const phase3Start = Date.now();
24517
24653
  logger.debug("cross-file: phase 3/4 starting (findFieldBindingTaintPaths)");
24518
- const phase3 = resolver.findFieldBindingTaintPaths();
24654
+ const phase3 = resolver.findFieldBindingTaintPaths(budgetExceeded);
24519
24655
  ipPaths.push(...phase3);
24520
24656
  logger.info("cross-file: phase 3/4 done", {
24521
24657
  paths: phase3.length,
@@ -24751,7 +24887,7 @@ function findCrossInstanceAliasingPaths(projectGraph, _sourceLines) {
24751
24887
  return paths;
24752
24888
  }
24753
24889
 
24754
- // node_modules/circle-ir/dist/analysis/html/html-extractor.js
24890
+ // ../circle-ir/dist/analysis/html/html-extractor.js
24755
24891
  var EVENT_HANDLER_ATTRS = new Set([
24756
24892
  "onclick",
24757
24893
  "ondblclick",
@@ -24906,7 +25042,7 @@ function stripQuotes2(text) {
24906
25042
  return text;
24907
25043
  }
24908
25044
 
24909
- // node_modules/circle-ir/dist/analysis/html/html-attribute-security-pass.js
25045
+ // ../circle-ir/dist/analysis/html/html-attribute-security-pass.js
24910
25046
  function runHtmlAttributeSecurityChecks(rootNode, filePath) {
24911
25047
  const findings = [];
24912
25048
  walkForSecurityChecks(rootNode, filePath, findings);
@@ -25132,7 +25268,7 @@ function truncate(s, maxLen) {
25132
25268
  return s.length > maxLen ? s.slice(0, maxLen) + "..." : s;
25133
25269
  }
25134
25270
 
25135
- // node_modules/circle-ir/dist/analysis/html/vue-template-xss-pass.js
25271
+ // ../circle-ir/dist/analysis/html/vue-template-xss-pass.js
25136
25272
  var DANGEROUS_BINDINGS = new Set([
25137
25273
  "v-html",
25138
25274
  "v-bind:innerhtml",
@@ -25252,7 +25388,7 @@ function collectTaintedNames(blocks) {
25252
25388
  return names;
25253
25389
  }
25254
25390
 
25255
- // node_modules/circle-ir/dist/analysis/html/html-merge.js
25391
+ // ../circle-ir/dist/analysis/html/html-merge.js
25256
25392
  function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
25257
25393
  const allTypes = [];
25258
25394
  const allCalls = [];
@@ -25402,7 +25538,7 @@ function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
25402
25538
  };
25403
25539
  }
25404
25540
 
25405
- // node_modules/circle-ir/dist/analysis/passes/taint-matcher-pass.js
25541
+ // ../circle-ir/dist/analysis/passes/taint-matcher-pass.js
25406
25542
  class TaintMatcherPass {
25407
25543
  name = "taint-matcher";
25408
25544
  category = "security";
@@ -25463,7 +25599,7 @@ class TaintMatcherPass {
25463
25599
  }
25464
25600
  }
25465
25601
 
25466
- // node_modules/circle-ir/dist/analysis/passes/constant-propagation-pass.js
25602
+ // ../circle-ir/dist/analysis/passes/constant-propagation-pass.js
25467
25603
  class ConstantPropagationPass {
25468
25604
  tree;
25469
25605
  name = "constant-propagation";
@@ -25493,7 +25629,7 @@ class ConstantPropagationPass {
25493
25629
  }
25494
25630
  }
25495
25631
 
25496
- // node_modules/circle-ir/dist/analysis/passes/language-sources-pass.js
25632
+ // ../circle-ir/dist/analysis/passes/language-sources-pass.js
25497
25633
  var JS_DOM_XSS_SINKS = [
25498
25634
  { pattern: /\.innerHTML\s*=/, type: "xss", cwe: "CWE-79", severity: "critical" },
25499
25635
  { pattern: /\.outerHTML\s*=/, type: "xss", cwe: "CWE-79", severity: "critical" },
@@ -25611,6 +25747,8 @@ class LanguageSourcesPass {
25611
25747
  additionalSources.push(...findCSharpBindingAttributeSources(code, language));
25612
25748
  additionalSources.push(...findCSharpMinimalApiSources(code, language));
25613
25749
  additionalSources.push(...findGoArgvSources(code, language));
25750
+ additionalSources.push(...findGoArchiveEntrySources(code, language));
25751
+ additionalSources.push(...findGoMultipartUploadSources(code, language));
25614
25752
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
25615
25753
  for (const s of jsDOMSinks) {
25616
25754
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -25681,6 +25819,7 @@ class LanguageSourcesPass {
25681
25819
  if (language === "go") {
25682
25820
  additionalSanitizers.push(...findGoMapAllowlistGuardSanitizers(code));
25683
25821
  additionalSanitizers.push(...findGoHtmlTemplateImportSanitizers(code));
25822
+ additionalSanitizers.push(...findGoPathContainmentGuardSanitizers(code));
25684
25823
  const goMisconfigFindings = findGoPatternFindings(code, graph.ir.meta.file);
25685
25824
  for (const finding of goMisconfigFindings) {
25686
25825
  ctx.addFinding(finding);
@@ -25718,6 +25857,7 @@ class LanguageSourcesPass {
25718
25857
  additionalSanitizers.push(...findPythonUrlAllowlistRedirectSanitizers(code));
25719
25858
  additionalSanitizers.push(...findPythonDefusedXmlSanitizers(code));
25720
25859
  additionalSanitizers.push(...findPythonJinjaAutoescapeSanitizers(code));
25860
+ additionalSanitizers.push(...findCrlfWrapperFunctionSanitizers(code, "python"));
25721
25861
  const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
25722
25862
  for (const finding of pyMisconfigFindings) {
25723
25863
  ctx.addFinding(finding);
@@ -25821,12 +25961,16 @@ class LanguageSourcesPass {
25821
25961
  ctx.addFinding(finding);
25822
25962
  }
25823
25963
  }
25964
+ if (language === "csharp") {
25965
+ additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code));
25966
+ }
25824
25967
  if (language === "java") {
25825
25968
  additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
25826
25969
  additionalSanitizers.push(...findJavaPathNormalizeStartsWithGuardSanitizers(code));
25827
25970
  additionalSanitizers.push(...findJavaPathGetFileNameSanitizers(code));
25828
25971
  additionalSanitizers.push(...findJavaCanonicalPathStartsWithGuardSanitizers(code));
25829
25972
  additionalSanitizers.push(...findJavaInlineCrlfStripLogSanitizers(code));
25973
+ additionalSanitizers.push(...findCrlfWrapperFunctionSanitizers(code, "java"));
25830
25974
  additionalSanitizers.push(...findJavaArgvFormExecSanitizers(code));
25831
25975
  for (const finding of findJavaPatternFindings(code, graph.ir.meta.file)) {
25832
25976
  ctx.addFinding(finding);
@@ -26242,13 +26386,121 @@ function findGoArgvSources(sourceCode, language) {
26242
26386
  }
26243
26387
  return sources;
26244
26388
  }
26389
+ function findGoArchiveEntrySources(sourceCode, language) {
26390
+ if (language !== "go")
26391
+ return [];
26392
+ if (!/\bimport\b[\s\S]{0,400}?archive\/(?:zip|tar)|"archive\/(?:zip|tar)"/.test(sourceCode))
26393
+ return [];
26394
+ const sources = [];
26395
+ const lines = sourceCode.split(`
26396
+ `);
26397
+ const entryVars = new Set;
26398
+ const zipRange = /\bfor\b[^;{]*?,\s*([A-Za-z_]\w*)\s*:?=\s*range\s+[A-Za-z_][\w.]*\.File\b/;
26399
+ const tarNext = /\b([A-Za-z_]\w*)\s*,\s*[A-Za-z_]\w*\s*:?=\s*[A-Za-z_][\w.]*\.Next\s*\(\s*\)/;
26400
+ for (const line of lines) {
26401
+ const z = zipRange.exec(line);
26402
+ if (z)
26403
+ entryVars.add(z[1]);
26404
+ const t = tarNext.exec(line);
26405
+ if (t)
26406
+ entryVars.add(t[1]);
26407
+ }
26408
+ if (entryVars.size === 0)
26409
+ return sources;
26410
+ const assignRe = /^\s*(?:var\s+)?([A-Za-z_]\w*)\s*(?::?=|=)\s*(.+?)\s*$/;
26411
+ for (let i2 = 0;i2 < lines.length; i2++) {
26412
+ const m = assignRe.exec(lines[i2]);
26413
+ if (!m)
26414
+ continue;
26415
+ const [, lhs, rhs] = m;
26416
+ let matched = null;
26417
+ for (const v of entryVars) {
26418
+ if (new RegExp(`(?<![\\w.])${v}\\s*\\.\\s*Name\\b`).test(rhs)) {
26419
+ matched = v;
26420
+ break;
26421
+ }
26422
+ }
26423
+ if (!matched)
26424
+ continue;
26425
+ const lineNumber = i2 + 1;
26426
+ if (sources.some((sc) => sc.line === lineNumber && sc.variable === lhs))
26427
+ continue;
26428
+ sources.push({
26429
+ type: "file_input",
26430
+ location: `${lhs} derived from archive entry name ${matched}.Name (Zip/Tar Slip)`,
26431
+ severity: "high",
26432
+ line: lineNumber,
26433
+ confidence: 0.9,
26434
+ variable: lhs
26435
+ });
26436
+ }
26437
+ return sources;
26438
+ }
26439
+ function findGoMultipartUploadSources(sourceCode, language) {
26440
+ if (language !== "go")
26441
+ return [];
26442
+ if (!/\.\s*(?:FormFile|MultipartForm|MultipartReader)\b/.test(sourceCode))
26443
+ return [];
26444
+ const sources = [];
26445
+ const lines = sourceCode.split(`
26446
+ `);
26447
+ const headerVars = new Set;
26448
+ 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*\(/;
26449
+ for (let i2 = 0;i2 < lines.length; i2++) {
26450
+ const m = formFileRe.exec(lines[i2]);
26451
+ if (!m)
26452
+ continue;
26453
+ const v = m[1];
26454
+ if (v === "_")
26455
+ continue;
26456
+ headerVars.add(v);
26457
+ sources.push({
26458
+ type: "http_param",
26459
+ location: `${v}.Filename from r.FormFile (client-supplied upload filename)`,
26460
+ severity: "high",
26461
+ line: i2 + 1,
26462
+ confidence: 0.9,
26463
+ variable: v
26464
+ });
26465
+ }
26466
+ const assignRe = /^\s*(?:var\s+)?([A-Za-z_]\w*)\s*(?::?=|=)\s*(.+?)\s*$/;
26467
+ for (let i2 = 0;i2 < lines.length; i2++) {
26468
+ const m = assignRe.exec(lines[i2]);
26469
+ if (!m)
26470
+ continue;
26471
+ const [, lhs, rhs] = m;
26472
+ if (lhs === "_")
26473
+ continue;
26474
+ let matched = null;
26475
+ for (const v of headerVars) {
26476
+ if (new RegExp(`(?<![\\w.])${v}\\s*\\.\\s*Filename\\b`).test(rhs)) {
26477
+ matched = v;
26478
+ break;
26479
+ }
26480
+ }
26481
+ if (!matched)
26482
+ continue;
26483
+ const lineNumber = i2 + 1;
26484
+ if (sources.some((sc) => sc.line === lineNumber && sc.variable === lhs))
26485
+ continue;
26486
+ sources.push({
26487
+ type: "http_param",
26488
+ location: `${lhs} derived from upload filename ${matched}.Filename`,
26489
+ severity: "high",
26490
+ line: lineNumber,
26491
+ confidence: 0.9,
26492
+ variable: lhs
26493
+ });
26494
+ }
26495
+ return sources;
26496
+ }
26245
26497
  function findCSharpRequestSources(sourceCode, language) {
26246
26498
  if (language !== "csharp")
26247
26499
  return [];
26248
26500
  const sources = [];
26249
26501
  const lines = sourceCode.split(`
26250
26502
  `);
26251
- const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
26503
+ const assignRe = /^\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
26252
26504
  const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
26253
26505
  const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
26254
26506
  const envReadRe = /\bEnvironment\s*\.\s*GetEnvironmentVariables?\s*(?:\(|\[)/;
@@ -26954,6 +27206,12 @@ function findPythonReflectionInvocationSinks(sourceCode, taintedVars) {
26954
27206
  }
26955
27207
  return sinks;
26956
27208
  }
27209
+ function isProvablyLiteralDomAssignment(line) {
27210
+ const eq = line.indexOf("=");
27211
+ if (eq < 0)
27212
+ return false;
27213
+ return isProvablyLiteralExpression(line.slice(eq + 1));
27214
+ }
26957
27215
  function findJavaScriptDOMSinks(sourceCode, language) {
26958
27216
  if (!["javascript", "typescript"].includes(language))
26959
27217
  return [];
@@ -26982,6 +27240,9 @@ function findJavaScriptDOMSinks(sourceCode, language) {
26982
27240
  method = "cssText";
26983
27241
  else if (line.includes("style.textContent"))
26984
27242
  method = "textContent";
27243
+ if (DOM_ASSIGNMENT_SINK_METHODS.has(method) && isProvablyLiteralDomAssignment(line)) {
27244
+ continue;
27245
+ }
26985
27246
  if (method === "document.write" || method === "document.writeln") {
26986
27247
  const argMatch = line.match(/document\.write(?:ln)?\s*\(\s*([^)]*)\)/);
26987
27248
  if (argMatch) {
@@ -27747,6 +28008,47 @@ function findGoMapAllowlistGuardSanitizers(code) {
27747
28008
  }
27748
28009
  return sanitizers;
27749
28010
  }
28011
+ function findGoPathContainmentGuardSanitizers(code) {
28012
+ const sanitizers = [];
28013
+ const lines = code.split(`
28014
+ `);
28015
+ const REJECT = /\b(?:continue|break|return|panic|fmt\.Errorf|errors\.New)\b/;
28016
+ const guarded = new Map;
28017
+ for (let i2 = 0;i2 < lines.length; i2++) {
28018
+ const line = lines[i2];
28019
+ const hp = /if\s*!\s*strings\s*\.\s*HasPrefix\s*\(\s*([A-Za-z_]\w*)\s*,/.exec(line);
28020
+ if (hp && guardRejects(lines, i2, REJECT, 4)) {
28021
+ if (!guarded.has(hp[1]))
28022
+ guarded.set(hp[1], i2 + 1);
28023
+ continue;
28024
+ }
28025
+ 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);
28026
+ if (rel) {
28027
+ const window2 = lines.slice(i2, i2 + 5).join(`
28028
+ `);
28029
+ if (/\.\./.test(window2) && REJECT.test(window2)) {
28030
+ if (!guarded.has(rel[2]))
28031
+ guarded.set(rel[2], i2 + 1);
28032
+ }
28033
+ }
28034
+ }
28035
+ if (guarded.size === 0)
28036
+ return sanitizers;
28037
+ for (const [name2, guardLine] of guarded) {
28038
+ const useRe = new RegExp(`(?<![\\w.])${name2}(?![\\w])`);
28039
+ for (let l = guardLine;l < lines.length; l++) {
28040
+ if (!useRe.test(lines[l]))
28041
+ continue;
28042
+ sanitizers.push({
28043
+ type: "go_path_containment_guard",
28044
+ method: "strings.HasPrefix",
28045
+ line: l + 1,
28046
+ sanitizes: ["path_traversal"]
28047
+ });
28048
+ }
28049
+ }
28050
+ return sanitizers;
28051
+ }
27750
28052
  function findGoHtmlTemplateImportSanitizers(code) {
27751
28053
  const sanitizers = [];
27752
28054
  const hasHtmlTemplate = /["\s]html\/template["\s]/.test(code);
@@ -28713,7 +29015,7 @@ function findJsPathResolveStartsWithGuardSanitizers(code) {
28713
29015
  for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
28714
29016
  if (!guardRe.test(lines[l]))
28715
29017
  continue;
28716
- if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
29018
+ if (guardRejects(lines, l, terminatorRe, 2)) {
28717
29019
  guardLine = l + 1;
28718
29020
  break;
28719
29021
  }
@@ -28833,7 +29135,7 @@ function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
28833
29135
  for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
28834
29136
  if (!guardRe.test(lines[l]))
28835
29137
  continue;
28836
- if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
29138
+ if (guardRejects(lines, l, terminatorRe, 2)) {
28837
29139
  guardLine = l + 1;
28838
29140
  break;
28839
29141
  }
@@ -28871,18 +29173,7 @@ function findJavaCanonicalPathStartsWithGuardSanitizers(code) {
28871
29173
  const m = guardRe.exec(lines[i2]);
28872
29174
  if (!m)
28873
29175
  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)
29176
+ if (!guardRejects(lines, i2, terminatorRe, 6))
28886
29177
  continue;
28887
29178
  const guarded = new Set([m[1]]);
28888
29179
  const mentions = (t) => {
@@ -28912,6 +29203,71 @@ function findJavaCanonicalPathStartsWithGuardSanitizers(code) {
28912
29203
  }
28913
29204
  return sanitizers;
28914
29205
  }
29206
+ function guardRejects(lines, i2, terminatorRe, window2) {
29207
+ if (terminatorRe.test(lines[i2]))
29208
+ return true;
29209
+ const opens = (lines[i2].match(/\{/g) ?? []).length;
29210
+ const closes = (lines[i2].match(/\}/g) ?? []).length;
29211
+ if (opens > 0 && closes >= opens)
29212
+ return false;
29213
+ for (let j = i2 + 1;j < Math.min(lines.length, i2 + window2); j++) {
29214
+ if (terminatorRe.test(lines[j]))
29215
+ return true;
29216
+ if (lines[j].includes("}"))
29217
+ return false;
29218
+ }
29219
+ return false;
29220
+ }
29221
+ function findCSharpFullPathStartsWithGuardSanitizers(code) {
29222
+ const sanitizers = [];
29223
+ const lines = code.split(`
29224
+ `);
29225
+ const assignRe = /^\s*(?:(?:var|string)\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
29226
+ const fullPathRe = /\bPath\s*\.\s*GetFullPath\s*\(/;
29227
+ const canonicalVars = new Set;
29228
+ for (const line of lines) {
29229
+ const a = assignRe.exec(line);
29230
+ if (a && fullPathRe.test(a[2]))
29231
+ canonicalVars.add(a[1]);
29232
+ }
29233
+ if (canonicalVars.size === 0)
29234
+ return sanitizers;
29235
+ const guardRe = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*StartsWith\s*\(/;
29236
+ const terminatorRe = /\b(throw|return)\b/;
29237
+ for (let i2 = 0;i2 < lines.length; i2++) {
29238
+ const m = guardRe.exec(lines[i2]);
29239
+ if (!m || !canonicalVars.has(m[1]))
29240
+ continue;
29241
+ if (!guardRejects(lines, i2, terminatorRe, 6))
29242
+ continue;
29243
+ const guarded = new Set([m[1]]);
29244
+ const mentions = (t) => {
29245
+ for (const n of guarded)
29246
+ if (new RegExp(`\\b${n}\\b`).test(t))
29247
+ return true;
29248
+ return false;
29249
+ };
29250
+ for (let l = 0;l < lines.length; l++) {
29251
+ const lt = lines[l];
29252
+ const a = assignRe.exec(lt);
29253
+ if (a) {
29254
+ if (mentions(a[2]))
29255
+ guarded.add(a[1]);
29256
+ else if (a[1] !== m[1])
29257
+ guarded.delete(a[1]);
29258
+ }
29259
+ if (mentions(lt)) {
29260
+ sanitizers.push({
29261
+ type: "csharp_fullpath_startswith_guard",
29262
+ method: "GetFullPath",
29263
+ line: l + 1,
29264
+ sanitizes: ["path_traversal", "external_taint_escape"]
29265
+ });
29266
+ }
29267
+ }
29268
+ }
29269
+ return sanitizers;
29270
+ }
28915
29271
  function findJavaPathGetFileNameSanitizers(code) {
28916
29272
  const sanitizers = [];
28917
29273
  const lines = code.split(`
@@ -28955,6 +29311,41 @@ function findJavaPathGetFileNameSanitizers(code) {
28955
29311
  }
28956
29312
  return sanitizers;
28957
29313
  }
29314
+ function findCrlfWrapperFunctionSanitizers(code, language) {
29315
+ const sanitizers = [];
29316
+ const lines = code.split(`
29317
+ `);
29318
+ 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*\(/;
29319
+ const stripRe = language === "java" ? /\.\s*replace(?:All)?\s*\(\s*(?:"[^"]*\\[rnt]|'\\[rnt]')/ : /(?:re\s*\.\s*sub\s*\(\s*r?["'][^"']*\\[rnt]|\.\s*replace\s*\(\s*["']\\[rnt])/;
29320
+ const bodyWindow = 10;
29321
+ const wrappers = new Set;
29322
+ for (let i2 = 0;i2 < lines.length; i2++) {
29323
+ const m = declRe.exec(lines[i2]);
29324
+ if (!m)
29325
+ continue;
29326
+ const body2 = lines.slice(i2, Math.min(lines.length, i2 + bodyWindow)).join(`
29327
+ `);
29328
+ if (stripRe.test(body2))
29329
+ wrappers.add(m[1]);
29330
+ }
29331
+ if (wrappers.size === 0)
29332
+ return sanitizers;
29333
+ for (let i2 = 0;i2 < lines.length; i2++) {
29334
+ for (const name2 of wrappers) {
29335
+ if (declRe.test(lines[i2]) && new RegExp(`\\b${name2}\\s*\\(`).test(lines[i2]))
29336
+ continue;
29337
+ if (!new RegExp(`\\b${name2}\\s*\\(`).test(lines[i2]))
29338
+ continue;
29339
+ sanitizers.push({
29340
+ type: language === "java" ? "java_wrapper_crlf_strip" : "python_wrapper_crlf_strip",
29341
+ method: name2,
29342
+ line: i2 + 1,
29343
+ sanitizes: ["log_injection", "external_taint_escape"]
29344
+ });
29345
+ }
29346
+ }
29347
+ return sanitizers;
29348
+ }
28958
29349
  function findJavaInlineCrlfStripLogSanitizers(code) {
28959
29350
  const sanitizers = [];
28960
29351
  const lines = code.split(`
@@ -32550,7 +32941,7 @@ function findJsTemplateInjectionSstiFindings(code, file) {
32550
32941
  return findings;
32551
32942
  }
32552
32943
 
32553
- // node_modules/circle-ir/dist/analysis/passes/source-semantics-pass.js
32944
+ // ../circle-ir/dist/analysis/passes/source-semantics-pass.js
32554
32945
  var DEMO_PATH_RE = /(?:^|\/)(?:demo|example|examples|samples|integration-tests|integration_tests)(?:\/|$)/i;
32555
32946
  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
32947
  var STATIC_FINAL_RE = /^\s*(?:public\s+|private\s+|protected\s+)?static\s+final\s+/;
@@ -32638,7 +33029,7 @@ class SourceSemanticsPass {
32638
33029
  }
32639
33030
  }
32640
33031
 
32641
- // node_modules/circle-ir/dist/analysis/passes/library-profile-source-gate-pass.js
33032
+ // ../circle-ir/dist/analysis/passes/library-profile-source-gate-pass.js
32642
33033
  var SPECULATIVE_SOURCE_TYPES = new Set([
32643
33034
  "interprocedural_param",
32644
33035
  "constructor_field"
@@ -32663,7 +33054,7 @@ class LibraryProfileSourceGatePass {
32663
33054
  droppedByType: {}
32664
33055
  };
32665
33056
  }
32666
- const sources = graph.ir.taint.sources;
33057
+ const sources = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sources : graph.ir.taint.sources;
32667
33058
  if (sources.length === 0) {
32668
33059
  return {
32669
33060
  profile,
@@ -32673,17 +33064,12 @@ class LibraryProfileSourceGatePass {
32673
33064
  };
32674
33065
  }
32675
33066
  const droppedByType = {};
32676
- const kept = [];
32677
33067
  for (const src of sources) {
32678
33068
  if (SPECULATIVE_SOURCE_TYPES.has(src.type)) {
32679
33069
  droppedByType[src.type] = (droppedByType[src.type] ?? 0) + 1;
32680
- continue;
32681
33070
  }
32682
- kept.push(src);
32683
33071
  }
32684
- const dropped = sources.length - kept.length;
32685
- sources.length = 0;
32686
- sources.push(...kept);
33072
+ const dropped = 0;
32687
33073
  return {
32688
33074
  profile,
32689
33075
  applied: true,
@@ -32693,7 +33079,7 @@ class LibraryProfileSourceGatePass {
32693
33079
  }
32694
33080
  }
32695
33081
 
32696
- // node_modules/circle-ir/dist/analysis/passes/mybatis-annotation-sql-sink-pass.js
33082
+ // ../circle-ir/dist/analysis/passes/mybatis-annotation-sql-sink-pass.js
32697
33083
  var MYBATIS_SQL_ANNOTATIONS = new Set([
32698
33084
  "Select",
32699
33085
  "Update",
@@ -32920,7 +33306,7 @@ class MyBatisAnnotationSqlSinkPass {
32920
33306
  }
32921
33307
  }
32922
33308
 
32923
- // node_modules/circle-ir/dist/analysis/passes/sink-filter-pass.js
33309
+ // ../circle-ir/dist/analysis/passes/sink-filter-pass.js
32924
33310
  function escapeReSf(s) {
32925
33311
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
32926
33312
  }
@@ -33027,7 +33413,32 @@ var PROTOCOL_CLIENT_PACKAGES = [
33027
33413
  "org.eclipse.paho"
33028
33414
  ];
33029
33415
  var OS_EXEC_RECEIVER_RE = /\b(?:Runtime|ProcessBuilder|DefaultExecutor|Executor|Exec|Launcher|ProcStarter|ProcessExecutor|RuntimeUtil)\s*[.(]/;
33416
+ var JAVA_STDOUT_PRINT_RE = /\bSystem\s*\.\s*(?:out|err)\s*\.\s*(?:println|print|printf|write|append)\s*\(/;
33030
33417
  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*,)/;
33418
+ 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*,)/;
33419
+ var SHELL_PROGRAMS = new Set([
33420
+ "sh",
33421
+ "bash",
33422
+ "zsh",
33423
+ "ksh",
33424
+ "dash",
33425
+ "ash",
33426
+ "csh",
33427
+ "tcsh",
33428
+ "fish",
33429
+ "cmd",
33430
+ "command",
33431
+ "powershell",
33432
+ "pwsh"
33433
+ ]);
33434
+ 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*"([^"]*)"/;
33435
+ function argvFirstLiteralIsShell(lineText) {
33436
+ const m = ARGV_FIRST_LITERAL_RE.exec(lineText);
33437
+ if (!m)
33438
+ return false;
33439
+ const base = m[1].split(/[\\/]/).pop().replace(/\.exe$/i, "").toLowerCase();
33440
+ return SHELL_PROGRAMS.has(base);
33441
+ }
33031
33442
  var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b[^;]*;\s*(?:\/\/.*|\/\*.*)?$/;
33032
33443
  var JEXL_ENGINE_TYPES = new Set(["JexlEngine", "Jexl", "JxltEngine"]);
33033
33444
  var JEXL_EXPRESSION_TYPE_RE = /(?:Jexl)?(?:Expression|Script|Template)(?:Script)?$/;
@@ -33197,10 +33608,41 @@ function jsSsrfHostGuardedLines(code) {
33197
33608
  }
33198
33609
  return covered;
33199
33610
  }
33611
+ 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*\)/;
33612
+ function isUnwrittenLocalMapKeyRead(code, source) {
33613
+ if (source.type !== "plugin_param")
33614
+ return false;
33615
+ const lines = code.split(`
33616
+ `);
33617
+ const lineText = lines[source.line - 1] ?? "";
33618
+ const read = /\b([A-Za-z_]\w*)\s*\.\s*get\s*\(\s*"([^"]*)"\s*\)/.exec(lineText);
33619
+ if (!read)
33620
+ return false;
33621
+ const [, recv, key] = read;
33622
+ const ctorRe = new RegExp(`\\b${recv}\\s*=\\s*new\\s+(?:Hash|Tree|LinkedHash|ConcurrentHash|Concurrent|Identity|Weak)?Map\\s*(?:<[^>]*>)?\\s*\\(`);
33623
+ if (!lines.some((l) => ctorRe.test(l)))
33624
+ return false;
33625
+ const escapeRe2 = new RegExp(`\\w\\s*\\([^)]*(?<![\\w.])${recv}(?![\\w.])[^)]*\\)`);
33626
+ for (const l of lines) {
33627
+ if (!escapeRe2.test(l))
33628
+ continue;
33629
+ const stripped = l.replace(new RegExp(`\\b${recv}\\s*\\.\\s*\\w+\\s*\\(`, "g"), "X(");
33630
+ if (escapeRe2.test(stripped))
33631
+ return false;
33632
+ }
33633
+ const putRe = new RegExp(`\\b${recv}\\s*\\.\\s*put\\s*\\(\\s*"${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"`);
33634
+ if (lines.some((l) => putRe.test(l)))
33635
+ return false;
33636
+ return true;
33637
+ }
33200
33638
  var CSHARP_SANITIZER_RES = [
33201
33639
  { re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: "xss" },
33202
33640
  { re: /\bHtmlEncoder\s*\.\s*Encode\s*\(/, type: "xss" },
33203
- { re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" }
33641
+ { re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" },
33642
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "ldap_injection" },
33643
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "xpath_injection" },
33644
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "command_injection" },
33645
+ { re: CSHARP_ALLOWLIST_STRIP_RE, type: "sql_injection" }
33204
33646
  ];
33205
33647
  function csharpSanitizedVarsByType(code) {
33206
33648
  const lines = code.split(`
@@ -33444,6 +33886,24 @@ function hasJavaImportFromPackage(packagePrefix, sourceLines) {
33444
33886
  }
33445
33887
  return false;
33446
33888
  }
33889
+ function hasSameOriginLiteralPrefix(lineText) {
33890
+ const m = lineText.match(/\(\s*(['"`])([^'"`]*)\1\s*(?:\+|,|\))/);
33891
+ const prefix = m?.[2];
33892
+ if (prefix === undefined) {
33893
+ const t = lineText.match(/\(\s*`([^`$]*)\$\{/);
33894
+ if (!t)
33895
+ return false;
33896
+ return isSameOriginQueryPrefix(t[1]);
33897
+ }
33898
+ return isSameOriginQueryPrefix(prefix);
33899
+ }
33900
+ function isSameOriginQueryPrefix(prefix) {
33901
+ if (!prefix.startsWith("/"))
33902
+ return false;
33903
+ if (prefix.startsWith("//"))
33904
+ return false;
33905
+ return prefix.includes("?");
33906
+ }
33447
33907
 
33448
33908
  class SinkFilterPass {
33449
33909
  name = "sink-filter";
@@ -33455,7 +33915,8 @@ class SinkFilterPass {
33455
33915
  const constProp = ctx.getResult("constant-propagation");
33456
33916
  const langSources = ctx.getResult("language-sources");
33457
33917
  const mergedSources = [...taintMatcher.sources, ...langSources.additionalSources];
33458
- const sources = ctx.code && ctx.language ? mergedSources.filter((s) => !isNonExecutableSourceLine(ctx.code, s.line, ctx.language)) : mergedSources;
33918
+ const executableSources = ctx.code && ctx.language ? mergedSources.filter((s) => !isNonExecutableSourceLine(ctx.code, s.line, ctx.language)) : mergedSources;
33919
+ const sources = ctx.code ? executableSources.filter((s) => !isUnwrittenLocalMapKeyRead(ctx.code, s)) : executableSources;
33459
33920
  const sinks = [...taintMatcher.sinks];
33460
33921
  for (const s of langSources.additionalSinks) {
33461
33922
  if (!sinks.some((x) => x.line === s.line && x.cwe === s.cwe && x.type === s.type)) {
@@ -33608,6 +34069,9 @@ class SinkFilterPass {
33608
34069
  if (setHeaderMatch) {
33609
34070
  return false;
33610
34071
  }
34072
+ if (sink.type === "open_redirect" && hasSameOriginLiteralPrefix(sinkLineText)) {
34073
+ return false;
34074
+ }
33611
34075
  if (sink.method === "cookie" && sink.type === "crlf") {
33612
34076
  return false;
33613
34077
  }
@@ -33747,13 +34211,23 @@ class SinkFilterPass {
33747
34211
  filtered = filtered.filter((sink) => {
33748
34212
  if (sink.type !== "command_injection")
33749
34213
  return true;
33750
- if (sink.method !== "ProcessBuilder" && sink.method !== "start")
34214
+ if (sink.method !== "ProcessBuilder" && sink.method !== "start" && sink.method !== "command") {
33751
34215
  return true;
34216
+ }
33752
34217
  const sinkLineText = sourceLines[sink.line - 1] ?? "";
33753
- if (!/\bnew\s+ProcessBuilder\s*\(/.test(sinkLineText))
34218
+ const isCtorLine = /\bnew\s+ProcessBuilder\s*\(/.test(sinkLineText);
34219
+ const isCommandLine = /\.\s*command\s*\(/.test(sinkLineText);
34220
+ if (!isCtorLine && !isCommandLine)
33754
34221
  return true;
33755
- if (PROCESS_BUILDER_ARGV_FORM_RE.test(sinkLineText))
34222
+ if (argvFirstLiteralIsShell(sinkLineText))
34223
+ return true;
34224
+ if (isCtorLine && PROCESS_BUILDER_ARGV_FORM_RE.test(sinkLineText))
34225
+ return false;
34226
+ if (isCommandLine && PROCESS_BUILDER_COMMAND_ARGV_FORM_RE.test(sinkLineText)) {
33756
34227
  return false;
34228
+ }
34229
+ if (!isCtorLine)
34230
+ return true;
33757
34231
  const ctorCall = irCalls.find((c) => c.method_name === "ProcessBuilder" && c.location.line === sink.line && (c.receiver === null || c.receiver === undefined));
33758
34232
  if (!ctorCall || ctorCall.arguments.length === 0)
33759
34233
  return true;
@@ -34023,6 +34497,15 @@ class SinkFilterPass {
34023
34497
  if (language === "java" && !HTML_CONTENT_TYPE_RE.test(ctx.code)) {
34024
34498
  filtered = filtered.filter((sink) => !(sink.type === "xss" && sink.method === "body"));
34025
34499
  }
34500
+ if (language === "java") {
34501
+ const sourceLines = ctx.code.split(`
34502
+ `);
34503
+ filtered = filtered.filter((sink) => {
34504
+ if (sink.type !== "xss" || sink.class !== undefined)
34505
+ return true;
34506
+ return !JAVA_STDOUT_PRINT_RE.test(sourceLines[sink.line - 1] ?? "");
34507
+ });
34508
+ }
34026
34509
  if (language === "java") {
34027
34510
  const guardedLines = javaTraversalRejectGuardedLines(ctx.code);
34028
34511
  if (guardedLines.size > 0) {
@@ -34108,6 +34591,43 @@ class SinkFilterPass {
34108
34591
  return true;
34109
34592
  });
34110
34593
  }
34594
+ if (language === "csharp") {
34595
+ const sourceLines = ctx.code.split(`
34596
+ `);
34597
+ const constBaseClients = new Set;
34598
+ 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*\)/;
34599
+ for (const line of sourceLines) {
34600
+ const m = declRe.exec(line);
34601
+ if (m)
34602
+ constBaseClients.add(m[1]);
34603
+ }
34604
+ if (constBaseClients.size > 0) {
34605
+ const relativeLiteralArgRe = /\.\s*(?:Get|Post|Put|Patch|Delete|Send|GetString|GetByteArray|GetStream)\w*Async\s*\(\s*(?:@?\$?)"([^"]*)"/;
34606
+ filtered = filtered.filter((sink) => {
34607
+ if (sink.type !== "ssrf")
34608
+ return true;
34609
+ const text = sourceLines[sink.line - 1] ?? "";
34610
+ let onConstClient = false;
34611
+ for (const v of constBaseClients) {
34612
+ if (new RegExp(`(?<![\\w])${escapeRegex(v)}\\s*\\.`).test(text)) {
34613
+ onConstClient = true;
34614
+ break;
34615
+ }
34616
+ }
34617
+ if (!onConstClient)
34618
+ return true;
34619
+ const m = relativeLiteralArgRe.exec(text);
34620
+ if (!m)
34621
+ return true;
34622
+ const lit = m[1];
34623
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(lit))
34624
+ return true;
34625
+ if (lit.startsWith("//"))
34626
+ return true;
34627
+ return false;
34628
+ });
34629
+ }
34630
+ }
34111
34631
  if (["javascript", "typescript"].includes(language)) {
34112
34632
  const sourceLines = ctx.code.split(`
34113
34633
  `);
@@ -34724,7 +35244,7 @@ function isXPathQuoteEscapedOnLine(lineText, varName) {
34724
35244
  return seen > 0;
34725
35245
  }
34726
35246
 
34727
- // node_modules/circle-ir/dist/analysis/passes/sink-semantics-pass.js
35247
+ // ../circle-ir/dist/analysis/passes/sink-semantics-pass.js
34728
35248
  function buildRegistry(entries) {
34729
35249
  const registry = new Map;
34730
35250
  for (const entry of entries) {
@@ -34772,7 +35292,7 @@ class SinkSemanticsPass {
34772
35292
  }
34773
35293
  }
34774
35294
 
34775
- // node_modules/circle-ir/dist/analysis/dependency-versions.js
35295
+ // ../circle-ir/dist/analysis/dependency-versions.js
34776
35296
  function resolveFastjsonFromPom(pomXml) {
34777
35297
  if (!pomXml)
34778
35298
  return null;
@@ -34950,7 +35470,7 @@ function fileConfiguresSnakeYamlSafely(source) {
34950
35470
  return false;
34951
35471
  }
34952
35472
 
34953
- // node_modules/circle-ir/dist/analysis/passes/deserialization-safety-gate-pass.js
35473
+ // ../circle-ir/dist/analysis/passes/deserialization-safety-gate-pass.js
34954
35474
  var FASTJSON_METHODS = new Set(["parseObject", "parse"]);
34955
35475
  var FASTJSON_CLASSES = new Set(["JSON", "JSONObject"]);
34956
35476
  var JACKSON_METHODS = new Set(["readValue", "convertValue", "treeToValue"]);
@@ -35027,7 +35547,7 @@ class DeserializationSafetyGatePass {
35027
35547
  }
35028
35548
  }
35029
35549
 
35030
- // node_modules/circle-ir/dist/analysis/passes/prompt-injection-safety-gate-pass.js
35550
+ // ../circle-ir/dist/analysis/passes/prompt-injection-safety-gate-pass.js
35031
35551
  function isDelimiterLiteral(lit) {
35032
35552
  const s = lit.trim();
35033
35553
  if (s.length === 0)
@@ -35270,7 +35790,7 @@ class PromptInjectionSafetyGatePass {
35270
35790
  }
35271
35791
  }
35272
35792
 
35273
- // node_modules/circle-ir/dist/analysis/passes/speculative-prompt-param-source-pass.js
35793
+ // ../circle-ir/dist/analysis/passes/speculative-prompt-param-source-pass.js
35274
35794
  function returnOrAssignRhs(text) {
35275
35795
  const t = text.trim();
35276
35796
  const ret = t.match(/^return\s+(.+?);?$/);
@@ -35396,7 +35916,7 @@ class SpeculativePromptParamSourcePass {
35396
35916
  }
35397
35917
  }
35398
35918
 
35399
- // node_modules/circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
35919
+ // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
35400
35920
  var REFLECTION_SINK_METHODS = new Set([
35401
35921
  "forName",
35402
35922
  "newInstance",
@@ -35584,7 +36104,7 @@ class CliMainReflectionSuppressPass {
35584
36104
  }
35585
36105
  }
35586
36106
 
35587
- // node_modules/circle-ir/dist/analysis/passes/library-profile-sink-gate-pass.js
36107
+ // ../circle-ir/dist/analysis/passes/library-profile-sink-gate-pass.js
35588
36108
  var DROPPED_SINK_TYPES = new Set([
35589
36109
  "log_injection"
35590
36110
  ]);
@@ -35658,7 +36178,7 @@ class LibraryProfileCwe22PathGatePass {
35658
36178
  droppedBySourceType: {}
35659
36179
  };
35660
36180
  }
35661
- const flows = graph.ir.taint.flows;
36181
+ const flows = ctx.hasResult("interprocedural") ? ctx.getResult("interprocedural").additionalFlows : graph.ir.taint.flows;
35662
36182
  if (!flows || flows.length === 0) {
35663
36183
  return {
35664
36184
  profile,
@@ -35668,19 +36188,18 @@ class LibraryProfileCwe22PathGatePass {
35668
36188
  };
35669
36189
  }
35670
36190
  const droppedBySourceType = {};
35671
- const kept = [];
36191
+ let tagged = 0;
35672
36192
  for (const flow of flows) {
35673
36193
  if (flow.sink_type === "path_traversal" && CWE22_SPECULATIVE_SOURCE_TYPES.has(flow.source_type)) {
35674
36194
  droppedBySourceType[flow.source_type] = (droppedBySourceType[flow.source_type] ?? 0) + 1;
35675
- continue;
36195
+ const tags = flow.tags ?? [];
36196
+ if (!tags.includes(LIBRARY_API_SURFACE_TAG)) {
36197
+ flow.tags = [...tags, LIBRARY_API_SURFACE_TAG];
36198
+ }
36199
+ tagged++;
35676
36200
  }
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
36201
  }
36202
+ const dropped = tagged;
35684
36203
  return {
35685
36204
  profile,
35686
36205
  applied: true,
@@ -35690,7 +36209,7 @@ class LibraryProfileCwe22PathGatePass {
35690
36209
  }
35691
36210
  }
35692
36211
 
35693
- // node_modules/circle-ir/dist/analysis/passes/library-profile-xss-gate-pass.js
36212
+ // ../circle-ir/dist/analysis/passes/library-profile-xss-gate-pass.js
35694
36213
  var XSS_NON_HTML_OUTPUT_CLASSES = new Set([
35695
36214
  "StringBuilder",
35696
36215
  "StringBuffer",
@@ -35771,7 +36290,7 @@ class LibraryProfileXssGatePass {
35771
36290
  }
35772
36291
  }
35773
36292
 
35774
- // node_modules/circle-ir/dist/analysis/passes/taint-propagation-pass.js
36293
+ // ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
35775
36294
  class TaintPropagationPass {
35776
36295
  name = "taint-propagation";
35777
36296
  category = "security";
@@ -35826,6 +36345,63 @@ class TaintPropagationPass {
35826
36345
  flows.push(f);
35827
36346
  return true;
35828
36347
  };
36348
+ if (ctx.language === "python" && typeof ctx.code === "string") {
36349
+ const pyTainted = buildPythonTaintedVars(ctx.code);
36350
+ if (pyTainted.size > 0) {
36351
+ for (const v of findPythonTrustBoundaryViolations(ctx.code, pyTainted)) {
36352
+ const sink = sinks.find((sk) => sk.line === v.sinkLine && sk.type === "trust_boundary");
36353
+ if (!sink)
36354
+ continue;
36355
+ const src = sources.find((sc) => sc.line === v.sourceLine) ?? sources.filter((sc) => sc.line <= v.sinkLine).sort((a, b) => b.line - a.line)[0];
36356
+ if (!src)
36357
+ continue;
36358
+ if (constProp.unreachableLines.has(v.sinkLine))
36359
+ continue;
36360
+ pushIfNew({
36361
+ source_line: src.line,
36362
+ sink_line: v.sinkLine,
36363
+ source_type: src.type,
36364
+ sink_type: "trust_boundary",
36365
+ path: [
36366
+ { variable: src.variable ?? "session_key", line: src.line, type: "source" },
36367
+ { variable: src.variable ?? "session_key", line: v.sinkLine, type: "sink" }
36368
+ ],
36369
+ confidence: (src.confidence ?? 1) * (sink.confidence ?? 1),
36370
+ sanitized: false
36371
+ });
36372
+ }
36373
+ }
36374
+ }
36375
+ if (ctx.language === "python" && typeof ctx.code === "string") {
36376
+ const pyTainted = buildPythonTaintedVars(ctx.code);
36377
+ if (pyTainted.size > 0) {
36378
+ const xssSanitizerLines = sanitizers.filter((sa) => sa.sanitizes.includes("xss")).map((sa) => sa.line);
36379
+ for (const rx of findPythonReturnXSSSinks(ctx.code, pyTainted)) {
36380
+ if (constProp.unreachableLines.has(rx.sinkLine))
36381
+ continue;
36382
+ const sink = sinks.find((sk) => sk.line === rx.sinkLine && sk.type === "xss");
36383
+ if (!sink)
36384
+ continue;
36385
+ const src = sources.filter((sc) => sc.line <= rx.sinkLine).sort((a, b) => b.line - a.line)[0];
36386
+ if (!src)
36387
+ continue;
36388
+ if (xssSanitizerLines.some((l) => l >= src.line && l <= rx.sinkLine))
36389
+ continue;
36390
+ pushIfNew({
36391
+ source_line: src.line,
36392
+ sink_line: rx.sinkLine,
36393
+ source_type: src.type,
36394
+ sink_type: "xss",
36395
+ path: [
36396
+ { variable: src.variable ?? "response", line: src.line, type: "source" },
36397
+ { variable: src.variable ?? "response", line: rx.sinkLine, type: "sink" }
36398
+ ],
36399
+ confidence: (src.confidence ?? 1) * (sink.confidence ?? 1),
36400
+ sanitized: false
36401
+ });
36402
+ }
36403
+ }
36404
+ }
35829
36405
  const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines, types) ?? [];
35830
36406
  for (const f of arrayFlows) {
35831
36407
  pushIfNew(f);
@@ -36515,6 +37091,108 @@ function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
36515
37091
  }
36516
37092
  return false;
36517
37093
  }
37094
+ var MASKABLE_STRING_LANGUAGES = new Set(["python", "javascript", "typescript"]);
37095
+ function maskStringLiteralsForTokenScan(expr) {
37096
+ let out2 = "";
37097
+ let i2 = 0;
37098
+ const braceExprs = (body2) => {
37099
+ let acc = "";
37100
+ for (let j = 0;j < body2.length; ) {
37101
+ if (body2[j] === "{" && body2[j + 1] === "{") {
37102
+ j += 2;
37103
+ continue;
37104
+ }
37105
+ if (body2[j] === "}" && body2[j + 1] === "}") {
37106
+ j += 2;
37107
+ continue;
37108
+ }
37109
+ if (body2[j] === "{") {
37110
+ j++;
37111
+ let depth = 1;
37112
+ let inner = "";
37113
+ while (j < body2.length && depth > 0) {
37114
+ if (body2[j] === "{")
37115
+ depth++;
37116
+ else if (body2[j] === "}") {
37117
+ depth--;
37118
+ if (depth === 0)
37119
+ break;
37120
+ }
37121
+ inner += body2[j];
37122
+ j++;
37123
+ }
37124
+ j++;
37125
+ acc += " " + maskStringLiteralsForTokenScan(inner) + " ";
37126
+ continue;
37127
+ }
37128
+ j++;
37129
+ }
37130
+ return acc;
37131
+ };
37132
+ while (i2 < expr.length) {
37133
+ const ch = expr[i2];
37134
+ if (ch === "`") {
37135
+ i2++;
37136
+ let acc = "";
37137
+ while (i2 < expr.length && expr[i2] !== "`") {
37138
+ if (expr[i2] === "\\") {
37139
+ i2 += 2;
37140
+ continue;
37141
+ }
37142
+ if (expr[i2] === "$" && expr[i2 + 1] === "{") {
37143
+ i2 += 2;
37144
+ let depth = 1;
37145
+ let inner = "";
37146
+ while (i2 < expr.length && depth > 0) {
37147
+ if (expr[i2] === "{")
37148
+ depth++;
37149
+ else if (expr[i2] === "}") {
37150
+ depth--;
37151
+ if (depth === 0)
37152
+ break;
37153
+ }
37154
+ inner += expr[i2];
37155
+ i2++;
37156
+ }
37157
+ i2++;
37158
+ acc += " " + maskStringLiteralsForTokenScan(inner) + " ";
37159
+ continue;
37160
+ }
37161
+ i2++;
37162
+ }
37163
+ i2++;
37164
+ out2 += " " + acc + " ";
37165
+ continue;
37166
+ }
37167
+ if (ch === '"' || ch === "'") {
37168
+ const pm = /[A-Za-z_][A-Za-z0-9_]*$/.exec(out2);
37169
+ const prefix = pm ? pm[0] : "";
37170
+ const isPrefix = prefix.length > 0 && /^[rRbBuUfF]+$/.test(prefix);
37171
+ const isF = isPrefix && /[fF]/.test(prefix);
37172
+ if (isPrefix)
37173
+ out2 = out2.slice(0, out2.length - prefix.length) + " ";
37174
+ const triple = expr.startsWith(ch.repeat(3), i2);
37175
+ const delim = triple ? ch.repeat(3) : ch;
37176
+ i2 += delim.length;
37177
+ let body2 = "";
37178
+ while (i2 < expr.length && !expr.startsWith(delim, i2)) {
37179
+ if (expr[i2] === "\\") {
37180
+ body2 += " ";
37181
+ i2 += 2;
37182
+ continue;
37183
+ }
37184
+ body2 += expr[i2];
37185
+ i2++;
37186
+ }
37187
+ i2 += delim.length;
37188
+ out2 += isF ? " " + braceExprs(body2) + " " : " ";
37189
+ continue;
37190
+ }
37191
+ out2 += ch;
37192
+ i2++;
37193
+ }
37194
+ return out2;
37195
+ }
36518
37196
  function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
36519
37197
  const flows = [];
36520
37198
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
@@ -36904,7 +37582,8 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36904
37582
  const expr = arg.expression;
36905
37583
  if (!expr)
36906
37584
  continue;
36907
- const exprIdentMatches = expr.match(/[\p{L}\p{N}_]+/gu);
37585
+ const scannable = language !== undefined && MASKABLE_STRING_LANGUAGES.has(language) ? maskStringLiteralsForTokenScan(expr) : expr;
37586
+ const exprIdentMatches = scannable.match(/[\p{L}\p{N}_]+/gu);
36908
37587
  let candidates = complexSources.length > 0 ? complexSources.slice() : null;
36909
37588
  if (exprIdentMatches) {
36910
37589
  for (const id of new Set(exprIdentMatches)) {
@@ -37054,7 +37733,7 @@ function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
37054
37733
  return sources[0];
37055
37734
  }
37056
37735
 
37057
- // node_modules/circle-ir/dist/analysis/interprocedural.js
37736
+ // ../circle-ir/dist/analysis/interprocedural.js
37058
37737
  function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourcesOrSanitizers, sinksOrOptions, sanitizersArg, optionsArg = {}) {
37059
37738
  let graph;
37060
37739
  let sources;
@@ -37568,7 +38247,7 @@ function findTaintBridges2(result) {
37568
38247
  return bridges;
37569
38248
  }
37570
38249
 
37571
- // node_modules/circle-ir/dist/analysis/passes/interprocedural-pass.js
38250
+ // ../circle-ir/dist/analysis/passes/interprocedural-pass.js
37572
38251
  class InterproceduralPass {
37573
38252
  name = "interprocedural";
37574
38253
  category = "security";
@@ -37774,7 +38453,7 @@ class InterproceduralPass {
37774
38453
  }
37775
38454
  }
37776
38455
 
37777
- // node_modules/circle-ir/dist/analysis/passes/dead-code-pass.js
38456
+ // ../circle-ir/dist/analysis/passes/dead-code-pass.js
37778
38457
  class DeadCodePass {
37779
38458
  name = "dead-code";
37780
38459
  category = "reliability";
@@ -37850,7 +38529,7 @@ class DeadCodePass {
37850
38529
  }
37851
38530
  }
37852
38531
 
37853
- // node_modules/circle-ir/dist/analysis/passes/missing-await-pass.js
38532
+ // ../circle-ir/dist/analysis/passes/missing-await-pass.js
37854
38533
  var ASYNC_METHODS = new Set([
37855
38534
  "readFile",
37856
38535
  "writeFile",
@@ -37929,7 +38608,7 @@ class MissingAwaitPass {
37929
38608
  }
37930
38609
  }
37931
38610
 
37932
- // node_modules/circle-ir/dist/analysis/passes/n-plus-one-pass.js
38611
+ // ../circle-ir/dist/analysis/passes/n-plus-one-pass.js
37933
38612
  var HIGH_CONFIDENCE_DB_METHODS = new Set([
37934
38613
  "executeQuery",
37935
38614
  "executeUpdate",
@@ -38052,7 +38731,7 @@ class NPlusOnePass {
38052
38731
  }
38053
38732
  }
38054
38733
 
38055
- // node_modules/circle-ir/dist/analysis/passes/missing-public-doc-pass.js
38734
+ // ../circle-ir/dist/analysis/passes/missing-public-doc-pass.js
38056
38735
  var TEST_PATH_RE = /[/._](test|tests|spec|specs|__tests?__|__mocks?__)[/._]/i;
38057
38736
  var UTIL_DIR_RE = /[/](utils?|helpers?|internal|private|common|shared)[/]/i;
38058
38737
  function hasDocCommentBefore(lines, startLine) {
@@ -38159,7 +38838,7 @@ class MissingPublicDocPass {
38159
38838
  }
38160
38839
  }
38161
38840
 
38162
- // node_modules/circle-ir/dist/analysis/passes/todo-in-prod-pass.js
38841
+ // ../circle-ir/dist/analysis/passes/todo-in-prod-pass.js
38163
38842
  var TEST_PATH_RE2 = /[/._](test|tests|spec|specs|__tests?__|__mocks?__)[/._]/i;
38164
38843
  var MARKER_RE = /(?:\/\/|#|--|^\s*\*)\s*(TODO|FIXME|HACK|XXX)\b/i;
38165
38844
  function markerSeverity(marker) {
@@ -38205,7 +38884,7 @@ class TodoInProdPass {
38205
38884
  }
38206
38885
  }
38207
38886
 
38208
- // node_modules/circle-ir/dist/analysis/passes/string-concat-loop-pass.js
38887
+ // ../circle-ir/dist/analysis/passes/string-concat-loop-pass.js
38209
38888
  var CONCAT_RE = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\+=/;
38210
38889
  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
38890
  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 +38947,7 @@ class StringConcatLoopPass {
38268
38947
  }
38269
38948
  }
38270
38949
 
38271
- // node_modules/circle-ir/dist/analysis/passes/sync-io-async-pass.js
38950
+ // ../circle-ir/dist/analysis/passes/sync-io-async-pass.js
38272
38951
  var BLOCKING_METHODS = new Set([
38273
38952
  "sleep"
38274
38953
  ]);
@@ -38329,7 +39008,7 @@ class SyncIoAsyncPass {
38329
39008
  }
38330
39009
  }
38331
39010
 
38332
- // node_modules/circle-ir/dist/analysis/passes/unchecked-return-pass.js
39011
+ // ../circle-ir/dist/analysis/passes/unchecked-return-pass.js
38333
39012
  var MUST_CHECK_HIGH = new Set([
38334
39013
  "createNewFile",
38335
39014
  "mkdir",
@@ -38402,7 +39081,7 @@ class UncheckedReturnPass {
38402
39081
  }
38403
39082
  }
38404
39083
 
38405
- // node_modules/circle-ir/dist/analysis/passes/null-deref-pass.js
39084
+ // ../circle-ir/dist/analysis/passes/null-deref-pass.js
38406
39085
  var NULL_EXPR_RE = /^\s*(null|None|undefined)\s*$/;
38407
39086
  function escRe(s) {
38408
39087
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -38484,7 +39163,7 @@ class NullDerefPass {
38484
39163
  }
38485
39164
  }
38486
39165
 
38487
- // node_modules/circle-ir/dist/analysis/passes/resource-leak-pass.js
39166
+ // ../circle-ir/dist/analysis/passes/resource-leak-pass.js
38488
39167
  function escapeRegex2(s) {
38489
39168
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38490
39169
  }
@@ -38761,7 +39440,7 @@ class ResourceLeakPass {
38761
39440
  }
38762
39441
  }
38763
39442
 
38764
- // node_modules/circle-ir/dist/graph/scope-graph.js
39443
+ // ../circle-ir/dist/graph/scope-graph.js
38765
39444
  function hasDeclKeyword(lineText, language) {
38766
39445
  switch (language) {
38767
39446
  case "java":
@@ -38801,7 +39480,7 @@ class ScopeGraph {
38801
39480
  }
38802
39481
  }
38803
39482
 
38804
- // node_modules/circle-ir/dist/analysis/passes/variable-shadowing-pass.js
39483
+ // ../circle-ir/dist/analysis/passes/variable-shadowing-pass.js
38805
39484
  var SKIP_NAMES = new Set([
38806
39485
  "let",
38807
39486
  "const",
@@ -38959,7 +39638,7 @@ class VariableShadowingPass {
38959
39638
  }
38960
39639
  }
38961
39640
 
38962
- // node_modules/circle-ir/dist/analysis/passes/leaked-global-pass.js
39641
+ // ../circle-ir/dist/analysis/passes/leaked-global-pass.js
38963
39642
  var SKIP_NAMES2 = new Set([
38964
39643
  "_",
38965
39644
  "e",
@@ -39046,7 +39725,7 @@ class LeakedGlobalPass {
39046
39725
  }
39047
39726
  }
39048
39727
 
39049
- // node_modules/circle-ir/dist/analysis/passes/unused-variable-pass.js
39728
+ // ../circle-ir/dist/analysis/passes/unused-variable-pass.js
39050
39729
  var SKIP_NAMES3 = new Set([
39051
39730
  "_",
39052
39731
  "unused",
@@ -39191,7 +39870,7 @@ class UnusedVariablePass {
39191
39870
  }
39192
39871
  }
39193
39872
 
39194
- // node_modules/circle-ir/dist/analysis/passes/dependency-fan-out-pass.js
39873
+ // ../circle-ir/dist/analysis/passes/dependency-fan-out-pass.js
39195
39874
  var DEFAULT_THRESHOLD = 20;
39196
39875
 
39197
39876
  class DependencyFanOutPass {
@@ -39223,7 +39902,7 @@ class DependencyFanOutPass {
39223
39902
  }
39224
39903
  }
39225
39904
 
39226
- // node_modules/circle-ir/dist/analysis/passes/stale-doc-ref-pass.js
39905
+ // ../circle-ir/dist/analysis/passes/stale-doc-ref-pass.js
39227
39906
  var DOC_BLOCK_RE = /\/\*\*([\s\S]*?)\*\//g;
39228
39907
  var LINK_RE = /\{@link\s+([\w.#]+)/g;
39229
39908
  var SEE_RE = /@see\s+([\w.#]+)/g;
@@ -39302,7 +39981,7 @@ class StaleDocRefPass {
39302
39981
  }
39303
39982
  }
39304
39983
 
39305
- // node_modules/circle-ir/dist/analysis/passes/infinite-loop-pass.js
39984
+ // ../circle-ir/dist/analysis/passes/infinite-loop-pass.js
39306
39985
  var EXIT_KEYWORDS = /\b(return|throw|raise|break|System\.exit|process\.exit|os\._exit|exit!\()\b/;
39307
39986
  var ITERATOR_LOOP_PATTERNS = [
39308
39987
  /\bfor\s*\([^)]*\s+of\s+/,
@@ -39421,7 +40100,7 @@ class InfiniteLoopPass {
39421
40100
  }
39422
40101
  }
39423
40102
 
39424
- // node_modules/circle-ir/dist/analysis/passes/deep-inheritance-pass.js
40103
+ // ../circle-ir/dist/analysis/passes/deep-inheritance-pass.js
39425
40104
  var DEPTH_THRESHOLD = 5;
39426
40105
  var CYCLE_GUARD = 20;
39427
40106
 
@@ -39484,7 +40163,7 @@ class DeepInheritancePass {
39484
40163
  }
39485
40164
  }
39486
40165
 
39487
- // node_modules/circle-ir/dist/analysis/passes/redundant-loop-pass.js
40166
+ // ../circle-ir/dist/analysis/passes/redundant-loop-pass.js
39488
40167
  var LENGTH_PATTERN = /\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\.\s*(?:length|size\(\)|count\(\))/g;
39489
40168
  var LENGTH_PATTERN_METHODS = /\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\.\s*(?:size\(\)|count\(\))/g;
39490
40169
  var OBJECT_STATIC_PATTERN = /\bObject\s*\.\s*(?:keys|values|entries)\s*\(\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*\)/g;
@@ -39607,7 +40286,7 @@ class RedundantLoopPass {
39607
40286
  }
39608
40287
  }
39609
40288
 
39610
- // node_modules/circle-ir/dist/analysis/passes/unbounded-collection-pass.js
40289
+ // ../circle-ir/dist/analysis/passes/unbounded-collection-pass.js
39611
40290
  var GROW_METHODS = {
39612
40291
  java: new Set(["add", "put", "offer", "push", "addAll", "addFirst", "addLast", "enqueue", "insert"]),
39613
40292
  javascript: new Set(["push", "set", "add", "unshift", "append", "prepend"]),
@@ -39731,7 +40410,7 @@ class UnboundedCollectionPass {
39731
40410
  }
39732
40411
  }
39733
40412
 
39734
- // node_modules/circle-ir/dist/analysis/passes/serial-await-pass.js
40413
+ // ../circle-ir/dist/analysis/passes/serial-await-pass.js
39735
40414
  var AWAIT_ASSIGN_RE = /(?:const|let|var)?\s*(\w+)\s*=\s*await\s/;
39736
40415
  var AWAIT_RE = /\bawait\s/;
39737
40416
 
@@ -39827,7 +40506,7 @@ class SerialAwaitPass {
39827
40506
  }
39828
40507
  }
39829
40508
 
39830
- // node_modules/circle-ir/dist/analysis/passes/react-inline-jsx-pass.js
40509
+ // ../circle-ir/dist/analysis/passes/react-inline-jsx-pass.js
39831
40510
  var JSX_COMPONENT_RE = /<[A-Z][A-Za-z0-9]*/;
39832
40511
  var INLINE_OBJECT_RE = /\s([A-Za-z][A-Za-z0-9_]*)=\{\{/g;
39833
40512
  var INLINE_ARROW_RE = /\s([A-Za-z][A-Za-z0-9_]*)=\{(?:\(|[A-Za-z_$]).*?=>/g;
@@ -39931,7 +40610,7 @@ class ReactInlineJsxPass {
39931
40610
  }
39932
40611
  }
39933
40612
 
39934
- // node_modules/circle-ir/dist/graph/exception-flow-graph.js
40613
+ // ../circle-ir/dist/graph/exception-flow-graph.js
39935
40614
  class ExceptionFlowGraph2 {
39936
40615
  pairs;
39937
40616
  catchEntryIds;
@@ -39982,7 +40661,7 @@ class ExceptionFlowGraph2 {
39982
40661
  }
39983
40662
  }
39984
40663
 
39985
- // node_modules/circle-ir/dist/analysis/passes/swallowed-exception-pass.js
40664
+ // ../circle-ir/dist/analysis/passes/swallowed-exception-pass.js
39986
40665
  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
40666
 
39988
40667
  class SwallowedExceptionPass {
@@ -40080,7 +40759,7 @@ class SwallowedExceptionPass {
40080
40759
  }
40081
40760
  }
40082
40761
 
40083
- // node_modules/circle-ir/dist/analysis/passes/broad-catch-pass.js
40762
+ // ../circle-ir/dist/analysis/passes/broad-catch-pass.js
40084
40763
  var JAVA_BROAD_RE = /catch\s*\(\s*(Exception|Throwable|RuntimeException|Error)\s/;
40085
40764
  var PYTHON_BROAD_RE = /^\s*except\s*:|except\s+(Exception|BaseException)\b/;
40086
40765
 
@@ -40136,7 +40815,7 @@ class BroadCatchPass {
40136
40815
  }
40137
40816
  }
40138
40817
 
40139
- // node_modules/circle-ir/dist/analysis/passes/unhandled-exception-pass.js
40818
+ // ../circle-ir/dist/analysis/passes/unhandled-exception-pass.js
40140
40819
  var JS_THROW_RE = /^\s*throw\s+/;
40141
40820
  var PYTHON_RAISE_RE = /^\s*raise\b/;
40142
40821
  function isValidationThrow(lines, throwLine) {
@@ -40306,7 +40985,7 @@ class UnhandledExceptionPass {
40306
40985
  }
40307
40986
  }
40308
40987
 
40309
- // node_modules/circle-ir/dist/analysis/passes/double-close-pass.js
40988
+ // ../circle-ir/dist/analysis/passes/double-close-pass.js
40310
40989
  var RESOURCE_CTORS2 = new Set([
40311
40990
  "FileInputStream",
40312
40991
  "FileOutputStream",
@@ -40421,7 +41100,7 @@ class DoubleClosePass {
40421
41100
  }
40422
41101
  }
40423
41102
 
40424
- // node_modules/circle-ir/dist/analysis/passes/use-after-close-pass.js
41103
+ // ../circle-ir/dist/analysis/passes/use-after-close-pass.js
40425
41104
  var RESOURCE_CTORS3 = new Set([
40426
41105
  "FileInputStream",
40427
41106
  "FileOutputStream",
@@ -40529,7 +41208,7 @@ class UseAfterClosePass {
40529
41208
  }
40530
41209
  }
40531
41210
 
40532
- // node_modules/circle-ir/dist/graph/dominator-graph.js
41211
+ // ../circle-ir/dist/graph/dominator-graph.js
40533
41212
  function computeRPO(cfg, entryId) {
40534
41213
  const outgoing = new Map;
40535
41214
  for (const edge of cfg.edges) {
@@ -40694,7 +41373,7 @@ class DominatorGraph2 {
40694
41373
  }
40695
41374
  }
40696
41375
 
40697
- // node_modules/circle-ir/dist/analysis/passes/cleanup-verify-pass.js
41376
+ // ../circle-ir/dist/analysis/passes/cleanup-verify-pass.js
40698
41377
  var RESOURCE_CTORS4 = new Set([
40699
41378
  "FileInputStream",
40700
41379
  "FileOutputStream",
@@ -40819,7 +41498,7 @@ class CleanupVerifyPass {
40819
41498
  }
40820
41499
  }
40821
41500
 
40822
- // node_modules/circle-ir/dist/analysis/passes/missing-override-pass.js
41501
+ // ../circle-ir/dist/analysis/passes/missing-override-pass.js
40823
41502
  class MissingOverridePass {
40824
41503
  name = "missing-override";
40825
41504
  category = "maintainability";
@@ -40906,7 +41585,7 @@ class MissingOverridePass {
40906
41585
  }
40907
41586
  }
40908
41587
 
40909
- // node_modules/circle-ir/dist/analysis/passes/unused-interface-method-pass.js
41588
+ // ../circle-ir/dist/analysis/passes/unused-interface-method-pass.js
40910
41589
  class UnusedInterfaceMethodPass {
40911
41590
  name = "unused-interface-method";
40912
41591
  category = "maintainability";
@@ -40949,7 +41628,7 @@ class UnusedInterfaceMethodPass {
40949
41628
  }
40950
41629
  }
40951
41630
 
40952
- // node_modules/circle-ir/dist/analysis/passes/blocking-main-thread-pass.js
41631
+ // ../circle-ir/dist/analysis/passes/blocking-main-thread-pass.js
40953
41632
  var HTTP_DECORATORS = new Set([
40954
41633
  "Get",
40955
41634
  "Post",
@@ -40988,6 +41667,18 @@ var CRYPTO_BLOCKING_METHODS = new Set([
40988
41667
  "deriveKeySync"
40989
41668
  ]);
40990
41669
  var SYNC_SUFFIX_RE2 = /Sync$/;
41670
+ var ROUTER_METHODS = new Set([
41671
+ "get",
41672
+ "post",
41673
+ "put",
41674
+ "patch",
41675
+ "delete",
41676
+ "all",
41677
+ "options",
41678
+ "head",
41679
+ "use"
41680
+ ]);
41681
+ var FN_ARG_PARAMS_RE = /^(?:async\s+)?(?:function\s*[\w$]*\s*)?\(([^)]*)\)\s*(?:=>|\{)|^(?:async\s+)?([A-Za-z_$][\w$]*)\s*=>/;
40991
41682
 
40992
41683
  class BlockingMainThreadPass {
40993
41684
  name = "blocking-main-thread";
@@ -41010,8 +41701,10 @@ class BlockingMainThreadPass {
41010
41701
  }
41011
41702
  }
41012
41703
  }
41013
- if (handlerRanges.length === 0)
41704
+ const inlineHandlers = this.collectInlineHandlerNames(graph.ir.calls);
41705
+ if (handlerRanges.length === 0 && inlineHandlers.size === 0) {
41014
41706
  return { blockingInHandlers: [] };
41707
+ }
41015
41708
  const blockingInHandlers = [];
41016
41709
  for (const call of graph.ir.calls) {
41017
41710
  const name2 = call.method_name;
@@ -41020,7 +41713,8 @@ class BlockingMainThreadPass {
41020
41713
  if (!isCrypto && !isSyncSuffix)
41021
41714
  continue;
41022
41715
  const line = call.location.line;
41023
- const range = handlerRanges.find((r) => line >= r.start && line <= r.end);
41716
+ const enclosing = call.in_method ?? null;
41717
+ const range = handlerRanges.find((r) => line >= r.start && line <= r.end) ?? (enclosing && inlineHandlers.has(enclosing) ? { start: line, end: line, name: enclosing } : undefined);
41024
41718
  if (!range)
41025
41719
  continue;
41026
41720
  const reason = isCrypto ? "crypto" : "sync-suffix";
@@ -41042,6 +41736,24 @@ class BlockingMainThreadPass {
41042
41736
  }
41043
41737
  return { blockingInHandlers };
41044
41738
  }
41739
+ collectInlineHandlerNames(calls) {
41740
+ const names = new Set;
41741
+ for (const call of calls) {
41742
+ if (!ROUTER_METHODS.has(call.method_name))
41743
+ continue;
41744
+ for (const arg of call.arguments) {
41745
+ const match = FN_ARG_PARAMS_RE.exec((arg.expression ?? "").trim());
41746
+ if (!match)
41747
+ continue;
41748
+ const params = (match[1] ?? match[2] ?? "").split(",").map((p) => p.trim().toLowerCase()).filter((p) => p.length > 0);
41749
+ if (params.some((p) => HANDLER_PARAM_NAMES.has(p))) {
41750
+ names.add(`${call.method_name}_handler`);
41751
+ break;
41752
+ }
41753
+ }
41754
+ }
41755
+ return names;
41756
+ }
41045
41757
  isRequestHandler(method) {
41046
41758
  if (method.annotations.some((a) => HTTP_DECORATORS.has(a)))
41047
41759
  return true;
@@ -41052,7 +41764,7 @@ class BlockingMainThreadPass {
41052
41764
  }
41053
41765
  }
41054
41766
 
41055
- // node_modules/circle-ir/dist/analysis/passes/excessive-allocation-pass.js
41767
+ // ../circle-ir/dist/analysis/passes/excessive-allocation-pass.js
41056
41768
  var ALLOC_PATTERNS = {
41057
41769
  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
41770
  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 +41831,7 @@ class ExcessiveAllocationPass {
41119
41831
  }
41120
41832
  }
41121
41833
 
41122
- // node_modules/circle-ir/dist/analysis/passes/missing-stream-pass.js
41834
+ // ../circle-ir/dist/analysis/passes/missing-stream-pass.js
41123
41835
  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
41836
  var JS_STREAM_RE = /\.pipe\s*\(|\.on\s*\(\s*['"]data['"]|for\s+await\s*\(|\bcreateReadStream\b|\bstream\b/i;
41125
41837
  var JAVA_WHOLE_READ_RE = /\bFiles\.readAllBytes\s*\(|\bFiles\.readAllLines\s*\(|\bFiles\.readString\s*\(|\bnew\s+BufferedReader\s*\(|\bFileInputStream\b/;
@@ -41266,7 +41978,7 @@ class MissingStreamPass {
41266
41978
  }
41267
41979
  }
41268
41980
 
41269
- // node_modules/circle-ir/dist/analysis/passes/god-class-pass.js
41981
+ // ../circle-ir/dist/analysis/passes/god-class-pass.js
41270
41982
  var WMC_THRESHOLD = 47;
41271
41983
  var LCOM2_THRESHOLD = 0.8;
41272
41984
  var CBO_THRESHOLD = 14;
@@ -41432,7 +42144,7 @@ class GodClassPass {
41432
42144
  }
41433
42145
  }
41434
42146
 
41435
- // node_modules/circle-ir/dist/analysis/passes/naming-convention-pass.js
42147
+ // ../circle-ir/dist/analysis/passes/naming-convention-pass.js
41436
42148
  var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
41437
42149
  var CAMEL_CASE_RE = /^[a-z][a-zA-Z0-9]*$/;
41438
42150
  var SNAKE_CASE_RE = /^[a-z_][a-z0-9_]*$/;
@@ -41572,7 +42284,7 @@ class NamingConventionPass {
41572
42284
  }
41573
42285
  }
41574
42286
 
41575
- // node_modules/circle-ir/dist/analysis/passes/security-headers-pass.js
42287
+ // ../circle-ir/dist/analysis/passes/security-headers-pass.js
41576
42288
  var HEADER_WRITE_METHODS = new Set([
41577
42289
  "setHeader",
41578
42290
  "addHeader",
@@ -42033,7 +42745,7 @@ function mapReturnValueToCorsRule(returnValue) {
42033
42745
  };
42034
42746
  }
42035
42747
 
42036
- // node_modules/circle-ir/dist/analysis/passes/_fp-allowlists.js
42748
+ // ../circle-ir/dist/analysis/passes/_fp-allowlists.js
42037
42749
  var PEM_BODY_RE = /[A-Za-z0-9+/]{30,}/;
42038
42750
  function pemHasInlineBody(lines, hitLineIdx) {
42039
42751
  const end = Math.min(hitLineIdx + 5, lines.length);
@@ -42060,7 +42772,7 @@ function isProtocolMandatedCryptoFile(file, code) {
42060
42772
  return false;
42061
42773
  }
42062
42774
 
42063
- // node_modules/circle-ir/dist/analysis/passes/scan-secrets-pass.js
42775
+ // ../circle-ir/dist/analysis/passes/scan-secrets-pass.js
42064
42776
  function applyDemoDowngrade(demoPath, severity, level) {
42065
42777
  if (!demoPath)
42066
42778
  return { severity, level };
@@ -42638,7 +43350,7 @@ class ScanSecretsPass {
42638
43350
  }
42639
43351
  }
42640
43352
 
42641
- // node_modules/circle-ir/dist/analysis/passes/python-receiver-taint-format-pass.js
43353
+ // ../circle-ir/dist/analysis/passes/python-receiver-taint-format-pass.js
42642
43354
  class PythonReceiverTaintFormatPass {
42643
43355
  name = "python-receiver-taint-format";
42644
43356
  category = "security";
@@ -42690,7 +43402,7 @@ class PythonReceiverTaintFormatPass {
42690
43402
  }
42691
43403
  }
42692
43404
 
42693
- // node_modules/circle-ir/dist/analysis/passes/spring4shell-pass.js
43405
+ // ../circle-ir/dist/analysis/passes/spring4shell-pass.js
42694
43406
  var CONTROLLER_ANNOTATIONS = new Set([
42695
43407
  "Controller",
42696
43408
  "RestController",
@@ -42898,7 +43610,7 @@ function isPotentialPojo(type) {
42898
43610
  return first >= 65 && first <= 90;
42899
43611
  }
42900
43612
 
42901
- // node_modules/circle-ir/dist/analysis/passes/insecure-cookie-pass.js
43613
+ // ../circle-ir/dist/analysis/passes/insecure-cookie-pass.js
42902
43614
  var COOKIE_RESPONSE_RECEIVERS = new Set([
42903
43615
  "res",
42904
43616
  "response",
@@ -43134,7 +43846,7 @@ class InsecureCookiePass {
43134
43846
  }
43135
43847
  }
43136
43848
 
43137
- // node_modules/circle-ir/dist/analysis/passes/weak-hash-pass.js
43849
+ // ../circle-ir/dist/analysis/passes/weak-hash-pass.js
43138
43850
  var WEAK_HASH_NAMES = new Set([
43139
43851
  "md2",
43140
43852
  "md4",
@@ -43354,7 +44066,7 @@ class WeakHashPass {
43354
44066
  }
43355
44067
  }
43356
44068
 
43357
- // node_modules/circle-ir/dist/analysis/path-classification.js
44069
+ // ../circle-ir/dist/analysis/path-classification.js
43358
44070
  var TEST_PATH_PATTERNS = [
43359
44071
  /(^|\/)test\//i,
43360
44072
  /(^|\/)tests\//i,
@@ -43391,7 +44103,7 @@ function isTestPath(filepath) {
43391
44103
  return false;
43392
44104
  }
43393
44105
 
43394
- // node_modules/circle-ir/dist/analysis/passes/weak-crypto-pass.js
44106
+ // ../circle-ir/dist/analysis/passes/weak-crypto-pass.js
43395
44107
  var WEAK_CIPHER_BASES = new Set([
43396
44108
  "des",
43397
44109
  "3des",
@@ -43896,7 +44608,7 @@ class WeakCryptoPass {
43896
44608
  }
43897
44609
  }
43898
44610
 
43899
- // node_modules/circle-ir/dist/analysis/passes/weak-random-pass.js
44611
+ // ../circle-ir/dist/analysis/passes/weak-random-pass.js
43900
44612
  var JAVA_RANDOM_METHODS = new Set([
43901
44613
  "nextInt",
43902
44614
  "nextLong",
@@ -44069,7 +44781,7 @@ class WeakRandomPass {
44069
44781
  }
44070
44782
  }
44071
44783
 
44072
- // node_modules/circle-ir/dist/analysis/passes/_credential-helpers.js
44784
+ // ../circle-ir/dist/analysis/passes/_credential-helpers.js
44073
44785
  var CRED_KEYWORD_RE2 = /(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key|credential)/i;
44074
44786
  function isCredentialIdentifier(name2) {
44075
44787
  if (!name2)
@@ -44162,7 +44874,7 @@ function priorHashOf(varName, priorCalls) {
44162
44874
  return false;
44163
44875
  }
44164
44876
 
44165
- // node_modules/circle-ir/dist/analysis/passes/weak-password-hash-pass.js
44877
+ // ../circle-ir/dist/analysis/passes/weak-password-hash-pass.js
44166
44878
  var FAST_HASH_NAMES = new Set([
44167
44879
  "sha224",
44168
44880
  "sha-224",
@@ -44321,7 +45033,7 @@ class WeakPasswordHashPass {
44321
45033
  }
44322
45034
  }
44323
45035
 
44324
- // node_modules/circle-ir/dist/analysis/passes/weak-password-encoding-pass.js
45036
+ // ../circle-ir/dist/analysis/passes/weak-password-encoding-pass.js
44325
45037
  function isBasicAuthContext(call, code) {
44326
45038
  const line = call.location.line;
44327
45039
  if (line < 1)
@@ -44433,7 +45145,7 @@ class WeakPasswordEncodingPass {
44433
45145
  }
44434
45146
  }
44435
45147
 
44436
- // node_modules/circle-ir/dist/analysis/passes/info-disclosure-stacktrace-pass.js
45148
+ // ../circle-ir/dist/analysis/passes/info-disclosure-stacktrace-pass.js
44437
45149
  var RESPONSE_RECEIVER_RE = /^(res|response|w|writer|ctx|c)$/i;
44438
45150
  var LOGGER_RECEIVER_RE = /^(log|logger|slog|console|pino|winston|sentry)$/i;
44439
45151
  var RESPONSE_SEND_METHODS = new Set([
@@ -44619,7 +45331,7 @@ class InfoDisclosureStacktracePass {
44619
45331
  }
44620
45332
  }
44621
45333
 
44622
- // node_modules/circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
45334
+ // ../circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
44623
45335
  var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
44624
45336
  var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
44625
45337
  function lineWindow(lines, startLine, endLine) {
@@ -44770,7 +45482,7 @@ class UnrestrictedFileUploadPass {
44770
45482
  }
44771
45483
  }
44772
45484
 
44773
- // node_modules/circle-ir/dist/analysis/passes/missing-sanitizer-gate-pass.js
45485
+ // ../circle-ir/dist/analysis/passes/missing-sanitizer-gate-pass.js
44774
45486
  var HTML_OUTPUT_SINKS = new Set([
44775
45487
  "addAttribute",
44776
45488
  "setAttribute",
@@ -44918,7 +45630,7 @@ function methodAcceptsHtmlShapedParam(method) {
44918
45630
  return false;
44919
45631
  }
44920
45632
 
44921
- // node_modules/circle-ir/dist/analysis/passes/insecure-deserialization-config-pass.js
45633
+ // ../circle-ir/dist/analysis/passes/insecure-deserialization-config-pass.js
44922
45634
  var ANY_TYPE_PERMISSION_RE = /\bAnyTypePermission\b/;
44923
45635
 
44924
45636
  class InsecureDeserializationConfigPass {
@@ -44996,7 +45708,7 @@ class InsecureDeserializationConfigPass {
44996
45708
  }
44997
45709
  var INSECURE_TYPE_NAME_HANDLING_RE = /\bTypeNameHandling\s*=\s*(?:Newtonsoft\.Json\.)?TypeNameHandling\.(All|Auto|Objects|Arrays)\b/;
44998
45710
 
44999
- // node_modules/circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
45711
+ // ../circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
45000
45712
  function isWriteStorageCall(call, language) {
45001
45713
  const method = call.method_name ?? "";
45002
45714
  const receiver = call.receiver ?? "";
@@ -45107,7 +45819,7 @@ class PlaintextPasswordStoragePass {
45107
45819
  }
45108
45820
  }
45109
45821
 
45110
- // node_modules/circle-ir/dist/analysis/passes/cleartext-credential-transport-pass.js
45822
+ // ../circle-ir/dist/analysis/passes/cleartext-credential-transport-pass.js
45111
45823
  var LOCALHOST_RE = /^(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::\d+)?$/i;
45112
45824
  function isInsecureHttpUrl(urlLiteral) {
45113
45825
  if (!urlLiteral)
@@ -45235,7 +45947,7 @@ class CleartextCredentialTransportPass {
45235
45947
  }
45236
45948
  }
45237
45949
 
45238
- // node_modules/circle-ir/dist/analysis/passes/tls-verify-disabled-pass.js
45950
+ // ../circle-ir/dist/analysis/passes/tls-verify-disabled-pass.js
45239
45951
  var PY_HTTP_METHODS = new Set([
45240
45952
  "get",
45241
45953
  "post",
@@ -45448,7 +46160,7 @@ class TlsVerifyDisabledPass {
45448
46160
  }
45449
46161
  }
45450
46162
 
45451
- // node_modules/circle-ir/dist/analysis/passes/module-side-effect-pass.js
46163
+ // ../circle-ir/dist/analysis/passes/module-side-effect-pass.js
45452
46164
  var JS_EXEC_METHODS = new Set([
45453
46165
  "exec",
45454
46166
  "spawn",
@@ -45679,7 +46391,7 @@ class ModuleSideEffectPass {
45679
46391
  }
45680
46392
  }
45681
46393
 
45682
- // node_modules/circle-ir/dist/analysis/passes/cache-no-vary-pass.js
46394
+ // ../circle-ir/dist/analysis/passes/cache-no-vary-pass.js
45683
46395
  function isSharedCacheable(value) {
45684
46396
  const v = value.toLowerCase();
45685
46397
  if (/\b(private|no-store|no-cache)\b/.test(v))
@@ -45966,7 +46678,7 @@ class CacheNoVaryPass {
45966
46678
  }
45967
46679
  }
45968
46680
 
45969
- // node_modules/circle-ir/dist/analysis/passes/jwt-verify-disabled-pass.js
46681
+ // ../circle-ir/dist/analysis/passes/jwt-verify-disabled-pass.js
45970
46682
  var PY_VERIFY_SIGNATURE_FALSE_RE = /["']verify_signature["']\s*:\s*False\b/;
45971
46683
  var PY_VERIFY_KW_FALSE_RE = /\bverify\s*=\s*False\b/;
45972
46684
  var PY_ALG_NONE_RE = /\balgorithms\s*=\s*[\[\(]\s*["']none["']/i;
@@ -46091,7 +46803,7 @@ class JwtVerifyDisabledPass {
46091
46803
  }
46092
46804
  }
46093
46805
 
46094
- // node_modules/circle-ir/dist/analysis/passes/csrf-protection-disabled-pass.js
46806
+ // ../circle-ir/dist/analysis/passes/csrf-protection-disabled-pass.js
46095
46807
  var JAVA_CSRF_DISABLE_RE = /\.csrf\s*\([^)]*\)\s*\.\s*disable\b/;
46096
46808
  var JAVA_CSRF_LAMBDA_DISABLE_RE = /\bcsrf\s*\(\s*\w+\s*->\s*\w+\s*\.\s*disable\s*\(/;
46097
46809
  var JAVA_CSRF_METHODREF_RE = /\bcsrf\s*\(\s*[\w.]+::disable\s*\)/;
@@ -46227,7 +46939,7 @@ class CsrfProtectionDisabledPass {
46227
46939
  }
46228
46940
  }
46229
46941
 
46230
- // node_modules/circle-ir/dist/analysis/passes/xml-entity-expansion-pass.js
46942
+ // ../circle-ir/dist/analysis/passes/xml-entity-expansion-pass.js
46231
46943
  var JAVA_FACTORIES = new Set([
46232
46944
  "SAXParserFactory",
46233
46945
  "DocumentBuilderFactory",
@@ -46367,7 +47079,7 @@ class XmlEntityExpansionPass {
46367
47079
  }
46368
47080
  }
46369
47081
 
46370
- // node_modules/circle-ir/dist/analysis/passes/mass-assignment-pass.js
47082
+ // ../circle-ir/dist/analysis/passes/mass-assignment-pass.js
46371
47083
  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
47084
  var JS_OBJECT_SPREAD_RE = /\{\s*\.\.\.\s*(?:req|request|ctx|context)(?:\.request)?\s*\.\s*(?:body|query|params|form)\b/;
46373
47085
 
@@ -46451,7 +47163,7 @@ class MassAssignmentPass {
46451
47163
  }
46452
47164
  }
46453
47165
 
46454
- // node_modules/circle-ir/dist/graph/import-graph.js
47166
+ // ../circle-ir/dist/graph/import-graph.js
46455
47167
  function dirname(filePath) {
46456
47168
  const idx = filePath.lastIndexOf("/");
46457
47169
  return idx >= 0 ? filePath.slice(0, idx) : "";
@@ -46584,7 +47296,7 @@ class ImportGraph2 {
46584
47296
  }
46585
47297
  }
46586
47298
 
46587
- // node_modules/circle-ir/dist/analysis/passes/circular-dependency-pass.js
47299
+ // ../circle-ir/dist/analysis/passes/circular-dependency-pass.js
46588
47300
  class CircularDependencyPass {
46589
47301
  run(_projectGraph, importGraph) {
46590
47302
  const findings = [];
@@ -46611,7 +47323,7 @@ class CircularDependencyPass {
46611
47323
  }
46612
47324
  }
46613
47325
 
46614
- // node_modules/circle-ir/dist/analysis/passes/orphan-module-pass.js
47326
+ // ../circle-ir/dist/analysis/passes/orphan-module-pass.js
46615
47327
  class OrphanModulePass {
46616
47328
  run(_projectGraph, importGraph) {
46617
47329
  const findings = [];
@@ -46635,7 +47347,7 @@ class OrphanModulePass {
46635
47347
  }
46636
47348
  }
46637
47349
 
46638
- // node_modules/circle-ir/dist/analysis/metrics/passes/size-metrics-pass.js
47350
+ // ../circle-ir/dist/analysis/metrics/passes/size-metrics-pass.js
46639
47351
  class SizeMetricsPass {
46640
47352
  name = "size-metrics";
46641
47353
  run(ctx) {
@@ -46691,7 +47403,7 @@ class SizeMetricsPass {
46691
47403
  }
46692
47404
  }
46693
47405
 
46694
- // node_modules/circle-ir/dist/analysis/metrics/passes/complexity-metrics-pass.js
47406
+ // ../circle-ir/dist/analysis/metrics/passes/complexity-metrics-pass.js
46695
47407
  class ComplexityMetricsPass {
46696
47408
  name = "complexity-metrics";
46697
47409
  run(ctx) {
@@ -46755,7 +47467,7 @@ class ComplexityMetricsPass {
46755
47467
  }
46756
47468
  }
46757
47469
 
46758
- // node_modules/circle-ir/dist/analysis/metrics/passes/halstead-metrics-pass.js
47470
+ // ../circle-ir/dist/analysis/metrics/passes/halstead-metrics-pass.js
46759
47471
  var KEYWORDS = new Set([
46760
47472
  "if",
46761
47473
  "else",
@@ -46910,7 +47622,7 @@ class HalsteadMetricsPass {
46910
47622
  }
46911
47623
  }
46912
47624
 
46913
- // node_modules/circle-ir/dist/analysis/metrics/passes/data-flow-metrics-pass.js
47625
+ // ../circle-ir/dist/analysis/metrics/passes/data-flow-metrics-pass.js
46914
47626
  class DataFlowMetricsPass {
46915
47627
  name = "data-flow-metrics";
46916
47628
  run(ctx) {
@@ -46928,7 +47640,7 @@ class DataFlowMetricsPass {
46928
47640
  }
46929
47641
  }
46930
47642
 
46931
- // node_modules/circle-ir/dist/analysis/metrics/passes/coupling-metrics-pass.js
47643
+ // ../circle-ir/dist/analysis/metrics/passes/coupling-metrics-pass.js
46932
47644
  class CouplingMetricsPass {
46933
47645
  name = "coupling-metrics";
46934
47646
  run(ctx) {
@@ -47006,7 +47718,7 @@ class CouplingMetricsPass {
47006
47718
  }
47007
47719
  }
47008
47720
 
47009
- // node_modules/circle-ir/dist/analysis/metrics/passes/inheritance-metrics-pass.js
47721
+ // ../circle-ir/dist/analysis/metrics/passes/inheritance-metrics-pass.js
47010
47722
  class InheritanceMetricsPass {
47011
47723
  name = "inheritance-metrics";
47012
47724
  run(ctx) {
@@ -47066,7 +47778,7 @@ class InheritanceMetricsPass {
47066
47778
  }
47067
47779
  }
47068
47780
 
47069
- // node_modules/circle-ir/dist/analysis/metrics/passes/cohesion-metrics-pass.js
47781
+ // ../circle-ir/dist/analysis/metrics/passes/cohesion-metrics-pass.js
47070
47782
  class CohesionMetricsPass {
47071
47783
  name = "cohesion-metrics";
47072
47784
  run(ctx) {
@@ -47149,7 +47861,7 @@ class CohesionMetricsPass {
47149
47861
  }
47150
47862
  }
47151
47863
 
47152
- // node_modules/circle-ir/dist/analysis/metrics/passes/documentation-metrics-pass.js
47864
+ // ../circle-ir/dist/analysis/metrics/passes/documentation-metrics-pass.js
47153
47865
  class DocumentationMetricsPass {
47154
47866
  name = "documentation-metrics";
47155
47867
  run(ctx) {
@@ -47202,7 +47914,7 @@ class DocumentationMetricsPass {
47202
47914
  }
47203
47915
  }
47204
47916
 
47205
- // node_modules/circle-ir/dist/analysis/metrics/passes/composite-metrics-pass.js
47917
+ // ../circle-ir/dist/analysis/metrics/passes/composite-metrics-pass.js
47206
47918
  class CompositeMetricsPass {
47207
47919
  name = "composite-metrics";
47208
47920
  run(ctx) {
@@ -47261,7 +47973,7 @@ class CompositeMetricsPass {
47261
47973
  }
47262
47974
  }
47263
47975
 
47264
- // node_modules/circle-ir/dist/analysis/metrics/metric-runner.js
47976
+ // ../circle-ir/dist/analysis/metrics/metric-runner.js
47265
47977
  class MetricRunner {
47266
47978
  passes = [
47267
47979
  new SizeMetricsPass,
@@ -47283,7 +47995,7 @@ class MetricRunner {
47283
47995
  return accumulated;
47284
47996
  }
47285
47997
  }
47286
- // node_modules/circle-ir/dist/analyzer.js
47998
+ // ../circle-ir/dist/analyzer.js
47287
47999
  var initialized = false;
47288
48000
  async function initAnalyzer(options = {}) {
47289
48001
  if (initialized)
@@ -47599,8 +48311,6 @@ async function analyze(code, filePath, language, options = {}) {
47599
48311
  pipeline.add(new LanguageSourcesPass);
47600
48312
  if (!disabledPasses.has("source-semantics"))
47601
48313
  pipeline.add(new SourceSemanticsPass);
47602
- if (!disabledPasses.has("library-profile-source-gate"))
47603
- pipeline.add(new LibraryProfileSourceGatePass);
47604
48314
  if (!disabledPasses.has("mybatis-annotation-sql-sink"))
47605
48315
  pipeline.add(new MyBatisAnnotationSqlSinkPass);
47606
48316
  pipeline.add(new SinkFilterPass);
@@ -47617,6 +48327,8 @@ async function analyze(code, filePath, language, options = {}) {
47617
48327
  pipeline.add(new LibraryProfileSinkGatePass);
47618
48328
  if (!disabledPasses.has("library-profile-xss-gate"))
47619
48329
  pipeline.add(new LibraryProfileXssGatePass);
48330
+ if (!disabledPasses.has("library-profile-source-gate"))
48331
+ pipeline.add(new LibraryProfileSourceGatePass);
47620
48332
  pipeline.add(new TaintPropagationPass);
47621
48333
  pipeline.add(new InterproceduralPass({
47622
48334
  enableEntryPointGate: options.enableEntryPointGate ?? true
@@ -47880,7 +48592,19 @@ async function analyzeProject(files, options = {}) {
47880
48592
  const fileAnalyses = [];
47881
48593
  const projectGraph = new ProjectGraph;
47882
48594
  const sourceLinesByFile = new Map;
48595
+ const perFileBudgetMs = options.perFileBudgetMs ?? 0;
48596
+ const perFileStart = Date.now();
48597
+ let perFileBudgetExceeded = false;
47883
48598
  for (const { code, filePath, language } of files) {
48599
+ if (perFileBudgetMs > 0 && Date.now() - perFileStart > perFileBudgetMs) {
48600
+ perFileBudgetExceeded = true;
48601
+ logger.warn("analyzeProject: per-file budget exceeded, skipping remaining files", {
48602
+ analysed: fileAnalyses.length,
48603
+ total: files.length,
48604
+ budgetMs: perFileBudgetMs
48605
+ });
48606
+ break;
48607
+ }
47884
48608
  const ir = await analyze(code, filePath, language, options);
47885
48609
  fileAnalyses.push({ file: filePath, analysis: ir });
47886
48610
  projectGraph.addFile(filePath, new CodeGraph(ir));
@@ -47936,6 +48660,9 @@ async function analyzeProject(files, options = {}) {
47936
48660
  if (crossFileResult.budgetExceeded) {
47937
48661
  projectAnalysis.cross_file_budget_exceeded = true;
47938
48662
  }
48663
+ if (perFileBudgetExceeded) {
48664
+ projectAnalysis.per_file_budget_exceeded = true;
48665
+ }
47939
48666
  return projectAnalysis;
47940
48667
  }
47941
48668
  function deriveProjectName(paths) {
@@ -47955,7 +48682,7 @@ function deriveProjectRoot(paths) {
47955
48682
  }
47956
48683
  return common.join("/") || "/";
47957
48684
  }
47958
- // node_modules/circle-ir/dist/analysis/sbom.js
48685
+ // ../circle-ir/dist/analysis/sbom.js
47959
48686
  function purlVersion(spec) {
47960
48687
  const stripped = spec.trim().replace(/^[\s^~=<>]+/, "");
47961
48688
  if (/^v?[0-9][\w.\-+]*$/.test(stripped))
@@ -49011,7 +49738,7 @@ var colors = {
49011
49738
  };
49012
49739
 
49013
49740
  // src/version.ts
49014
- var version = "4.9.17";
49741
+ var version = "4.9.20";
49015
49742
 
49016
49743
  // src/formatters.ts
49017
49744
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -49564,8 +50291,8 @@ function generateSarifResults(results, crossFileData) {
49564
50291
 
49565
50292
  // src/build-info.ts
49566
50293
  var buildInfo = {
49567
- gitSha: "2910c91",
49568
- builtAt: "2026-09-19T09:22:05.775Z"
50294
+ gitSha: "eb03cc5",
50295
+ builtAt: "2026-09-19T19:54:44.551Z"
49569
50296
  };
49570
50297
 
49571
50298
  // src/utils/args.ts