java-topology/defects/groovy/patch/groovy-diamond-recursion-CLEAN.md
russell@unturf.com 61a717bfcc diamond-scan-deeper: kotlin/scala3/groovy/ghc O(2^D) hierarchy traversal — CLEAN + groovy-0001/0002 patches
Kotlin: confirmed CLEAN (DFS.VisitedWithSet throughout, existing marker valid)
Scala3: confirmed CLEAN (BaseDataBuilder.addAll deduplicates, existing marker valid)
GHC: CLEAN — NameSet/UniqSet/ExpansionFuel tracking in all superclass expansion paths
  (checkClassCycles, mk_strict_superclasses, transSuperClasses, closeWrtFunDeps)
Groovy: CLEAN for diamond — getAllInterfaces/collectAllInterfacesReverseOrder/addAllInterfaces
  all use if(set.add(node)) guards (GROOVY-11036)
  groovy-0001/0002 patch docs created (O(N²) membership scans, pre-existing UNDF assigned)
Clojure: CLEAN (set-based BFS, supers/ancestors use set-based worklist)
2026-03-29 20:30:49 -04:00

50 lines
2.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Groovy Compiler — Diamond Recursion CWE-407 Scan: CLEAN
**Pattern:** Recursive O(2^D) traversal of interface/trait hierarchy without visited set
**Scan date:** 2026-03-29
**Scope:** `src/main/java/org/codehaus/groovy/`
## Method
Searched for recursive ClassNode hierarchy traversal without proper `if (set.add())` guards.
Traced all callers of `getInterfaces()`, `getSuperClass()`, `getAllInterfaces()`,
`getInterfacesAndSuperInterfaces()`, and `collectAllInterfacesReverseOrder()`.
## Key candidates reviewed
### `ClassNode.getAllInterfaces()` — private `getAllInterfaces(Set)`
`src/main/java/org/codehaus/groovy/ast/ClassNode.java` lines 441452.
Fixed in GROOVY-11036: now uses `if (set.add(face)) face.getAllInterfaces(set)`.
The guard on `set.add()` return value prevents re-traversal of shared ancestors.
CLEAN (post-fix).
### `GeneralUtils.addAllInterfaces()` / `getInterfacesAndSuperInterfaces()`
`src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java` lines 491518.
Uses `if (result.add(in)) { addAllInterfaces(result, in); }` for interface branches.
Superclass branch is linear (Java single-inheritance), so no diamond possible there.
CLEAN.
### `Traits.collectAllInterfacesReverseOrder()`
`src/main/java/org/codehaus/groovy/transform/trait/Traits.java` lines 307316.
Uses `if (interfaces.add(iNode)) collectAllInterfacesReverseOrder(iNode, interfaces)`.
Correct guard. CLEAN.
### `ResolveVisitor` cycle detection
`src/main/java/org/codehaus/groovy/control/ResolveVisitor.java` lines 13631381.
Uses BFS with `done.add(next)` as the loop guard. CLEAN.
### `WideningCategories.lowestUpperBound()`
`src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java` lines 319447.
Recursion on superclass chain (linear, no diamond). Interface sets pre-computed via
`getInterfacesAndSuperInterfaces()` which is guarded. CLEAN.
## Verdict
CLEAN for O(2^D) diamond recursion. Groovy uses `if (set.add(node))` guards on all
recursive interface hierarchy traversal. The two existing defects (groovy-0001,
groovy-0002) are O(N²) list-membership scans, not diamond recursion.