circle-ir 3.152.0 → 3.154.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.
- package/configs/sink-semantics.json +102 -0
- package/configs/sinks/command.yaml +0 -10
- package/configs/sinks/deserialization.yaml +54 -0
- package/configs/sinks/path.yaml +0 -94
- package/configs/sinks/sql.yaml +24 -24
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +24 -8
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/library-profile-sink-gate-pass.d.ts +43 -1
- package/dist/analysis/passes/library-profile-sink-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/library-profile-sink-gate-pass.js +75 -0
- package/dist/analysis/passes/library-profile-sink-gate-pass.js.map +1 -1
- package/dist/analysis/passes/library-profile-xss-gate-pass.d.ts +104 -0
- package/dist/analysis/passes/library-profile-xss-gate-pass.d.ts.map +1 -0
- package/dist/analysis/passes/library-profile-xss-gate-pass.js +196 -0
- package/dist/analysis/passes/library-profile-xss-gate-pass.js.map +1 -0
- package/dist/analysis/require-entry-path.d.ts +91 -0
- package/dist/analysis/require-entry-path.d.ts.map +1 -0
- package/dist/analysis/require-entry-path.js +387 -0
- package/dist/analysis/require-entry-path.js.map +1 -0
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +55 -1
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +37 -1
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +437 -257
- package/dist/core/circle-ir-core.cjs +38 -9
- package/dist/core/circle-ir-core.js +38 -9
- package/dist/types/config.d.ts +11 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +25 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
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] },
|
|
@@ -11304,9 +11304,16 @@ var DEFAULT_SINKS = [
|
|
|
11304
11304
|
{ method: "newBufferedWriter", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
11305
11305
|
{ method: "copy", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
|
|
11306
11306
|
{ method: "move", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11307
|
+
// NOTE: `Files.exists`, `Files.isDirectory`, `Files.isRegularFile`
|
|
11308
|
+
// were removed in 3.154.0 (#245 RC2). These NIO methods are pure
|
|
11309
|
+
// boolean queries — they read a filesystem attribute and cannot
|
|
11310
|
+
// cause traversal escape. A CWE-22 sink must consume the path to
|
|
11311
|
+
// open, read, write, delete, list, or link a filesystem entry;
|
|
11312
|
+
// check-only receivers reveal at most a boolean. Empirically
|
|
11313
|
+
// ~12 H+C FPs across the 10-repo Tier 2 cohort
|
|
11314
|
+
// (cognium-ai#189 §4). `java.io.File` instance methods
|
|
11315
|
+
// (`file.isDirectory()`, `file.exists()`, `file.canRead()`, …)
|
|
11316
|
+
// are already not registered as CWE-22 sinks.
|
|
11310
11317
|
// RandomAccessFile
|
|
11311
11318
|
{ method: "RandomAccessFile", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
11312
11319
|
// Framework-specific resource loading (Cocoon, Spring, etc.)
|
|
@@ -11793,6 +11800,15 @@ var DEFAULT_SINKS = [
|
|
|
11793
11800
|
{ method: "parseObject", class: "JSON", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
|
|
11794
11801
|
{ method: "parseObject", class: "JSONObject", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
|
|
11795
11802
|
{ method: "fromJson", class: "Gson", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0], safe_if_class_literal_at: 1 },
|
|
11803
|
+
// Jackson ObjectReader — pre-configured reader; typed 2-arg overload is safe
|
|
11804
|
+
// when arg[1] is a class literal or TypeReference<>()/TypeToken<>() (handled
|
|
11805
|
+
// by argIsClassLiteral extension). Added 3.153.0 (cognium-dev #233).
|
|
11806
|
+
{ method: "readValue", class: "ObjectReader", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
|
|
11807
|
+
// Jackson ObjectMapper.convertValue — same class-literal shape as readValue.
|
|
11808
|
+
{ method: "convertValue", class: "ObjectMapper", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0], safe_if_class_literal_at: 1 },
|
|
11809
|
+
// Kryo.readObject(input, User.class) — typed form is safe; polymorphic form
|
|
11810
|
+
// (Kryo.readClassAndObject or non-literal type) remains a sink.
|
|
11811
|
+
{ method: "readObject", class: "Kryo", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], safe_if_class_literal_at: 1 },
|
|
11796
11812
|
// XMLDecoder
|
|
11797
11813
|
{ method: "readObject", class: "XMLDecoder", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [] },
|
|
11798
11814
|
// Java serialization constructors
|
|
@@ -13652,7 +13668,17 @@ function argIsClassLiteral(call, position) {
|
|
|
13652
13668
|
if (!arg) return false;
|
|
13653
13669
|
const expr = (arg.literal ?? arg.expression ?? "").trim();
|
|
13654
13670
|
if (!expr) return false;
|
|
13655
|
-
|
|
13671
|
+
if (CLASS_LITERAL_RE.test(expr)) return true;
|
|
13672
|
+
return TYPE_TOKEN_RE.test(expr);
|
|
13673
|
+
}
|
|
13674
|
+
var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
|
|
13675
|
+
function argIsStringLiteral(call, position) {
|
|
13676
|
+
const arg = call.arguments.find((a) => a.position === position);
|
|
13677
|
+
if (!arg) return false;
|
|
13678
|
+
if (arg.literal !== void 0 && arg.literal !== null && arg.literal !== "") return true;
|
|
13679
|
+
const expr = (arg.expression ?? "").trim();
|
|
13680
|
+
if (!expr) return false;
|
|
13681
|
+
return expr.startsWith('"') && expr.endsWith('"') && expr.length >= 2 || expr.startsWith("'") && expr.endsWith("'") && expr.length >= 2 || expr.startsWith('"""') && expr.endsWith('"""');
|
|
13656
13682
|
}
|
|
13657
13683
|
var CWE_78_RECEIVER_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
13658
13684
|
// java.lang.*
|
|
@@ -13827,6 +13853,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
13827
13853
|
if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
|
|
13828
13854
|
continue;
|
|
13829
13855
|
}
|
|
13856
|
+
if (pattern.safe_if_string_literal_at !== void 0 && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
|
|
13857
|
+
continue;
|
|
13858
|
+
}
|
|
13830
13859
|
if (pattern.type === "command_injection") {
|
|
13831
13860
|
if (call.is_constructor) {
|
|
13832
13861
|
if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
|
|
@@ -19553,6 +19582,238 @@ function applyLibraryApiSurfaceDowngrade(findings) {
|
|
|
19553
19582
|
});
|
|
19554
19583
|
}
|
|
19555
19584
|
|
|
19585
|
+
// src/analysis/entry-point-detection.ts
|
|
19586
|
+
var TIER_1_METHOD_ANNOTATIONS = /* @__PURE__ */ new Set([
|
|
19587
|
+
// Spring MVC
|
|
19588
|
+
"RequestMapping",
|
|
19589
|
+
"GetMapping",
|
|
19590
|
+
"PostMapping",
|
|
19591
|
+
"PutMapping",
|
|
19592
|
+
"DeleteMapping",
|
|
19593
|
+
"PatchMapping",
|
|
19594
|
+
// Spring messaging / WebSocket
|
|
19595
|
+
"MessageMapping",
|
|
19596
|
+
"SubscribeMapping",
|
|
19597
|
+
// Spring messaging listeners
|
|
19598
|
+
"KafkaListener",
|
|
19599
|
+
"KafkaHandler",
|
|
19600
|
+
"RabbitListener",
|
|
19601
|
+
"RabbitHandler",
|
|
19602
|
+
"JmsListener",
|
|
19603
|
+
"StreamListener",
|
|
19604
|
+
// Spring Cloud AWS
|
|
19605
|
+
"SqsListener",
|
|
19606
|
+
"SqsHandler",
|
|
19607
|
+
// Spring application events (borderline — inclusive in ship 1)
|
|
19608
|
+
"EventListener",
|
|
19609
|
+
// CRON / scheduled
|
|
19610
|
+
"Scheduled",
|
|
19611
|
+
// JAX-RS
|
|
19612
|
+
"Path",
|
|
19613
|
+
"GET",
|
|
19614
|
+
"POST",
|
|
19615
|
+
"PUT",
|
|
19616
|
+
"DELETE",
|
|
19617
|
+
"PATCH",
|
|
19618
|
+
"HEAD",
|
|
19619
|
+
"OPTIONS",
|
|
19620
|
+
// Jenkins Stapler form-binding (#224 — CVE-2022-20617 docker-commons).
|
|
19621
|
+
// `@DataBoundConstructor` / `@DataBoundSetter` mark the trust boundary
|
|
19622
|
+
// between Jenkins UI (or `config.xml` unmarshal) and the plugin: the
|
|
19623
|
+
// Stapler runtime invokes the annotated constructor / setter with
|
|
19624
|
+
// user-supplied strings from a submitted form or a persisted job
|
|
19625
|
+
// config. Every parameter is a user-supplied taint source.
|
|
19626
|
+
"DataBoundConstructor",
|
|
19627
|
+
"DataBoundSetter"
|
|
19628
|
+
]);
|
|
19629
|
+
var TIER_1_CLASS_ANNOTATIONS = /* @__PURE__ */ new Set([
|
|
19630
|
+
// Spring MVC
|
|
19631
|
+
"RestController",
|
|
19632
|
+
"Controller",
|
|
19633
|
+
// Spring stereotype beans (#136 — see header)
|
|
19634
|
+
"Service",
|
|
19635
|
+
"Repository",
|
|
19636
|
+
"Component",
|
|
19637
|
+
// JAX-RS resource class
|
|
19638
|
+
"Path",
|
|
19639
|
+
// Servlet 3.0 annotation-based servlet
|
|
19640
|
+
"WebServlet",
|
|
19641
|
+
// JSR-356 WebSocket endpoint
|
|
19642
|
+
"ServerEndpoint",
|
|
19643
|
+
// Declarative HTTP client (borderline — inclusive in ship 1; contract-defined
|
|
19644
|
+
// parameter shapes are downstream-relevant trust boundaries)
|
|
19645
|
+
"FeignClient"
|
|
19646
|
+
]);
|
|
19647
|
+
var TIER_1_BY_SUPERTYPE = /* @__PURE__ */ new Map([
|
|
19648
|
+
// Servlet API
|
|
19649
|
+
["HttpServlet", /* @__PURE__ */ new Set(["doGet", "doPost", "doPut", "doDelete", "doHead", "doOptions", "doTrace", "service"])],
|
|
19650
|
+
["GenericServlet", /* @__PURE__ */ new Set(["service"])],
|
|
19651
|
+
["Filter", /* @__PURE__ */ new Set(["doFilter"])],
|
|
19652
|
+
// Spring web
|
|
19653
|
+
["HandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion"])],
|
|
19654
|
+
["AsyncHandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion", "afterConcurrentHandlingStarted"])],
|
|
19655
|
+
// Spring boot CLI entry points
|
|
19656
|
+
["CommandLineRunner", /* @__PURE__ */ new Set(["run"])],
|
|
19657
|
+
["ApplicationRunner", /* @__PURE__ */ new Set(["run"])],
|
|
19658
|
+
// Netty channel handlers (#154 — CVE-2022-26884 dolphinscheduler).
|
|
19659
|
+
// `SimpleChannelInboundHandler<T>.channelRead0(ctx, msg)` is the
|
|
19660
|
+
// standard typed read entry; `ChannelInboundHandler.channelRead(ctx,
|
|
19661
|
+
// Object)` is the untyped base. `ChannelInboundHandlerAdapter` and
|
|
19662
|
+
// `ChannelDuplexHandler` are the most-common adapter classes user
|
|
19663
|
+
// handlers extend. `NettyRequestProcessor` is the dolphinscheduler-
|
|
19664
|
+
// specific (and other Netty-RPC projects') wire-message processor
|
|
19665
|
+
// surface — `process(channel, command)` is the network entry point.
|
|
19666
|
+
["SimpleChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead0", "messageReceived"])],
|
|
19667
|
+
["ChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
|
|
19668
|
+
["ChannelInboundHandlerAdapter", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
|
|
19669
|
+
["ChannelDuplexHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
|
|
19670
|
+
["NettyRequestProcessor", /* @__PURE__ */ new Set(["process"])],
|
|
19671
|
+
// XStream deserialization converters (#224 — CVE-2020-26217,
|
|
19672
|
+
// CVE-2021-21345). The xstream deserializer invokes `unmarshal`
|
|
19673
|
+
// with attacker-controlled `HierarchicalStreamReader` state whenever
|
|
19674
|
+
// untrusted XML reaches `XStream.fromXML`. Each converter is a
|
|
19675
|
+
// deserialization gadget surface — its `unmarshal` parameters are
|
|
19676
|
+
// network-facing taint sources even though the enclosing class
|
|
19677
|
+
// carries no framework annotation. `marshal` is included for the
|
|
19678
|
+
// symmetric round-trip surface but is rarely exploitable on its
|
|
19679
|
+
// own; the fixup targets `unmarshal` primarily.
|
|
19680
|
+
//
|
|
19681
|
+
// `SingleValueConverter` is the string-form variant (fromString
|
|
19682
|
+
// parses an attacker string; toString is the sink half of the
|
|
19683
|
+
// round-trip). `ConverterMatcher` is the shared base and some
|
|
19684
|
+
// downstream projects subclass it directly.
|
|
19685
|
+
["Converter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
|
|
19686
|
+
["SingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
|
|
19687
|
+
["ConverterMatcher", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
|
|
19688
|
+
// XStream abstract base classes — direct-parent `extends` match
|
|
19689
|
+
// covers the common shape where user converters subclass a base
|
|
19690
|
+
// rather than implementing `Converter` directly.
|
|
19691
|
+
["AbstractReflectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal", "doMarshal", "doUnmarshal"])],
|
|
19692
|
+
["AbstractSingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
|
|
19693
|
+
["AbstractCollectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])]
|
|
19694
|
+
]);
|
|
19695
|
+
var TIER_3_CLASS_SUFFIXES = [
|
|
19696
|
+
"Util",
|
|
19697
|
+
"Utils",
|
|
19698
|
+
"Helper",
|
|
19699
|
+
"Helpers"
|
|
19700
|
+
];
|
|
19701
|
+
var TIER_3_PACKAGE_FRAGMENTS = [
|
|
19702
|
+
".template.",
|
|
19703
|
+
".templates.",
|
|
19704
|
+
".engine.",
|
|
19705
|
+
".engines."
|
|
19706
|
+
];
|
|
19707
|
+
var TIER_3_JDK_FACADE_INTERFACES = /* @__PURE__ */ new Set([
|
|
19708
|
+
// Collection root + common containers
|
|
19709
|
+
"Collection",
|
|
19710
|
+
"List",
|
|
19711
|
+
"Set",
|
|
19712
|
+
"Map",
|
|
19713
|
+
"Queue",
|
|
19714
|
+
"Deque",
|
|
19715
|
+
"SortedSet",
|
|
19716
|
+
"SortedMap",
|
|
19717
|
+
"NavigableSet",
|
|
19718
|
+
"NavigableMap",
|
|
19719
|
+
// Iteration / ordering / equality contracts
|
|
19720
|
+
"Iterator",
|
|
19721
|
+
"Iterable",
|
|
19722
|
+
"ListIterator",
|
|
19723
|
+
"Comparator",
|
|
19724
|
+
"Comparable",
|
|
19725
|
+
// Serialization / cloning contracts
|
|
19726
|
+
"Serializable",
|
|
19727
|
+
"Externalizable",
|
|
19728
|
+
"Cloneable"
|
|
19729
|
+
]);
|
|
19730
|
+
function classNameLooksLikeUtility(name2) {
|
|
19731
|
+
if (!name2) return false;
|
|
19732
|
+
for (const suffix of TIER_3_CLASS_SUFFIXES) {
|
|
19733
|
+
if (name2.length > suffix.length && name2.endsWith(suffix)) return true;
|
|
19734
|
+
}
|
|
19735
|
+
return false;
|
|
19736
|
+
}
|
|
19737
|
+
function packageLooksLikeTemplateOrEngine(pkg) {
|
|
19738
|
+
if (!pkg) return false;
|
|
19739
|
+
const padded = `.${pkg}.`;
|
|
19740
|
+
for (const frag of TIER_3_PACKAGE_FRAGMENTS) {
|
|
19741
|
+
if (padded.includes(frag)) return true;
|
|
19742
|
+
}
|
|
19743
|
+
return false;
|
|
19744
|
+
}
|
|
19745
|
+
function implementsJdkFacade(t) {
|
|
19746
|
+
if (!t) return false;
|
|
19747
|
+
for (const impl of t.implements ?? []) {
|
|
19748
|
+
if (TIER_3_JDK_FACADE_INTERFACES.has(simpleTypeName(impl))) return true;
|
|
19749
|
+
}
|
|
19750
|
+
return false;
|
|
19751
|
+
}
|
|
19752
|
+
function classShapeIsLibraryFacade(enclosingType) {
|
|
19753
|
+
if (!enclosingType) return false;
|
|
19754
|
+
if (classNameLooksLikeUtility(enclosingType.name)) return true;
|
|
19755
|
+
if (packageLooksLikeTemplateOrEngine(enclosingType.package)) return true;
|
|
19756
|
+
if (implementsJdkFacade(enclosingType)) return true;
|
|
19757
|
+
return false;
|
|
19758
|
+
}
|
|
19759
|
+
function annotationsInclude(annotations, targets) {
|
|
19760
|
+
if (!annotations || annotations.length === 0) return false;
|
|
19761
|
+
for (const raw of annotations) {
|
|
19762
|
+
const simple = raw.replace(/^@/, "").replace(/[<(].*$/, "").trim();
|
|
19763
|
+
if (targets.has(simple)) return true;
|
|
19764
|
+
}
|
|
19765
|
+
return false;
|
|
19766
|
+
}
|
|
19767
|
+
function simpleTypeName(ref) {
|
|
19768
|
+
return ref.replace(/<.*$/, "").trim();
|
|
19769
|
+
}
|
|
19770
|
+
function looksLikeMainMethod(method) {
|
|
19771
|
+
if (method.name !== "main") return false;
|
|
19772
|
+
const params = method.parameters ?? [];
|
|
19773
|
+
if (params.length !== 1) return false;
|
|
19774
|
+
const t = (params[0].type ?? "").replace(/\s+/g, "");
|
|
19775
|
+
return t === "String[]" || t === "String..." || t === "java.lang.String[]";
|
|
19776
|
+
}
|
|
19777
|
+
function methodIsSupertypeLifecycleEntryPoint(method, enclosingType) {
|
|
19778
|
+
if (!method.name) return false;
|
|
19779
|
+
if (!enclosingType) return false;
|
|
19780
|
+
const candidates = [];
|
|
19781
|
+
if (enclosingType.extends) candidates.push(simpleTypeName(enclosingType.extends));
|
|
19782
|
+
for (const i2 of enclosingType.implements ?? []) candidates.push(simpleTypeName(i2));
|
|
19783
|
+
for (const supertype of candidates) {
|
|
19784
|
+
const lifecycleMethods = TIER_1_BY_SUPERTYPE.get(supertype);
|
|
19785
|
+
if (lifecycleMethods?.has(method.name)) return true;
|
|
19786
|
+
}
|
|
19787
|
+
return false;
|
|
19788
|
+
}
|
|
19789
|
+
function classifyEntryPointTier(method, enclosingType, ctx) {
|
|
19790
|
+
const language = (ctx.language ?? "").toLowerCase();
|
|
19791
|
+
if (language !== "java") return "TIER_UNKNOWN";
|
|
19792
|
+
if (!method) return "TIER_UNKNOWN";
|
|
19793
|
+
if (classShapeIsLibraryFacade(enclosingType)) {
|
|
19794
|
+
return "TIER_3_LIBRARY_API";
|
|
19795
|
+
}
|
|
19796
|
+
if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS)) {
|
|
19797
|
+
return "TIER_1_ENTRY_POINT";
|
|
19798
|
+
}
|
|
19799
|
+
if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS)) {
|
|
19800
|
+
return "TIER_1_ENTRY_POINT";
|
|
19801
|
+
}
|
|
19802
|
+
if (methodIsSupertypeLifecycleEntryPoint(method, enclosingType)) {
|
|
19803
|
+
return "TIER_1_ENTRY_POINT";
|
|
19804
|
+
}
|
|
19805
|
+
if (looksLikeMainMethod(method)) {
|
|
19806
|
+
return "TIER_1_ENTRY_POINT";
|
|
19807
|
+
}
|
|
19808
|
+
return "TIER_3_LIBRARY_API";
|
|
19809
|
+
}
|
|
19810
|
+
function shouldGateInterproceduralParam(sourceType, enclosingMethod, enclosingType, ctx) {
|
|
19811
|
+
if (sourceType !== "interprocedural_param") return false;
|
|
19812
|
+
if (!enclosingMethod) return false;
|
|
19813
|
+
const tier = classifyEntryPointTier(enclosingMethod, enclosingType, ctx);
|
|
19814
|
+
return tier === "TIER_3_LIBRARY_API";
|
|
19815
|
+
}
|
|
19816
|
+
|
|
19556
19817
|
// src/analysis/project-profile-transform.ts
|
|
19557
19818
|
var DOWNGRADE_ELIGIBLE_RULE_IDS = /* @__PURE__ */ new Set([
|
|
19558
19819
|
"code_injection",
|
|
@@ -30818,7 +31079,7 @@ var REFLECTION_SINK_METHODS = /* @__PURE__ */ new Set([
|
|
|
30818
31079
|
"loadClass",
|
|
30819
31080
|
"defineClass"
|
|
30820
31081
|
]);
|
|
30821
|
-
var
|
|
31082
|
+
var TIER_1_CLASS_ANNOTATIONS2 = /* @__PURE__ */ new Set([
|
|
30822
31083
|
// Spring MVC
|
|
30823
31084
|
"RestController",
|
|
30824
31085
|
"Controller",
|
|
@@ -30835,7 +31096,7 @@ var TIER_1_CLASS_ANNOTATIONS = /* @__PURE__ */ new Set([
|
|
|
30835
31096
|
// Declarative HTTP client
|
|
30836
31097
|
"FeignClient"
|
|
30837
31098
|
]);
|
|
30838
|
-
var
|
|
31099
|
+
var TIER_1_METHOD_ANNOTATIONS2 = /* @__PURE__ */ new Set([
|
|
30839
31100
|
// Spring MVC
|
|
30840
31101
|
"RequestMapping",
|
|
30841
31102
|
"GetMapping",
|
|
@@ -30936,7 +31197,7 @@ var CliMainReflectionSuppressPass = class {
|
|
|
30936
31197
|
let hasFrameworkSignal = false;
|
|
30937
31198
|
for (const type of types) {
|
|
30938
31199
|
for (const ann of type.annotations) {
|
|
30939
|
-
if (
|
|
31200
|
+
if (TIER_1_CLASS_ANNOTATIONS2.has(normalizeAnnotation(ann))) {
|
|
30940
31201
|
hasFrameworkSignal = true;
|
|
30941
31202
|
break;
|
|
30942
31203
|
}
|
|
@@ -30955,7 +31216,7 @@ var CliMainReflectionSuppressPass = class {
|
|
|
30955
31216
|
if (hasFrameworkSignal) break;
|
|
30956
31217
|
for (const method of type.methods) {
|
|
30957
31218
|
for (const ann of method.annotations) {
|
|
30958
|
-
if (
|
|
31219
|
+
if (TIER_1_METHOD_ANNOTATIONS2.has(normalizeAnnotation(ann))) {
|
|
30959
31220
|
hasFrameworkSignal = true;
|
|
30960
31221
|
break;
|
|
30961
31222
|
}
|
|
@@ -31044,21 +31305,168 @@ var LibraryProfileSinkGatePass = class {
|
|
|
31044
31305
|
};
|
|
31045
31306
|
}
|
|
31046
31307
|
};
|
|
31047
|
-
|
|
31048
|
-
|
|
31049
|
-
|
|
31050
|
-
|
|
31308
|
+
var CWE22_SPECULATIVE_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
31309
|
+
"interprocedural_param",
|
|
31310
|
+
"constructor_field"
|
|
31311
|
+
]);
|
|
31312
|
+
var LibraryProfileCwe22PathGatePass = class {
|
|
31313
|
+
name = "library-profile-cwe22-path-gate";
|
|
31051
31314
|
category = "security";
|
|
31052
31315
|
run(ctx) {
|
|
31053
31316
|
const { graph } = ctx;
|
|
31054
|
-
const
|
|
31055
|
-
|
|
31056
|
-
|
|
31057
|
-
|
|
31058
|
-
|
|
31059
|
-
|
|
31060
|
-
|
|
31061
|
-
|
|
31317
|
+
const profile = graph.ir.meta.projectProfile;
|
|
31318
|
+
if (!isLibraryShape2(profile)) {
|
|
31319
|
+
return {
|
|
31320
|
+
profile,
|
|
31321
|
+
applied: false,
|
|
31322
|
+
dropped: 0,
|
|
31323
|
+
droppedBySourceType: {}
|
|
31324
|
+
};
|
|
31325
|
+
}
|
|
31326
|
+
const flows = graph.ir.taint.flows;
|
|
31327
|
+
if (!flows || flows.length === 0) {
|
|
31328
|
+
return {
|
|
31329
|
+
profile,
|
|
31330
|
+
applied: true,
|
|
31331
|
+
dropped: 0,
|
|
31332
|
+
droppedBySourceType: {}
|
|
31333
|
+
};
|
|
31334
|
+
}
|
|
31335
|
+
const droppedBySourceType = {};
|
|
31336
|
+
const kept = [];
|
|
31337
|
+
for (const flow of flows) {
|
|
31338
|
+
if (flow.sink_type === "path_traversal" && CWE22_SPECULATIVE_SOURCE_TYPES.has(flow.source_type)) {
|
|
31339
|
+
droppedBySourceType[flow.source_type] = (droppedBySourceType[flow.source_type] ?? 0) + 1;
|
|
31340
|
+
continue;
|
|
31341
|
+
}
|
|
31342
|
+
kept.push(flow);
|
|
31343
|
+
}
|
|
31344
|
+
const dropped = flows.length - kept.length;
|
|
31345
|
+
if (dropped > 0) {
|
|
31346
|
+
flows.length = 0;
|
|
31347
|
+
flows.push(...kept);
|
|
31348
|
+
}
|
|
31349
|
+
return {
|
|
31350
|
+
profile,
|
|
31351
|
+
applied: true,
|
|
31352
|
+
dropped,
|
|
31353
|
+
droppedBySourceType
|
|
31354
|
+
};
|
|
31355
|
+
}
|
|
31356
|
+
};
|
|
31357
|
+
|
|
31358
|
+
// src/analysis/passes/library-profile-xss-gate-pass.ts
|
|
31359
|
+
var XSS_NON_HTML_OUTPUT_CLASSES = /* @__PURE__ */ new Set([
|
|
31360
|
+
// In-memory buffers — no HTML output surface.
|
|
31361
|
+
"StringBuilder",
|
|
31362
|
+
"StringBuffer",
|
|
31363
|
+
"CharArrayWriter",
|
|
31364
|
+
"ByteArrayOutputStream",
|
|
31365
|
+
// CLI stdio — CLI apps, not web response bodies.
|
|
31366
|
+
"PrintStream",
|
|
31367
|
+
"System",
|
|
31368
|
+
// HTTP client builders — these are taint SOURCES (outbound reads),
|
|
31369
|
+
// not XSS sinks. `response.body()`, `.post()`, `.get()` on
|
|
31370
|
+
// hutool `HttpRequest` / `HttpResponse` reach us as sink
|
|
31371
|
+
// matches only because xss.yaml catches String-valued receivers.
|
|
31372
|
+
"HttpRequest",
|
|
31373
|
+
"HttpRequestBuilder",
|
|
31374
|
+
"HttpResponse",
|
|
31375
|
+
// Servlet non-body IO. `HttpSession.setAttribute`, `HttpSession.putValue`,
|
|
31376
|
+
// `HttpServletRequest.setAttribute` are session/request attribute
|
|
31377
|
+
// IO, not HTML output. Under `library/*` there is no JSP renderer
|
|
31378
|
+
// reflecting them back. `HttpServletResponse` itself is NOT on
|
|
31379
|
+
// this list — its writers are genuine XSS sinks.
|
|
31380
|
+
"HttpSession",
|
|
31381
|
+
"ServletRequest",
|
|
31382
|
+
"HttpServletRequest",
|
|
31383
|
+
// Wire-protocol writers (jedis internal).
|
|
31384
|
+
"RedisOutputStream",
|
|
31385
|
+
"SafeEncoder",
|
|
31386
|
+
"RESP2",
|
|
31387
|
+
"Protocol",
|
|
31388
|
+
// JSON parsers — these read JSON into POJOs. Source, not sink.
|
|
31389
|
+
"JSONUtil",
|
|
31390
|
+
"JSON",
|
|
31391
|
+
"ObjectMapper",
|
|
31392
|
+
"JsonReader",
|
|
31393
|
+
// Loggers — log injection is CWE-117 (already covered by #112);
|
|
31394
|
+
// these appear here because xss.yaml has a String-valued catch-all
|
|
31395
|
+
// that hits Logger receivers.
|
|
31396
|
+
"Logger",
|
|
31397
|
+
"LoggerFactory",
|
|
31398
|
+
"Log",
|
|
31399
|
+
"Slf4jLogger",
|
|
31400
|
+
// Router / interceptor context stores. Zuul `RequestContext`,
|
|
31401
|
+
// Sentinel `Context` are internal request-processing state, not
|
|
31402
|
+
// HTML output.
|
|
31403
|
+
"RequestContext",
|
|
31404
|
+
"Context"
|
|
31405
|
+
]);
|
|
31406
|
+
function isLibraryShape3(profile) {
|
|
31407
|
+
if (!profile || profile === "unknown") return false;
|
|
31408
|
+
return profile.startsWith("library/");
|
|
31409
|
+
}
|
|
31410
|
+
var LibraryProfileXssGatePass = class {
|
|
31411
|
+
name = "library-profile-xss-gate";
|
|
31412
|
+
category = "security";
|
|
31413
|
+
run(ctx) {
|
|
31414
|
+
const { graph } = ctx;
|
|
31415
|
+
const profile = graph.ir.meta.projectProfile;
|
|
31416
|
+
if (!isLibraryShape3(profile)) {
|
|
31417
|
+
return {
|
|
31418
|
+
profile,
|
|
31419
|
+
applied: false,
|
|
31420
|
+
dropped: 0,
|
|
31421
|
+
droppedByClass: {}
|
|
31422
|
+
};
|
|
31423
|
+
}
|
|
31424
|
+
const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
|
|
31425
|
+
if (sinks.length === 0) {
|
|
31426
|
+
return {
|
|
31427
|
+
profile,
|
|
31428
|
+
applied: true,
|
|
31429
|
+
dropped: 0,
|
|
31430
|
+
droppedByClass: {}
|
|
31431
|
+
};
|
|
31432
|
+
}
|
|
31433
|
+
const droppedByClass = {};
|
|
31434
|
+
const kept = [];
|
|
31435
|
+
for (const sink of sinks) {
|
|
31436
|
+
if (sink.type === "xss" && sink.class && XSS_NON_HTML_OUTPUT_CLASSES.has(sink.class)) {
|
|
31437
|
+
droppedByClass[sink.class] = (droppedByClass[sink.class] ?? 0) + 1;
|
|
31438
|
+
continue;
|
|
31439
|
+
}
|
|
31440
|
+
kept.push(sink);
|
|
31441
|
+
}
|
|
31442
|
+
const dropped = sinks.length - kept.length;
|
|
31443
|
+
if (dropped > 0) {
|
|
31444
|
+
sinks.length = 0;
|
|
31445
|
+
sinks.push(...kept);
|
|
31446
|
+
}
|
|
31447
|
+
return {
|
|
31448
|
+
profile,
|
|
31449
|
+
applied: true,
|
|
31450
|
+
dropped,
|
|
31451
|
+
droppedByClass
|
|
31452
|
+
};
|
|
31453
|
+
}
|
|
31454
|
+
};
|
|
31455
|
+
|
|
31456
|
+
// src/analysis/passes/taint-propagation-pass.ts
|
|
31457
|
+
var TaintPropagationPass = class {
|
|
31458
|
+
name = "taint-propagation";
|
|
31459
|
+
category = "security";
|
|
31460
|
+
run(ctx) {
|
|
31461
|
+
const { graph } = ctx;
|
|
31462
|
+
const { calls, types } = graph.ir;
|
|
31463
|
+
const constProp = ctx.getResult("constant-propagation");
|
|
31464
|
+
const sinkFilter = ctx.getResult("sink-filter");
|
|
31465
|
+
const { sources, sinks, sanitizers } = sinkFilter;
|
|
31466
|
+
if (sinks.length === 0) {
|
|
31467
|
+
return { flows: [] };
|
|
31468
|
+
}
|
|
31469
|
+
const canSynthesize = ctx.language === "python" && typeof ctx.code === "string";
|
|
31062
31470
|
if (sources.length === 0 && !canSynthesize) {
|
|
31063
31471
|
return { flows: [] };
|
|
31064
31472
|
}
|
|
@@ -31835,238 +32243,6 @@ function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
|
|
|
31835
32243
|
return sources[0];
|
|
31836
32244
|
}
|
|
31837
32245
|
|
|
31838
|
-
// src/analysis/entry-point-detection.ts
|
|
31839
|
-
var TIER_1_METHOD_ANNOTATIONS2 = /* @__PURE__ */ new Set([
|
|
31840
|
-
// Spring MVC
|
|
31841
|
-
"RequestMapping",
|
|
31842
|
-
"GetMapping",
|
|
31843
|
-
"PostMapping",
|
|
31844
|
-
"PutMapping",
|
|
31845
|
-
"DeleteMapping",
|
|
31846
|
-
"PatchMapping",
|
|
31847
|
-
// Spring messaging / WebSocket
|
|
31848
|
-
"MessageMapping",
|
|
31849
|
-
"SubscribeMapping",
|
|
31850
|
-
// Spring messaging listeners
|
|
31851
|
-
"KafkaListener",
|
|
31852
|
-
"KafkaHandler",
|
|
31853
|
-
"RabbitListener",
|
|
31854
|
-
"RabbitHandler",
|
|
31855
|
-
"JmsListener",
|
|
31856
|
-
"StreamListener",
|
|
31857
|
-
// Spring Cloud AWS
|
|
31858
|
-
"SqsListener",
|
|
31859
|
-
"SqsHandler",
|
|
31860
|
-
// Spring application events (borderline — inclusive in ship 1)
|
|
31861
|
-
"EventListener",
|
|
31862
|
-
// CRON / scheduled
|
|
31863
|
-
"Scheduled",
|
|
31864
|
-
// JAX-RS
|
|
31865
|
-
"Path",
|
|
31866
|
-
"GET",
|
|
31867
|
-
"POST",
|
|
31868
|
-
"PUT",
|
|
31869
|
-
"DELETE",
|
|
31870
|
-
"PATCH",
|
|
31871
|
-
"HEAD",
|
|
31872
|
-
"OPTIONS",
|
|
31873
|
-
// Jenkins Stapler form-binding (#224 — CVE-2022-20617 docker-commons).
|
|
31874
|
-
// `@DataBoundConstructor` / `@DataBoundSetter` mark the trust boundary
|
|
31875
|
-
// between Jenkins UI (or `config.xml` unmarshal) and the plugin: the
|
|
31876
|
-
// Stapler runtime invokes the annotated constructor / setter with
|
|
31877
|
-
// user-supplied strings from a submitted form or a persisted job
|
|
31878
|
-
// config. Every parameter is a user-supplied taint source.
|
|
31879
|
-
"DataBoundConstructor",
|
|
31880
|
-
"DataBoundSetter"
|
|
31881
|
-
]);
|
|
31882
|
-
var TIER_1_CLASS_ANNOTATIONS2 = /* @__PURE__ */ new Set([
|
|
31883
|
-
// Spring MVC
|
|
31884
|
-
"RestController",
|
|
31885
|
-
"Controller",
|
|
31886
|
-
// Spring stereotype beans (#136 — see header)
|
|
31887
|
-
"Service",
|
|
31888
|
-
"Repository",
|
|
31889
|
-
"Component",
|
|
31890
|
-
// JAX-RS resource class
|
|
31891
|
-
"Path",
|
|
31892
|
-
// Servlet 3.0 annotation-based servlet
|
|
31893
|
-
"WebServlet",
|
|
31894
|
-
// JSR-356 WebSocket endpoint
|
|
31895
|
-
"ServerEndpoint",
|
|
31896
|
-
// Declarative HTTP client (borderline — inclusive in ship 1; contract-defined
|
|
31897
|
-
// parameter shapes are downstream-relevant trust boundaries)
|
|
31898
|
-
"FeignClient"
|
|
31899
|
-
]);
|
|
31900
|
-
var TIER_1_BY_SUPERTYPE = /* @__PURE__ */ new Map([
|
|
31901
|
-
// Servlet API
|
|
31902
|
-
["HttpServlet", /* @__PURE__ */ new Set(["doGet", "doPost", "doPut", "doDelete", "doHead", "doOptions", "doTrace", "service"])],
|
|
31903
|
-
["GenericServlet", /* @__PURE__ */ new Set(["service"])],
|
|
31904
|
-
["Filter", /* @__PURE__ */ new Set(["doFilter"])],
|
|
31905
|
-
// Spring web
|
|
31906
|
-
["HandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion"])],
|
|
31907
|
-
["AsyncHandlerInterceptor", /* @__PURE__ */ new Set(["preHandle", "postHandle", "afterCompletion", "afterConcurrentHandlingStarted"])],
|
|
31908
|
-
// Spring boot CLI entry points
|
|
31909
|
-
["CommandLineRunner", /* @__PURE__ */ new Set(["run"])],
|
|
31910
|
-
["ApplicationRunner", /* @__PURE__ */ new Set(["run"])],
|
|
31911
|
-
// Netty channel handlers (#154 — CVE-2022-26884 dolphinscheduler).
|
|
31912
|
-
// `SimpleChannelInboundHandler<T>.channelRead0(ctx, msg)` is the
|
|
31913
|
-
// standard typed read entry; `ChannelInboundHandler.channelRead(ctx,
|
|
31914
|
-
// Object)` is the untyped base. `ChannelInboundHandlerAdapter` and
|
|
31915
|
-
// `ChannelDuplexHandler` are the most-common adapter classes user
|
|
31916
|
-
// handlers extend. `NettyRequestProcessor` is the dolphinscheduler-
|
|
31917
|
-
// specific (and other Netty-RPC projects') wire-message processor
|
|
31918
|
-
// surface — `process(channel, command)` is the network entry point.
|
|
31919
|
-
["SimpleChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead0", "messageReceived"])],
|
|
31920
|
-
["ChannelInboundHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
|
|
31921
|
-
["ChannelInboundHandlerAdapter", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
|
|
31922
|
-
["ChannelDuplexHandler", /* @__PURE__ */ new Set(["channelRead", "channelReadComplete"])],
|
|
31923
|
-
["NettyRequestProcessor", /* @__PURE__ */ new Set(["process"])],
|
|
31924
|
-
// XStream deserialization converters (#224 — CVE-2020-26217,
|
|
31925
|
-
// CVE-2021-21345). The xstream deserializer invokes `unmarshal`
|
|
31926
|
-
// with attacker-controlled `HierarchicalStreamReader` state whenever
|
|
31927
|
-
// untrusted XML reaches `XStream.fromXML`. Each converter is a
|
|
31928
|
-
// deserialization gadget surface — its `unmarshal` parameters are
|
|
31929
|
-
// network-facing taint sources even though the enclosing class
|
|
31930
|
-
// carries no framework annotation. `marshal` is included for the
|
|
31931
|
-
// symmetric round-trip surface but is rarely exploitable on its
|
|
31932
|
-
// own; the fixup targets `unmarshal` primarily.
|
|
31933
|
-
//
|
|
31934
|
-
// `SingleValueConverter` is the string-form variant (fromString
|
|
31935
|
-
// parses an attacker string; toString is the sink half of the
|
|
31936
|
-
// round-trip). `ConverterMatcher` is the shared base and some
|
|
31937
|
-
// downstream projects subclass it directly.
|
|
31938
|
-
["Converter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
|
|
31939
|
-
["SingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
|
|
31940
|
-
["ConverterMatcher", /* @__PURE__ */ new Set(["marshal", "unmarshal"])],
|
|
31941
|
-
// XStream abstract base classes — direct-parent `extends` match
|
|
31942
|
-
// covers the common shape where user converters subclass a base
|
|
31943
|
-
// rather than implementing `Converter` directly.
|
|
31944
|
-
["AbstractReflectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal", "doMarshal", "doUnmarshal"])],
|
|
31945
|
-
["AbstractSingleValueConverter", /* @__PURE__ */ new Set(["fromString", "toString"])],
|
|
31946
|
-
["AbstractCollectionConverter", /* @__PURE__ */ new Set(["marshal", "unmarshal"])]
|
|
31947
|
-
]);
|
|
31948
|
-
var TIER_3_CLASS_SUFFIXES = [
|
|
31949
|
-
"Util",
|
|
31950
|
-
"Utils",
|
|
31951
|
-
"Helper",
|
|
31952
|
-
"Helpers"
|
|
31953
|
-
];
|
|
31954
|
-
var TIER_3_PACKAGE_FRAGMENTS = [
|
|
31955
|
-
".template.",
|
|
31956
|
-
".templates.",
|
|
31957
|
-
".engine.",
|
|
31958
|
-
".engines."
|
|
31959
|
-
];
|
|
31960
|
-
var TIER_3_JDK_FACADE_INTERFACES = /* @__PURE__ */ new Set([
|
|
31961
|
-
// Collection root + common containers
|
|
31962
|
-
"Collection",
|
|
31963
|
-
"List",
|
|
31964
|
-
"Set",
|
|
31965
|
-
"Map",
|
|
31966
|
-
"Queue",
|
|
31967
|
-
"Deque",
|
|
31968
|
-
"SortedSet",
|
|
31969
|
-
"SortedMap",
|
|
31970
|
-
"NavigableSet",
|
|
31971
|
-
"NavigableMap",
|
|
31972
|
-
// Iteration / ordering / equality contracts
|
|
31973
|
-
"Iterator",
|
|
31974
|
-
"Iterable",
|
|
31975
|
-
"ListIterator",
|
|
31976
|
-
"Comparator",
|
|
31977
|
-
"Comparable",
|
|
31978
|
-
// Serialization / cloning contracts
|
|
31979
|
-
"Serializable",
|
|
31980
|
-
"Externalizable",
|
|
31981
|
-
"Cloneable"
|
|
31982
|
-
]);
|
|
31983
|
-
function classNameLooksLikeUtility(name2) {
|
|
31984
|
-
if (!name2) return false;
|
|
31985
|
-
for (const suffix of TIER_3_CLASS_SUFFIXES) {
|
|
31986
|
-
if (name2.length > suffix.length && name2.endsWith(suffix)) return true;
|
|
31987
|
-
}
|
|
31988
|
-
return false;
|
|
31989
|
-
}
|
|
31990
|
-
function packageLooksLikeTemplateOrEngine(pkg) {
|
|
31991
|
-
if (!pkg) return false;
|
|
31992
|
-
const padded = `.${pkg}.`;
|
|
31993
|
-
for (const frag of TIER_3_PACKAGE_FRAGMENTS) {
|
|
31994
|
-
if (padded.includes(frag)) return true;
|
|
31995
|
-
}
|
|
31996
|
-
return false;
|
|
31997
|
-
}
|
|
31998
|
-
function implementsJdkFacade(t) {
|
|
31999
|
-
if (!t) return false;
|
|
32000
|
-
for (const impl of t.implements ?? []) {
|
|
32001
|
-
if (TIER_3_JDK_FACADE_INTERFACES.has(simpleTypeName(impl))) return true;
|
|
32002
|
-
}
|
|
32003
|
-
return false;
|
|
32004
|
-
}
|
|
32005
|
-
function classShapeIsLibraryFacade(enclosingType) {
|
|
32006
|
-
if (!enclosingType) return false;
|
|
32007
|
-
if (classNameLooksLikeUtility(enclosingType.name)) return true;
|
|
32008
|
-
if (packageLooksLikeTemplateOrEngine(enclosingType.package)) return true;
|
|
32009
|
-
if (implementsJdkFacade(enclosingType)) return true;
|
|
32010
|
-
return false;
|
|
32011
|
-
}
|
|
32012
|
-
function annotationsInclude(annotations, targets) {
|
|
32013
|
-
if (!annotations || annotations.length === 0) return false;
|
|
32014
|
-
for (const raw of annotations) {
|
|
32015
|
-
const simple = raw.replace(/^@/, "").replace(/[<(].*$/, "").trim();
|
|
32016
|
-
if (targets.has(simple)) return true;
|
|
32017
|
-
}
|
|
32018
|
-
return false;
|
|
32019
|
-
}
|
|
32020
|
-
function simpleTypeName(ref) {
|
|
32021
|
-
return ref.replace(/<.*$/, "").trim();
|
|
32022
|
-
}
|
|
32023
|
-
function looksLikeMainMethod(method) {
|
|
32024
|
-
if (method.name !== "main") return false;
|
|
32025
|
-
const params = method.parameters ?? [];
|
|
32026
|
-
if (params.length !== 1) return false;
|
|
32027
|
-
const t = (params[0].type ?? "").replace(/\s+/g, "");
|
|
32028
|
-
return t === "String[]" || t === "String..." || t === "java.lang.String[]";
|
|
32029
|
-
}
|
|
32030
|
-
function methodIsSupertypeLifecycleEntryPoint(method, enclosingType) {
|
|
32031
|
-
if (!method.name) return false;
|
|
32032
|
-
if (!enclosingType) return false;
|
|
32033
|
-
const candidates = [];
|
|
32034
|
-
if (enclosingType.extends) candidates.push(simpleTypeName(enclosingType.extends));
|
|
32035
|
-
for (const i2 of enclosingType.implements ?? []) candidates.push(simpleTypeName(i2));
|
|
32036
|
-
for (const supertype of candidates) {
|
|
32037
|
-
const lifecycleMethods = TIER_1_BY_SUPERTYPE.get(supertype);
|
|
32038
|
-
if (lifecycleMethods?.has(method.name)) return true;
|
|
32039
|
-
}
|
|
32040
|
-
return false;
|
|
32041
|
-
}
|
|
32042
|
-
function classifyEntryPointTier(method, enclosingType, ctx) {
|
|
32043
|
-
const language = (ctx.language ?? "").toLowerCase();
|
|
32044
|
-
if (language !== "java") return "TIER_UNKNOWN";
|
|
32045
|
-
if (!method) return "TIER_UNKNOWN";
|
|
32046
|
-
if (classShapeIsLibraryFacade(enclosingType)) {
|
|
32047
|
-
return "TIER_3_LIBRARY_API";
|
|
32048
|
-
}
|
|
32049
|
-
if (annotationsInclude(method.annotations, TIER_1_METHOD_ANNOTATIONS2)) {
|
|
32050
|
-
return "TIER_1_ENTRY_POINT";
|
|
32051
|
-
}
|
|
32052
|
-
if (enclosingType && annotationsInclude(enclosingType.annotations, TIER_1_CLASS_ANNOTATIONS2)) {
|
|
32053
|
-
return "TIER_1_ENTRY_POINT";
|
|
32054
|
-
}
|
|
32055
|
-
if (methodIsSupertypeLifecycleEntryPoint(method, enclosingType)) {
|
|
32056
|
-
return "TIER_1_ENTRY_POINT";
|
|
32057
|
-
}
|
|
32058
|
-
if (looksLikeMainMethod(method)) {
|
|
32059
|
-
return "TIER_1_ENTRY_POINT";
|
|
32060
|
-
}
|
|
32061
|
-
return "TIER_3_LIBRARY_API";
|
|
32062
|
-
}
|
|
32063
|
-
function shouldGateInterproceduralParam(sourceType, enclosingMethod, enclosingType, ctx) {
|
|
32064
|
-
if (sourceType !== "interprocedural_param") return false;
|
|
32065
|
-
if (!enclosingMethod) return false;
|
|
32066
|
-
const tier = classifyEntryPointTier(enclosingMethod, enclosingType, ctx);
|
|
32067
|
-
return tier === "TIER_3_LIBRARY_API";
|
|
32068
|
-
}
|
|
32069
|
-
|
|
32070
32246
|
// src/analysis/passes/interprocedural-pass.ts
|
|
32071
32247
|
var InterproceduralPass = class {
|
|
32072
32248
|
name = "interprocedural";
|
|
@@ -40743,10 +40919,14 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
40743
40919
|
pipeline.add(new CliMainReflectionSuppressPass());
|
|
40744
40920
|
if (!disabledPasses.has("library-profile-sink-gate"))
|
|
40745
40921
|
pipeline.add(new LibraryProfileSinkGatePass());
|
|
40922
|
+
if (!disabledPasses.has("library-profile-xss-gate"))
|
|
40923
|
+
pipeline.add(new LibraryProfileXssGatePass());
|
|
40746
40924
|
pipeline.add(new TaintPropagationPass());
|
|
40747
40925
|
pipeline.add(new InterproceduralPass({
|
|
40748
40926
|
enableEntryPointGate: options.enableEntryPointGate ?? true
|
|
40749
40927
|
}));
|
|
40928
|
+
if (!disabledPasses.has("library-profile-cwe22-path-gate"))
|
|
40929
|
+
pipeline.add(new LibraryProfileCwe22PathGatePass());
|
|
40750
40930
|
if (!disabledPasses.has("scan-secrets")) pipeline.add(new ScanSecretsPass());
|
|
40751
40931
|
if (!disabledPasses.has("dead-code")) pipeline.add(new DeadCodePass());
|
|
40752
40932
|
if (!disabledPasses.has("missing-await")) pipeline.add(new MissingAwaitPass());
|