# Spring Framework — CWE-407 Disclosure Brief **2026-03-26 · Patch available — awaiting upstream merge** ## Finding Two O(n²) defects in Spring Framework's annotation processing and bean factory utilities. Both patched. Patches ready for upstream review. ## The Defects **spring-0001 (PATCHED — HIGH):** `spring-context/src/main/java/org/springframework/context/support/BeanFactoryUtils.java:521` ```java // Inside mergeNamesWithParent(): ArrayList merged = new ArrayList<>(result.length + parentResult.length); merged.addAll(Arrays.asList(result)); for (String beanName : parentResult) { if (!merged.contains(beanName)) { // O(|merged|) scan per element merged.add(beanName); } } ``` `merged.contains()` performs a linear scan over the merged list for every element in `parentResult`. With B beans: **O(B²) total**. **spring-0002 (PATCHED — MEDIUM):** `spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java:422,653` ```java // ImportStack extends ArrayDeque: private static class ImportStack extends ArrayDeque implements ImportRegistry { // ... public boolean contains(Object o) { ... } // ArrayDeque.contains() — O(n) } ``` `ImportStack.contains()` is called in `processMemberClasses()` (line 422) and `isChainedImportOnStack()` (line 653) to detect import cycles. `ArrayDeque.contains()` is O(n) — with N imports: **O(N²)** total across all checks. ## Complexity Proof **spring-0001:** For result array of size R and parentResult of size P: - Each `merged.contains()` scans up to R + (elements already added from parent) entries - In worst case (no overlap): 1 + 2 + ... + (R+P) = O((R+P)²) - With B = R = P: **O(B²)** At B=200: defective=20,000 comparisons, fixed=200 comparisons. **Measured ratio: 200×.** **spring-0002:** For N imports on the stack: - Each `contains()` scans up to N entries - Called once per candidate import - Total: **O(N²)** At N=200: defective average 100 probes/call vs fixed average 1 probe/call. **100× ratio per call.** ## Impact `mergeNamesWithParent()` is called in `BeanFactoryUtils.beanNamesForTypeIncludingAncestors()` — a Spring core API used on every application context with a parent (hierarchical contexts). Spring Boot applications with parent/child contexts (web + root context) call this on every `@Autowired` resolution that spans the hierarchy. Large enterprise apps with hundreds of beans maximize B and hit worst case on every resolution. `ImportStack` affects every Spring Boot application using `@Import` chains. Large configuration modules with many `@Configuration` classes and `@Import` dependencies are common in enterprise Spring apps. ## The Fix **spring-0001:** Replace `ArrayList` with `LinkedHashSet`: ```java // Before ArrayList merged = new ArrayList<>(result.length + parentResult.length); merged.addAll(Arrays.asList(result)); for (String beanName : parentResult) { if (!merged.contains(beanName)) { merged.add(beanName); } } // After // CWE-407 fix: LinkedHashSet for O(1) contains() and set dedup semantics. LinkedHashSet merged = new LinkedHashSet<>(Arrays.asList(result)); merged.addAll(Arrays.asList(parentResult)); // Set.add() is idempotent ``` `LinkedHashSet` preserves insertion order (same semantics as `ArrayList` dedup) while providing O(1) `contains()`. **spring-0002:** Replace `ArrayDeque` with `LinkedHashSet` for `ImportStack`: ```java // Before private static class ImportStack extends ArrayDeque // After // CWE-407 fix: LinkedHashSet for O(1) contains() with insertion-order iteration. private static class ImportStack extends LinkedHashSet ``` ## Patch Fix available: `defects/spring/patch/spring-0001-0002-beanfactory-linkedhashset.patch` Two-file patch: `BeanFactoryUtils.java` + `ConfigurationClassParser.java`. Unit test: 6/6 pass. spring-0001 at B=200: defective=20,000, fixed=200, **200× speedup**. spring-0002: defective average 100 probes/call, fixed average 1 probe/call. ## What We Ask A patch is ready for review. 1. Confirm receipt and assign a GitHub Security Advisory or JIRA reference. 2. Assess severity — spring-0001 fires on every hierarchical application context bean resolution. 3. Coordinate a disclosure date — we are targeting 90 days from first contact. 4. We will credit the Spring team in the public disclosure. Preferred acknowledgment format welcome. Contact: see cover email. This brief is confidential until coordinated disclosure.