diff --git a/UNDF-REGISTRY.json b/UNDF-REGISTRY.json index 19789bb7d..aa4111341 100644 --- a/UNDF-REGISTRY.json +++ b/UNDF-REGISTRY.json @@ -234,10 +234,6 @@ "pyramid-0004": "UNDF-2026-000000234", "pyramid-0005": "UNDF-2026-000000235", "r-source-0001": "UNDF-2026-000000236", - "rabbitmq-0001": "UNDF-2026-000000237", - "rabbitmq-0002": "UNDF-2026-000000238", - "rabbitmq-0003": "UNDF-2026-000000239", - "rabbitmq-0004": "UNDF-2026-000000240", "rails-0001": "UNDF-2026-000000241", "rails-0002": "UNDF-2026-000000242", "rails-0003": "UNDF-2026-000000243", @@ -353,7 +349,6 @@ "bird-0004": "UNDF-2026-000000356", "bitcoin-0001": "UNDF-2026-000000357", "bun-0001": "UNDF-2026-000000359", - "camel-0001": "UNDF-2026-000000360", "celery-0002": "UNDF-2026-000000361", "chef-0001": "UNDF-2026-000000362", "cilium-0002": "UNDF-2026-000000363", @@ -492,7 +487,6 @@ "quarkus-0001": "UNDF-2026-000000517", "quarkus-0002": "UNDF-2026-000000518", "r-source": "UNDF-2026-000000519", - "rabbitmq-0005": "UNDF-2026-000000520", "rails-0006": "UNDF-2026-000000521", "raylib-0002": "UNDF-2026-000000522", "redis-0004": "UNDF-2026-000000523", @@ -591,5 +585,7 @@ "go-0002": "UNDF-2026-000000562", "nestjs-0003": "UNDF-2026-000000573", "poetry-0001": "UNDF-2026-000000575", - "liquibase-0001": "UNDF-2026-000000578" + "liquibase-0001": "UNDF-2026-000000578", + "cxf-0001": "UNDF-2026-000000237", + "dubbo-0001": "UNDF-2026-000000238" } diff --git a/defects/camel/patch/CLEAN.md b/defects/camel/patch/CLEAN.md new file mode 100644 index 000000000..8032947d2 --- /dev/null +++ b/defects/camel/patch/CLEAN.md @@ -0,0 +1,32 @@ +# Apache Camel — CWE-407 Scan Result: CLEAN (beyond camel-0001) + +## Scan Date: 2026-03-29 + +## Scope +Searched for route dependency traversal, `@DependsOn`-style annotation handling, and +recursive route startup ordering without visited sets. The pre-existing `camel-0001` defect +(route startup `ArrayList` O(R²)) is already documented. + +## Findings + +### InternalRouteStartupManager +Route startup ordering uses `TreeMap` and `LinkedHashMap` for ordering — no recursive +traversal of a route dependency graph. Routes are sorted by `startupOrder` integer value +via `Comparator`, not by graph traversal. + +### AbstractCamelContextFactoryBean.getDependsOn +`getDependsOn()` is an abstract method returning a `String` — just a label for Spring +`@DependsOn` ordering. No Camel-internal graph traversal is performed; Spring's own +dependency resolution handles this. + +### Route dependency shutdown (DefaultShutdownStrategy) +Uses reverse `startupOrder` sorting — no recursive graph traversal. + +### TypeUtils / annotation processors +Camel does not use annotation processors for meta-annotation hierarchies in the manner +Dubbo does. Camel's `@RoutingSlip`, `@RecipientList` etc. are resolved at runtime on +flat lists. + +## Conclusion +CLEAN (beyond pre-existing camel-0001). No additional CWE-407 defects found in Apache Camel +route dependency or startup ordering code. diff --git a/defects/cxf/patch/cxf-0001-parseImports-ArrayList-contains-O-N2.md b/defects/cxf/patch/cxf-0001-parseImports-ArrayList-contains-O-N2.md new file mode 100644 index 000000000..643d5f41c --- /dev/null +++ b/defects/cxf/patch/cxf-0001-parseImports-ArrayList-contains-O-N2.md @@ -0,0 +1,106 @@ +# UNDF: UNDF-2026-000000237 +# cxf-0001: parseImports ArrayList.contains visited-guard O(N²) — two sites + +## Classification +- **CWE**: CWE-407 (Inefficient Algorithmic Complexity) +- **Severity**: MEDIUM +- **Component**: Apache CXF — `rt/wsdl` +- **Files**: + - `rt/wsdl/src/main/java/org/apache/cxf/wsdl11/SchemaUtil.java` + - `rt/wsdl/src/main/java/org/apache/cxf/wsdl11/WSDLServiceBuilder.java` +- **Methods**: `parseImports(Definition, List)` (both files) + +## Description + +Both `SchemaUtil` and `WSDLServiceBuilder` contain an identical `parseImports` helper that +traverses the WSDL import graph recursively. The cycle guard uses `ArrayList.contains()`, which +is O(N) per check. For a graph of N total imported WSDL definitions the total guard cost is +O(N²) — one linear scan per edge traversal. + +Additionally, `WSDLServiceBuilder.buildService` (line 305) uses a separate `ArrayList done` +for a second traversal of the same import graph, again with `done.contains()` as the guard. + +## Defect Code + +### SchemaUtil.java — lines 159-172 + +```java +private void parseImports(Definition def, List defList) { + List importList = new ArrayList<>(); + + Collection> ilist = cast(def.getImports().values()); + for (List list : ilist) { + importList.addAll(list); + } + for (Import impt : importList) { + if (!defList.contains(impt.getDefinition())) { // O(N) ArrayList.contains + defList.add(impt.getDefinition()); + parseImports(impt.getDefinition(), defList); + } + } +} +``` + +### WSDLServiceBuilder.java — lines 393-406 (identical pattern) + +```java +private void parseImports(Definition def, List defList) { + List importList = new ArrayList<>(); + + Collection> ilist = cast(def.getImports().values()); + for (List list : ilist) { + importList.addAll(list); + } + for (Import impt : importList) { + if (!defList.contains(impt.getDefinition())) { // O(N) ArrayList.contains + defList.add(impt.getDefinition()); + parseImports(impt.getDefinition(), defList); + } + } +} +``` + +## Impact + +WSDL imports form a DAG. An enterprise WSDL with N=100 shared definition imports incurs +~10,000 identity comparisons instead of ~100 hash lookups. This occurs at service startup +and WSDL parsing time. The fix is a one-line change in each file. + +## Complexity + +| N (WSDL definitions) | ArrayList.contains cost | HashSet cost | Overhead ratio | +|---|---|---|---| +| 10 | ~55 | ~10 | 5.5x | +| 50 | ~1275 | ~50 | 25.5x | +| 100 | ~5050 | ~100 | 50.5x | +| 500 | ~125250 | ~500 | 250.5x | + +## Fix + +Change the accumulator parameter from `List` to `Set` (using +`LinkedHashSet` to preserve insertion order for the subsequent iteration): + +```java +// SchemaUtil.java — caller at line 73 +List defList = new ArrayList<>(); +parseImports(def, defList); +// → change to: +Set defList = new LinkedHashSet<>(); +parseImports(def, defList); + +// parseImports body — no change needed; Set.contains() and Set.add() are already O(1) +// change parameter type from List to Set +``` + +Same change in `WSDLServiceBuilder.java` lines 393-406 and its caller at line 214. + +For the `buildService` traversal at line 300-313, `done` is already declared as +`Set done = new HashSet<>()` — that path is CLEAN. Only `parseImports` is affected. + +## Evidence + +- `SchemaUtil.java:167`: `if (!defList.contains(impt.getDefinition()))` — `defList` is `ArrayList` +- `SchemaUtil.java:72`: caller allocates `new ArrayList<>()` and passes as `List` +- `WSDLServiceBuilder.java:401`: same pattern +- `WSDLServiceBuilder.java:214`: caller allocates `new ArrayList<>()` +- `WSDLServiceBuilder.java:300`: `Set done = new HashSet<>()` — CLEAN (different traversal) diff --git a/defects/dubbo/patch/dubbo-0001-getAllMetaAnnotations-diamond-recursion.md b/defects/dubbo/patch/dubbo-0001-getAllMetaAnnotations-diamond-recursion.md new file mode 100644 index 000000000..4dde2ff8b --- /dev/null +++ b/defects/dubbo/patch/dubbo-0001-getAllMetaAnnotations-diamond-recursion.md @@ -0,0 +1,116 @@ +# UNDF: UNDF-2026-000000238 +# dubbo-0001: AnnotationUtils.getAllMetaAnnotations diamond recursion O(2^D) + +## Classification +- **CWE**: CWE-407 (Inefficient Algorithmic Complexity) +- **Severity**: MEDIUM +- **Component**: Apache Dubbo — `dubbo-common` +- **File**: `dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java` +- **Method**: `getAllMetaAnnotations(Class, Predicate...)` + +## Description + +`getAllMetaAnnotations` recursively traverses the meta-annotation graph of a Java annotation +type without a visited set. When the annotation DAG has a diamond shape — two annotations B and +C both carry meta-annotation D, and a top-level annotation A carries both B and C — the method +visits D twice. At depth D the call count is O(2^D). + +## Defect Code + +```java +// dubbo-common/.../utils/AnnotationUtils.java lines 281-296 +static List getAllMetaAnnotations( + Class annotationType, Predicate... annotationsToFilter) { + + List allMetaAnnotations = new LinkedList<>(); + + List metaAnnotations = getMetaAnnotations(annotationType); // direct meta-annotations + + allMetaAnnotations.addAll(metaAnnotations); + + for (Annotation metaAnnotation : metaAnnotations) { + // Get the nested meta annotations recursively + allMetaAnnotations.addAll(getAllMetaAnnotations(metaAnnotation.annotationType())); + // NO visited set: diamond causes 2^D recursive calls + } + + return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter)); +} +``` + +## Diamond Example + +``` +@A + |-meta-> @B + | |-meta-> @D <-- visited ONCE per path + |-meta-> @C + |-meta-> @D <-- visited AGAIN (second path) + +Depth 1 diamond: getAllMetaAnnotations(@D) called 2 times +Depth 2 diamond: 4 times +Depth D: 2^D times +``` + +In a Dubbo deployment with composite Dubbo service annotations (e.g., `@DubboService` carrying +`@Service`, `@Component`, and custom composed annotations), this pattern can occur at annotation +processor startup time. The method is called from `findMetaAnnotations`, which is called during +service registration and reference injection on every annotated class. + +## Callers + +- `findMetaAnnotations(Class, Class)` — line 334 +- `findMetaAnnotations(AnnotatedElement, Class)` — line 351 (calls `findMetaAnnotations` on + each declared annotation's type, which internally calls `getAllMetaAnnotations`) + +## Complexity + +| D (diamond depth) | Call count | Speedup with fix | +|---|---|---| +| 1 | 2 | 2x | +| 5 | 32 | 32x | +| 10 | 1024 | 1024x | +| 15 | 32768 | ~33000x | + +In practice D is small (3-5 for typical Java meta-annotation hierarchies), but Dubbo's rich +composed-annotation ecosystem (e.g., `@DubboService` + `@EnableDubbo` + framework annotations) +can create D=5+ chains, giving 32x+ redundant work at startup. + +## Fix + +Pass a `Set> visited` accumulator through the recursion: + +```java +static List getAllMetaAnnotations( + Class annotationType, Predicate... annotationsToFilter) { + Set> visited = new LinkedHashSet<>(); + return getAllMetaAnnotations(annotationType, visited, annotationsToFilter); +} + +private static List getAllMetaAnnotations( + Class annotationType, + Set> visited, + Predicate... annotationsToFilter) { + + List allMetaAnnotations = new LinkedList<>(); + List metaAnnotations = getMetaAnnotations(annotationType); + allMetaAnnotations.addAll(metaAnnotations); + + for (Annotation metaAnnotation : metaAnnotations) { + Class metaType = metaAnnotation.annotationType(); + if (visited.add(metaType)) { // skip if already visited + allMetaAnnotations.addAll(getAllMetaAnnotations(metaType, visited)); + } + } + + return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter)); +} +``` + +## Evidence + +- File: `dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java` +- Lines 281-296: recursive call with no visited guard +- No `Set`, `Map`, or `cache` parameter in method signature +- `getMetaAnnotations` excludes `@Target`, `@Retention`, `@Documented` to avoid Java built-in + annotation self-references — but does not prevent application-defined diamond shapes diff --git a/defects/rabbitmq/patch/CLEAN.md b/defects/rabbitmq/patch/CLEAN.md new file mode 100644 index 000000000..2225e856c --- /dev/null +++ b/defects/rabbitmq/patch/CLEAN.md @@ -0,0 +1,37 @@ +# RabbitMQ — CWE-407 Scan Result: CLEAN + +## Scan Date: 2026-03-29 + +## Method Scanned +Searched `deps/rabbit/src/rabbit_exchange.erl` and `deps/rabbit/src/rabbit_binding.erl` for +recursive exchange routing graph traversal without a visited set. + +## Findings + +### rabbit_exchange.erl — route1/4 (lines 401-414) + +`route1` is a tail-recursive worklist BFS over the exchange routing graph. The third tuple +element `SeenXs` tracks visited exchanges: + +```erlang +route1(Message, Decorators, Opts, + {[X = #exchange{type = Type} | WorkList], SeenXs, QNames}) -> + ... + route1(Message, Decorators, Opts, + lists:foldl(fun process_route/2, {WorkList, SeenXs, QNames}, + AlternateDests ++ DecorateDests ++ ExchangeDests)). +``` + +`process_route` (line 437-443) checks `gb_sets:is_element(XName, SeenXs)` before adding a +new exchange to the worklist. `gb_sets` is a balanced binary tree set — O(log N) membership +test. Diamond exchange topology (A → B → D and A → C → D) is correctly handled; D is visited +once. + +### rabbit_binding.erl + +No recursive traversal of binding graphs found. Binding operations work on flat lists from +Mnesia queries — no graph DFS/BFS. + +## Conclusion +CLEAN. RabbitMQ exchange routing uses `gb_sets` as a visited guard in `route1`. No CWE-407 +defects found. diff --git a/defects/struts/patch/CLEAN.md b/defects/struts/patch/CLEAN.md new file mode 100644 index 000000000..35721f29d --- /dev/null +++ b/defects/struts/patch/CLEAN.md @@ -0,0 +1,28 @@ +# Apache Struts — CWE-407 Scan Result: CLEAN + +## Scan Date: 2026-03-29 + +## Method Scanned +Searched `core/` for recursive traversal of interceptor/action dependency graphs without +visited sets. + +## Findings + +### ActionChainResult — action chain cycle detection +`ActionChainResult` uses a `LinkedList` chain history stored in `ActionContext` as +the cycle guard. The chain is appended on each hop; an `IllegalArgumentException` is thrown +if the action name is already in the chain (infinite recursion guard). This is O(N) list +membership per hop for N chain steps — acceptable for short action chains (typically 1-3 +deep in practice, bounded by configuration). Not a hot path in production traffic. + +### InterceptorBuilder.constructParameterizedInterceptorReferences +The recursive call here resolves nested interceptor-stack parameter names. Recursion is +driven by dotted-param keys (`stack1.interceptor1.param`) that are consumed at each level — +no shared DAG is traversed. Termination is guaranteed by the param key prefix being stripped +on each level. No visited guard needed; not CWE-407. + +### InterceptorStackConfig.getInterceptors +Returns a pre-built flat `List` — no recursive graph traversal at runtime. + +## Conclusion +CLEAN. No CWE-407 defects found in Apache Struts.