circle-ir 3.151.0 → 3.153.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11286,11 +11286,11 @@ var DEFAULT_SINKS = [
11286
11286
  { method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11287
11287
  { method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11288
11288
  { method: "FileWriter", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11289
- // ClassLoader/Class resource loading (can be abused for path traversal)
11290
- { method: "getResource", class: "ClassLoader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11291
- { method: "getResourceAsStream", class: "ClassLoader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11292
- { method: "getResource", class: "Class", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11293
- { method: "getResourceAsStream", class: "Class", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11289
+ // NOTE: ClassLoader.getResource / Class.getResource were removed in
11290
+ // 3.153.0 (#233). Classpath resource resolution cannot escape the
11291
+ // classpath root via `../` (JAR entries are opaque). If reintroduced,
11292
+ // the correct CWE is CWE-829 (untrusted-classpath-resource), not
11293
+ // CWE-22 path traversal.
11294
11294
  // Paths.get can be used for path traversal
11295
11295
  { method: "get", class: "Paths", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11296
11296
  { method: "of", class: "Path", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -11793,6 +11793,15 @@ var DEFAULT_SINKS = [
11793
11793
  { method: "parseObject", class: "JSON", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
11794
11794
  { method: "parseObject", class: "JSONObject", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
11795
11795
  { method: "fromJson", class: "Gson", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0], safe_if_class_literal_at: 1 },
11796
+ // Jackson ObjectReader — pre-configured reader; typed 2-arg overload is safe
11797
+ // when arg[1] is a class literal or TypeReference<>()/TypeToken<>() (handled
11798
+ // by argIsClassLiteral extension). Added 3.153.0 (cognium-dev #233).
11799
+ { method: "readValue", class: "ObjectReader", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
11800
+ // Jackson ObjectMapper.convertValue — same class-literal shape as readValue.
11801
+ { method: "convertValue", class: "ObjectMapper", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0], safe_if_class_literal_at: 1 },
11802
+ // Kryo.readObject(input, User.class) — typed form is safe; polymorphic form
11803
+ // (Kryo.readClassAndObject or non-literal type) remains a sink.
11804
+ { method: "readObject", class: "Kryo", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
11796
11805
  // XMLDecoder
11797
11806
  { method: "readObject", class: "XMLDecoder", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [] },
11798
11807
  // Java serialization constructors
@@ -13652,7 +13661,17 @@ function argIsClassLiteral(call, position) {
13652
13661
  if (!arg) return false;
13653
13662
  const expr = (arg.literal ?? arg.expression ?? "").trim();
13654
13663
  if (!expr) return false;
13655
- return CLASS_LITERAL_RE.test(expr);
13664
+ if (CLASS_LITERAL_RE.test(expr)) return true;
13665
+ return TYPE_TOKEN_RE.test(expr);
13666
+ }
13667
+ var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
13668
+ function argIsStringLiteral(call, position) {
13669
+ const arg = call.arguments.find((a) => a.position === position);
13670
+ if (!arg) return false;
13671
+ if (arg.literal !== void 0 && arg.literal !== null && arg.literal !== "") return true;
13672
+ const expr = (arg.expression ?? "").trim();
13673
+ if (!expr) return false;
13674
+ return expr.startsWith('"') && expr.endsWith('"') && expr.length >= 2 || expr.startsWith("'") && expr.endsWith("'") && expr.length >= 2 || expr.startsWith('"""') && expr.endsWith('"""');
13656
13675
  }
13657
13676
  var CWE_78_RECEIVER_ALLOWLIST = /* @__PURE__ */ new Set([
13658
13677
  // java.lang.*
@@ -13827,6 +13846,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
13827
13846
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
13828
13847
  continue;
13829
13848
  }
13849
+ if (pattern.safe_if_string_literal_at !== void 0 && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
13850
+ continue;
13851
+ }
13830
13852
  if (pattern.type === "command_injection") {
13831
13853
  if (call.is_constructor) {
13832
13854
  if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
@@ -19553,6 +19575,238 @@ function applyLibraryApiSurfaceDowngrade(findings) {
19553
19575
  });
19554
19576
  }
19555
19577
 
19578
+ // src/analysis/entry-point-detection.ts
19579
+ var TIER_1_METHOD_ANNOTATIONS = /* @__PURE__ */ new Set([
19580
+ // Spring MVC
19581
+ "RequestMapping",
19582
+ "GetMapping",
19583
+ "PostMapping",
19584
+ "PutMapping",
19585
+ "DeleteMapping",
19586
+ "PatchMapping",
19587
+ // Spring messaging / WebSocket
19588
+ "MessageMapping",
19589
+ "SubscribeMapping",
19590
+ // Spring messaging listeners
19591
+ "KafkaListener",
19592
+ "KafkaHandler",
19593
+ "RabbitListener",
19594
+ "RabbitHandler",
19595
+ "JmsListener",
19596
+ "StreamListener",
19597
+ // Spring Cloud AWS
19598
+ "SqsListener",
19599
+ "SqsHandler",
19600
+ // Spring application events (borderline — inclusive in ship 1)
19601
+ "EventListener",
19602
+ // CRON / scheduled
19603
+ "Scheduled",
19604
+ // JAX-RS
19605
+ "Path",
19606
+ "GET",
19607
+ "POST",
19608
+ "PUT",
19609
+ "DELETE",
19610
+ "PATCH",
19611
+ "HEAD",
19612
+ "OPTIONS",
19613
+ // Jenkins Stapler form-binding (#224 — CVE-2022-20617 docker-commons).
19614
+ // `@DataBoundConstructor` / `@DataBoundSetter` mark the trust boundary
19615
+ // between Jenkins UI (or `config.xml` unmarshal) and the plugin: the
19616
+ // Stapler runtime invokes the annotated constructor / setter with
19617
+ // user-supplied strings from a submitted form or a persisted job
19618
+ // config. Every parameter is a user-supplied taint source.
19619
+ "DataBoundConstructor",
19620
+ "DataBoundSetter"
19621
+ ]);
19622
+ var TIER_1_CLASS_ANNOTATIONS = /* @__PURE__ */ new Set([
19623
+ // Spring MVC
19624
+ "RestController",
19625
+ "Controller",
19626
+ // Spring stereotype beans (#136 — see header)
19627
+ "Service",
19628
+ "Repository",
19629
+ "Component",
19630
+ // JAX-RS resource class
19631
+ "Path",
19632
+ // Servlet 3.0 annotation-based servlet
19633
+ "WebServlet",
19634
+ // JSR-356 WebSocket endpoint
19635
+ "ServerEndpoint",
19636
+ // Declarative HTTP client (borderline — inclusive in ship 1; contract-defined
19637
+ // parameter shapes are downstream-relevant trust boundaries)
19638
+ "FeignClient"
19639
+ ]);
19640
+ var TIER_1_BY_SUPERTYPE = /* @__PURE__ */ new Map([
19641
+ // Servlet API
19642
+ ["HttpServlet", /* @__PURE__ */ new Set(["doGet", "doPost", "doPut", "doDelete", "doHead", "doOptions", "doTrace", "service"])],
19643
+ ["GenericServlet", /* @__PURE__ */ new Set(["service"])],
19644
+ ["Filter", /* @__PURE__ */ new Set(["doFilter"])],
19645
+ // Spring web
19646
+ ["HandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion"])],
19647
+ ["AsyncHandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion", "afterConcurrentHandlingStarted"])],
19648
+ // Spring boot CLI entry points
19649
+ ["CommandLineRunner", /* @__PURE__ */ new Set(["run"])],
19650
+ ["ApplicationRunner", /* @__PURE__ */ new Set(["run"])],
19651
+ // Netty channel handlers (#154 — CVE-2022-26884 dolphinscheduler).
19652
+ // `SimpleChannelInboundHandler<T>.channelRead0(ctx, msg)` is the
19653
+ // standard typed read entry; `ChannelInboundHandler.channelRead(ctx,
19654
+ // Object)` is the untyped base. `ChannelInboundHandlerAdapter` and
19655
+ // `ChannelDuplexHandler` are the most-common adapter classes user
19656
+ // handlers extend. `NettyRequestProcessor` is the dolphinscheduler-
19657
+ // specific (and other Netty-RPC projects') wire-message processor
19658
+ // surface — `process(channel, command)` is the network entry point.
19659
+ ["SimpleChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead0", "messageReceived"])],
19660
+ ["ChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
19661
+ ["ChannelInboundHandlerAdapter", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
19662
+ ["ChannelDuplexHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
19663
+ ["NettyRequestProcessor", /* @__PURE__ */ new Set(["process"])],
19664
+ // XStream deserialization converters (#224 — CVE-2020-26217,
19665
+ // CVE-2021-21345). The xstream deserializer invokes `unmarshal`
19666
+ // with attacker-controlled `HierarchicalStreamReader` state whenever
19667
+ // untrusted XML reaches `XStream.fromXML`. Each converter is a
19668
+ // deserialization gadget surface — its `unmarshal` parameters are
19669
+ // network-facing taint sources even though the enclosing class
19670
+ // carries no framework annotation. `marshal` is included for the
19671
+ // symmetric round-trip surface but is rarely exploitable on its
19672
+ // own; the fixup targets `unmarshal` primarily.
19673
+ //
19674
+ // `SingleValueConverter` is the string-form variant (fromString
19675
+ // parses an attacker string; toString is the sink half of the
19676
+ // round-trip). `ConverterMatcher` is the shared base and some
19677
+ // downstream projects subclass it directly.
19678
+ ["Converter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
19679
+ ["SingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
19680
+ ["ConverterMatcher", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
19681
+ // XStream abstract base classes — direct-parent `extends` match
19682
+ // covers the common shape where user converters subclass a base
19683
+ // rather than implementing `Converter` directly.
19684
+ ["AbstractReflectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal", "doMarshal", "doUnmarshal"])],
19685
+ ["AbstractSingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
19686
+ ["AbstractCollectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])]
19687
+ ]);
19688
+ var TIER_3_CLASS_SUFFIXES = [
19689
+ "Util",
19690
+ "Utils",
19691
+ "Helper",
19692
+ "Helpers"
19693
+ ];
19694
+ var TIER_3_PACKAGE_FRAGMENTS = [
19695
+ ".template.",
19696
+ ".templates.",
19697
+ ".engine.",
19698
+ ".engines."
19699
+ ];
19700
+ var TIER_3_JDK_FACADE_INTERFACES = /* @__PURE__ */ new Set([
19701
+ // Collection root + common containers
19702
+ "Collection",
19703
+ "List",
19704
+ "Set",
19705
+ "Map",
19706
+ "Queue",
19707
+ "Deque",
19708
+ "SortedSet",
19709
+ "SortedMap",
19710
+ "NavigableSet",
19711
+ "NavigableMap",
19712
+ // Iteration / ordering / equality contracts
19713
+ "Iterator",
19714
+ "Iterable",
19715
+ "ListIterator",
19716
+ "Comparator",
19717
+ "Comparable",
19718
+ // Serialization / cloning contracts
19719
+ "Serializable",
19720
+ "Externalizable",
19721
+ "Cloneable"
19722
+ ]);
19723
+ function classNameLooksLikeUtility(name2) {
19724
+ if (!name2) return false;
19725
+ for (const suffix of TIER_3_CLASS_SUFFIXES) {
19726
+ if (name2.length > suffix.length && name2.endsWith(suffix)) return true;
19727
+ }
19728
+ return false;
19729
+ }
19730
+ function packageLooksLikeTemplateOrEngine(pkg) {
19731
+ if (!pkg) return false;
19732
+ const padded = `.${pkg}.`;
19733
+ for (const frag of TIER_3_PACKAGE_FRAGMENTS) {
19734
+ if (padded.includes(frag)) return true;
19735
+ }
19736
+ return false;
19737
+ }
19738
+ function implementsJdkFacade(t) {
19739
+ if (!t) return false;
19740
+ for (const impl of t.implements ?? []) {
19741
+ if (TIER_3_JDK_FACADE_INTERFACES.has(simpleTypeName(impl))) return true;
19742
+ }
19743
+ return false;
19744
+ }
19745
+ function classShapeIsLibraryFacade(enclosingType) {
19746
+ if (!enclosingType) return false;
19747
+ if (classNameLooksLikeUtility(enclosingType.name)) return true;
19748
+ if (packageLooksLikeTemplateOrEngine(enclosingType.package)) return true;
19749
+ if (implementsJdkFacade(enclosingType)) return true;
19750
+ return false;
19751
+ }
19752
+ function annotationsInclude(annotations, targets) {
19753
+ if (!annotations || annotations.length === 0) return false;
19754
+ for (const raw of annotations) {
19755
+ const simple = raw.replace(/^@/, "").replace(/[<(].*$/, "").trim();
19756
+ if (targets.has(simple)) return true;
19757
+ }
19758
+ return false;
19759
+ }
19760
+ function simpleTypeName(ref) {
19761
+ return ref.replace(/<.*$/, "").trim();
19762
+ }
19763
+ function looksLikeMainMethod(method) {
19764
+ if (method.name !== "main") return false;
19765
+ const params = method.parameters ?? [];
19766
+ if (params.length !== 1) return false;
19767
+ const t = (params[0].type ?? "").replace(/\s+/g, "");
19768
+ return t === "String[]" || t === "String..." || t === "java.lang.String[]";
19769
+ }
19770
+ function methodIsSupertypeLifecycleEntryPoint(method, enclosingType) {
19771
+ if (!method.name) return false;
19772
+ if (!enclosingType) return false;
19773
+ const candidates = [];
19774
+ if (enclosingType.extends) candidates.push(simpleTypeName(enclosingType.extends));
19775
+ for (const i2 of enclosingType.implements ?? []) candidates.push(simpleTypeName(i2));
19776
+ for (const supertype of candidates) {
19777
+ const lifecycleMethods = TIER_1_BY_SUPERTYPE.get(supertype);
19778
+ if (lifecycleMethods?.has(method.name)) return true;
19779
+ }
19780
+ return false;
19781
+ }
19782
+ function classifyEntryPointTier(method, enclosingType, ctx) {
19783
+ const language = (ctx.language ?? "").toLowerCase();
19784
+ if (language !== "java") return "TIER_UNKNOWN";
19785
+ if (!method) return "TIER_UNKNOWN";
19786
+ if (classShapeIsLibraryFacade(enclosingType)) {
19787
+ return "TIER_3_LIBRARY_API";
19788
+ }
19789
+ if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS)) {
19790
+ return "TIER_1_ENTRY_POINT";
19791
+ }
19792
+ if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS)) {
19793
+ return "TIER_1_ENTRY_POINT";
19794
+ }
19795
+ if (methodIsSupertypeLifecycleEntryPoint(method, enclosingType)) {
19796
+ return "TIER_1_ENTRY_POINT";
19797
+ }
19798
+ if (looksLikeMainMethod(method)) {
19799
+ return "TIER_1_ENTRY_POINT";
19800
+ }
19801
+ return "TIER_3_LIBRARY_API";
19802
+ }
19803
+ function shouldGateInterproceduralParam(sourceType, enclosingMethod, enclosingType, ctx) {
19804
+ if (sourceType !== "interprocedural_param") return false;
19805
+ if (!enclosingMethod) return false;
19806
+ const tier = classifyEntryPointTier(enclosingMethod, enclosingType, ctx);
19807
+ return tier === "TIER_3_LIBRARY_API";
19808
+ }
19809
+
19556
19810
  // src/analysis/project-profile-transform.ts
19557
19811
  var DOWNGRADE_ELIGIBLE_RULE_IDS = /* @__PURE__ */ new Set([
19558
19812
  "code_injection",
@@ -30818,7 +31072,7 @@ var REFLECTION_SINK_METHODS = /* @__PURE__ */ new Set([
30818
31072
  "loadClass",
30819
31073
  "defineClass"
30820
31074
  ]);
30821
- var TIER_1_CLASS_ANNOTATIONS = /* @__PURE__ */ new Set([
31075
+ var TIER_1_CLASS_ANNOTATIONS2 = /* @__PURE__ */ new Set([
30822
31076
  // Spring MVC
30823
31077
  "RestController",
30824
31078
  "Controller",
@@ -30835,7 +31089,7 @@ var TIER_1_CLASS_ANNOTATIONS = /* @__PURE__ */ new Set([
30835
31089
  // Declarative HTTP client
30836
31090
  "FeignClient"
30837
31091
  ]);
30838
- var TIER_1_METHOD_ANNOTATIONS = /* @__PURE__ */ new Set([
31092
+ var TIER_1_METHOD_ANNOTATIONS2 = /* @__PURE__ */ new Set([
30839
31093
  // Spring MVC
30840
31094
  "RequestMapping",
30841
31095
  "GetMapping",
@@ -30936,7 +31190,7 @@ var CliMainReflectionSuppressPass = class {
30936
31190
  let hasFrameworkSignal = false;
30937
31191
  for (const type of types) {
30938
31192
  for (const ann of type.annotations) {
30939
- if (TIER_1_CLASS_ANNOTATIONS.has(normalizeAnnotation(ann))) {
31193
+ if (TIER_1_CLASS_ANNOTATIONS2.has(normalizeAnnotation(ann))) {
30940
31194
  hasFrameworkSignal = true;
30941
31195
  break;
30942
31196
  }
@@ -30955,7 +31209,7 @@ var CliMainReflectionSuppressPass = class {
30955
31209
  if (hasFrameworkSignal) break;
30956
31210
  for (const method of type.methods) {
30957
31211
  for (const ann of method.annotations) {
30958
- if (TIER_1_METHOD_ANNOTATIONS.has(normalizeAnnotation(ann))) {
31212
+ if (TIER_1_METHOD_ANNOTATIONS2.has(normalizeAnnotation(ann))) {
30959
31213
  hasFrameworkSignal = true;
30960
31214
  break;
30961
31215
  }
@@ -30991,20 +31245,74 @@ var CliMainReflectionSuppressPass = class {
30991
31245
  }
30992
31246
  };
30993
31247
 
30994
- // src/analysis/passes/taint-propagation-pass.ts
30995
- var TaintPropagationPass = class {
30996
- name = "taint-propagation";
31248
+ // src/analysis/passes/library-profile-sink-gate-pass.ts
31249
+ var DROPPED_SINK_TYPES = /* @__PURE__ */ new Set([
31250
+ "log_injection"
31251
+ ]);
31252
+ function isLibraryShape2(profile) {
31253
+ if (!profile || profile === "unknown") return false;
31254
+ return profile.startsWith("library/");
31255
+ }
31256
+ var LibraryProfileSinkGatePass = class {
31257
+ name = "library-profile-sink-gate";
30997
31258
  category = "security";
30998
31259
  run(ctx) {
30999
31260
  const { graph } = ctx;
31000
- const { calls, types } = graph.ir;
31001
- const constProp = ctx.getResult("constant-propagation");
31002
- const sinkFilter = ctx.getResult("sink-filter");
31003
- const { sources, sinks, sanitizers } = sinkFilter;
31004
- if (sinks.length === 0) {
31005
- return { flows: [] };
31006
- }
31007
- const canSynthesize = ctx.language === "python" && typeof ctx.code === "string";
31261
+ const profile = graph.ir.meta.projectProfile;
31262
+ if (!isLibraryShape2(profile)) {
31263
+ return {
31264
+ profile,
31265
+ applied: false,
31266
+ dropped: 0,
31267
+ droppedByType: {}
31268
+ };
31269
+ }
31270
+ const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
31271
+ if (sinks.length === 0) {
31272
+ return {
31273
+ profile,
31274
+ applied: true,
31275
+ dropped: 0,
31276
+ droppedByType: {}
31277
+ };
31278
+ }
31279
+ const droppedByType = {};
31280
+ const kept = [];
31281
+ for (const sink of sinks) {
31282
+ if (DROPPED_SINK_TYPES.has(sink.type)) {
31283
+ droppedByType[sink.type] = (droppedByType[sink.type] ?? 0) + 1;
31284
+ continue;
31285
+ }
31286
+ kept.push(sink);
31287
+ }
31288
+ const dropped = sinks.length - kept.length;
31289
+ if (dropped > 0) {
31290
+ sinks.length = 0;
31291
+ sinks.push(...kept);
31292
+ }
31293
+ return {
31294
+ profile,
31295
+ applied: true,
31296
+ dropped,
31297
+ droppedByType
31298
+ };
31299
+ }
31300
+ };
31301
+
31302
+ // src/analysis/passes/taint-propagation-pass.ts
31303
+ var TaintPropagationPass = class {
31304
+ name = "taint-propagation";
31305
+ category = "security";
31306
+ run(ctx) {
31307
+ const { graph } = ctx;
31308
+ const { calls, types } = graph.ir;
31309
+ const constProp = ctx.getResult("constant-propagation");
31310
+ const sinkFilter = ctx.getResult("sink-filter");
31311
+ const { sources, sinks, sanitizers } = sinkFilter;
31312
+ if (sinks.length === 0) {
31313
+ return { flows: [] };
31314
+ }
31315
+ const canSynthesize = ctx.language === "python" && typeof ctx.code === "string";
31008
31316
  if (sources.length === 0 && !canSynthesize) {
31009
31317
  return { flows: [] };
31010
31318
  }
@@ -31781,238 +32089,6 @@ function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
31781
32089
  return sources[0];
31782
32090
  }
31783
32091
 
31784
- // src/analysis/entry-point-detection.ts
31785
- var TIER_1_METHOD_ANNOTATIONS2 = /* @__PURE__ */ new Set([
31786
- // Spring MVC
31787
- "RequestMapping",
31788
- "GetMapping",
31789
- "PostMapping",
31790
- "PutMapping",
31791
- "DeleteMapping",
31792
- "PatchMapping",
31793
- // Spring messaging / WebSocket
31794
- "MessageMapping",
31795
- "SubscribeMapping",
31796
- // Spring messaging listeners
31797
- "KafkaListener",
31798
- "KafkaHandler",
31799
- "RabbitListener",
31800
- "RabbitHandler",
31801
- "JmsListener",
31802
- "StreamListener",
31803
- // Spring Cloud AWS
31804
- "SqsListener",
31805
- "SqsHandler",
31806
- // Spring application events (borderline — inclusive in ship 1)
31807
- "EventListener",
31808
- // CRON / scheduled
31809
- "Scheduled",
31810
- // JAX-RS
31811
- "Path",
31812
- "GET",
31813
- "POST",
31814
- "PUT",
31815
- "DELETE",
31816
- "PATCH",
31817
- "HEAD",
31818
- "OPTIONS",
31819
- // Jenkins Stapler form-binding (#224 — CVE-2022-20617 docker-commons).
31820
- // `@DataBoundConstructor` / `@DataBoundSetter` mark the trust boundary
31821
- // between Jenkins UI (or `config.xml` unmarshal) and the plugin: the
31822
- // Stapler runtime invokes the annotated constructor / setter with
31823
- // user-supplied strings from a submitted form or a persisted job
31824
- // config. Every parameter is a user-supplied taint source.
31825
- "DataBoundConstructor",
31826
- "DataBoundSetter"
31827
- ]);
31828
- var TIER_1_CLASS_ANNOTATIONS2 = /* @__PURE__ */ new Set([
31829
- // Spring MVC
31830
- "RestController",
31831
- "Controller",
31832
- // Spring stereotype beans (#136 — see header)
31833
- "Service",
31834
- "Repository",
31835
- "Component",
31836
- // JAX-RS resource class
31837
- "Path",
31838
- // Servlet 3.0 annotation-based servlet
31839
- "WebServlet",
31840
- // JSR-356 WebSocket endpoint
31841
- "ServerEndpoint",
31842
- // Declarative HTTP client (borderline — inclusive in ship 1; contract-defined
31843
- // parameter shapes are downstream-relevant trust boundaries)
31844
- "FeignClient"
31845
- ]);
31846
- var TIER_1_BY_SUPERTYPE = /* @__PURE__ */ new Map([
31847
- // Servlet API
31848
- ["HttpServlet", /* @__PURE__ */ new Set(["doGet", "doPost", "doPut", "doDelete", "doHead", "doOptions", "doTrace", "service"])],
31849
- ["GenericServlet", /* @__PURE__ */ new Set(["service"])],
31850
- ["Filter", /* @__PURE__ */ new Set(["doFilter"])],
31851
- // Spring web
31852
- ["HandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion"])],
31853
- ["AsyncHandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion", "afterConcurrentHandlingStarted"])],
31854
- // Spring boot CLI entry points
31855
- ["CommandLineRunner", /* @__PURE__ */ new Set(["run"])],
31856
- ["ApplicationRunner", /* @__PURE__ */ new Set(["run"])],
31857
- // Netty channel handlers (#154 — CVE-2022-26884 dolphinscheduler).
31858
- // `SimpleChannelInboundHandler<T>.channelRead0(ctx, msg)` is the
31859
- // standard typed read entry; `ChannelInboundHandler.channelRead(ctx,
31860
- // Object)` is the untyped base. `ChannelInboundHandlerAdapter` and
31861
- // `ChannelDuplexHandler` are the most-common adapter classes user
31862
- // handlers extend. `NettyRequestProcessor` is the dolphinscheduler-
31863
- // specific (and other Netty-RPC projects') wire-message processor
31864
- // surface — `process(channel, command)` is the network entry point.
31865
- ["SimpleChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead0", "messageReceived"])],
31866
- ["ChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
31867
- ["ChannelInboundHandlerAdapter", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
31868
- ["ChannelDuplexHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
31869
- ["NettyRequestProcessor", /* @__PURE__ */ new Set(["process"])],
31870
- // XStream deserialization converters (#224 — CVE-2020-26217,
31871
- // CVE-2021-21345). The xstream deserializer invokes `unmarshal`
31872
- // with attacker-controlled `HierarchicalStreamReader` state whenever
31873
- // untrusted XML reaches `XStream.fromXML`. Each converter is a
31874
- // deserialization gadget surface — its `unmarshal` parameters are
31875
- // network-facing taint sources even though the enclosing class
31876
- // carries no framework annotation. `marshal` is included for the
31877
- // symmetric round-trip surface but is rarely exploitable on its
31878
- // own; the fixup targets `unmarshal` primarily.
31879
- //
31880
- // `SingleValueConverter` is the string-form variant (fromString
31881
- // parses an attacker string; toString is the sink half of the
31882
- // round-trip). `ConverterMatcher` is the shared base and some
31883
- // downstream projects subclass it directly.
31884
- ["Converter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
31885
- ["SingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
31886
- ["ConverterMatcher", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
31887
- // XStream abstract base classes — direct-parent `extends` match
31888
- // covers the common shape where user converters subclass a base
31889
- // rather than implementing `Converter` directly.
31890
- ["AbstractReflectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal", "doMarshal", "doUnmarshal"])],
31891
- ["AbstractSingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
31892
- ["AbstractCollectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])]
31893
- ]);
31894
- var TIER_3_CLASS_SUFFIXES = [
31895
- "Util",
31896
- "Utils",
31897
- "Helper",
31898
- "Helpers"
31899
- ];
31900
- var TIER_3_PACKAGE_FRAGMENTS = [
31901
- ".template.",
31902
- ".templates.",
31903
- ".engine.",
31904
- ".engines."
31905
- ];
31906
- var TIER_3_JDK_FACADE_INTERFACES = /* @__PURE__ */ new Set([
31907
- // Collection root + common containers
31908
- "Collection",
31909
- "List",
31910
- "Set",
31911
- "Map",
31912
- "Queue",
31913
- "Deque",
31914
- "SortedSet",
31915
- "SortedMap",
31916
- "NavigableSet",
31917
- "NavigableMap",
31918
- // Iteration / ordering / equality contracts
31919
- "Iterator",
31920
- "Iterable",
31921
- "ListIterator",
31922
- "Comparator",
31923
- "Comparable",
31924
- // Serialization / cloning contracts
31925
- "Serializable",
31926
- "Externalizable",
31927
- "Cloneable"
31928
- ]);
31929
- function classNameLooksLikeUtility(name2) {
31930
- if (!name2) return false;
31931
- for (const suffix of TIER_3_CLASS_SUFFIXES) {
31932
- if (name2.length > suffix.length && name2.endsWith(suffix)) return true;
31933
- }
31934
- return false;
31935
- }
31936
- function packageLooksLikeTemplateOrEngine(pkg) {
31937
- if (!pkg) return false;
31938
- const padded = `.${pkg}.`;
31939
- for (const frag of TIER_3_PACKAGE_FRAGMENTS) {
31940
- if (padded.includes(frag)) return true;
31941
- }
31942
- return false;
31943
- }
31944
- function implementsJdkFacade(t) {
31945
- if (!t) return false;
31946
- for (const impl of t.implements ?? []) {
31947
- if (TIER_3_JDK_FACADE_INTERFACES.has(simpleTypeName(impl))) return true;
31948
- }
31949
- return false;
31950
- }
31951
- function classShapeIsLibraryFacade(enclosingType) {
31952
- if (!enclosingType) return false;
31953
- if (classNameLooksLikeUtility(enclosingType.name)) return true;
31954
- if (packageLooksLikeTemplateOrEngine(enclosingType.package)) return true;
31955
- if (implementsJdkFacade(enclosingType)) return true;
31956
- return false;
31957
- }
31958
- function annotationsInclude(annotations, targets) {
31959
- if (!annotations || annotations.length === 0) return false;
31960
- for (const raw of annotations) {
31961
- const simple = raw.replace(/^@/, "").replace(/[<(].*$/, "").trim();
31962
- if (targets.has(simple)) return true;
31963
- }
31964
- return false;
31965
- }
31966
- function simpleTypeName(ref) {
31967
- return ref.replace(/<.*$/, "").trim();
31968
- }
31969
- function looksLikeMainMethod(method) {
31970
- if (method.name !== "main") return false;
31971
- const params = method.parameters ?? [];
31972
- if (params.length !== 1) return false;
31973
- const t = (params[0].type ?? "").replace(/\s+/g, "");
31974
- return t === "String[]" || t === "String..." || t === "java.lang.String[]";
31975
- }
31976
- function methodIsSupertypeLifecycleEntryPoint(method, enclosingType) {
31977
- if (!method.name) return false;
31978
- if (!enclosingType) return false;
31979
- const candidates = [];
31980
- if (enclosingType.extends) candidates.push(simpleTypeName(enclosingType.extends));
31981
- for (const i2 of enclosingType.implements ?? []) candidates.push(simpleTypeName(i2));
31982
- for (const supertype of candidates) {
31983
- const lifecycleMethods = TIER_1_BY_SUPERTYPE.get(supertype);
31984
- if (lifecycleMethods?.has(method.name)) return true;
31985
- }
31986
- return false;
31987
- }
31988
- function classifyEntryPointTier(method, enclosingType, ctx) {
31989
- const language = (ctx.language ?? "").toLowerCase();
31990
- if (language !== "java") return "TIER_UNKNOWN";
31991
- if (!method) return "TIER_UNKNOWN";
31992
- if (classShapeIsLibraryFacade(enclosingType)) {
31993
- return "TIER_3_LIBRARY_API";
31994
- }
31995
- if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS2)) {
31996
- return "TIER_1_ENTRY_POINT";
31997
- }
31998
- if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS2)) {
31999
- return "TIER_1_ENTRY_POINT";
32000
- }
32001
- if (methodIsSupertypeLifecycleEntryPoint(method, enclosingType)) {
32002
- return "TIER_1_ENTRY_POINT";
32003
- }
32004
- if (looksLikeMainMethod(method)) {
32005
- return "TIER_1_ENTRY_POINT";
32006
- }
32007
- return "TIER_3_LIBRARY_API";
32008
- }
32009
- function shouldGateInterproceduralParam(sourceType, enclosingMethod, enclosingType, ctx) {
32010
- if (sourceType !== "interprocedural_param") return false;
32011
- if (!enclosingMethod) return false;
32012
- const tier = classifyEntryPointTier(enclosingMethod, enclosingType, ctx);
32013
- return tier === "TIER_3_LIBRARY_API";
32014
- }
32015
-
32016
32092
  // src/analysis/passes/interprocedural-pass.ts
32017
32093
  var InterproceduralPass = class {
32018
32094
  name = "interprocedural";
@@ -40687,6 +40763,8 @@ async function analyze(code, filePath, language, options = {}) {
40687
40763
  if (!disabledPasses.has("sink-semantics")) pipeline.add(new SinkSemanticsPass());
40688
40764
  if (!disabledPasses.has("cli-main-reflection-suppress"))
40689
40765
  pipeline.add(new CliMainReflectionSuppressPass());
40766
+ if (!disabledPasses.has("library-profile-sink-gate"))
40767
+ pipeline.add(new LibraryProfileSinkGatePass());
40690
40768
  pipeline.add(new TaintPropagationPass());
40691
40769
  pipeline.add(new InterproceduralPass({
40692
40770
  enableEntryPointGate: options.enableEntryPointGate ?? true