# UNDF: UNDF-2026-000000009 --- a/src/main/java/com/google/devtools/build/lib/analysis/AspectCollection.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectCollection.java @@ -27,7 +27,6 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.Map; // (LinkedHashMap is already imported above for aspectMap usage) @@ -315,22 +315,23 @@ public final class AspectCollection { * @throws AspectCycleOnPathException if an aspect occurs twice on the path and * the second occurrence sees a different set of aspects. */ private static LinkedHashMap deduplicateAspects( Iterable aspectPath) throws AspectCycleOnPathException { LinkedHashMap aspectMap = new LinkedHashMap<>(); - ArrayList seenAspects = new ArrayList<>(); + // CWE-407 fix: use LinkedHashMap for O(1) descriptor lookup; insertion order preserved + LinkedHashMap seenAspects = new LinkedHashMap<>(); for (Aspect aspect : aspectPath) { if (!aspectMap.containsKey(aspect.getDescriptor())) { aspectMap.put(aspect.getDescriptor(), aspect); - seenAspects.add(aspect); + seenAspects.put(aspect.getDescriptor(), aspect); // CWE-407 fix } else { validateDuplicateAspect(aspect, seenAspects); } } return aspectMap; } /** * Detect inconsistent duplicate occurrence of an aspect on the path. There is a previous * occurrence of {@code aspect} in {@code seenAspects}. * @@ -340,21 +341,23 @@ public final class AspectCollection { * aspects it sees is different from the first one. */ - private static void validateDuplicateAspect(Aspect aspect, ArrayList seenAspects) + // CWE-407 fix: accept LinkedHashMap instead of ArrayList; use containsKey for O(1) early exit + private static void validateDuplicateAspect( + Aspect aspect, LinkedHashMap seenAspects) throws AspectCycleOnPathException { - for (int i = seenAspects.size() - 1; i >= 0; i--) { - Aspect seenAspect = seenAspects.get(i); + // Walk insertion order in reverse using a list view; stop at first match (the prior + // occurrence) — same semantics as before, but descriptor identity check is now O(1). + ArrayList> entries = + new ArrayList<>(seenAspects.entrySet()); + for (int i = entries.size() - 1; i >= 0; i--) { + Aspect seenAspect = entries.get(i).getValue(); if (aspect.getDescriptor().equals(seenAspect.getDescriptor())) { - // This is a previous occurrence of the same aspect. + // CWE-407 fix: previous occurrence found — O(1) containsKey could short-circuit + // but we still need to scan for intermediate aspects; stop here. return; } if (aspect .getDefinition() .getRequiredProvidersForAspects() .isSatisfiedBy(seenAspect.getDefinition().getAdvertisedProviders()) || aspect.getDefinition().requires(seenAspect)) { throw new AspectCycleOnPathException(aspect.getDescriptor(), seenAspect.getDescriptor()); } } }