# UNDF: UNDF-2026-000000517
# quarkus-0001: BeanInfo.getBoundInterceptors — O(I²) bound.contains in nested loops

## CWE-407 — Algorithmic Complexity: Linear Membership Test in Loop

| Field        | Value |
|--------------|-------|
| ID           | quarkus-0001 |
| Severity     | HIGH |
| Ecosystem    | quarkus |
| Package      | arc/processor |
| File         | `independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java` |
| Lines        | 493, 500, 522 |
| Complexity   | O(I²) where I = total interceptors across all interception registrations |
| Fix          | Use `LinkedHashSet<InterceptorInfo>` for `bound` collection |

## Description

`getBoundInterceptors()` builds a deduplicated `List<InterceptorInfo> bound = new ArrayList<>()`.
For each `InterceptionInfo` in `lifecycleInterceptors.values()` (outer loop), it iterates
`interception.interceptors` (inner loop) and calls `bound.contains(interceptor)` — O(|bound|) on
ArrayList. The same pattern repeats for `interceptedMethods.values()`.

Similarly, `getBoundDecorators()` at line 522 has the same pattern with `List<DecoratorInfo>`.

```java
List<InterceptorInfo> bound = new ArrayList<>();
for (InterceptionInfo interception : lifecycleInterceptors.values()) {  // O(M)
    for (InterceptorInfo interceptor : interception.interceptors) {     // O(I_per_method)
        if (!bound.contains(interceptor)) {                             // O(|bound|) ArrayList
            bound.add(interceptor);
        }
    }
}
for (InterceptionInfo interception : interceptedMethods.values()) {     // O(M)
    for (InterceptorInfo interceptor : interception.interceptors) {     // O(I_per_method)
        if (!bound.contains(interceptor)) {                             // O(|bound|) ArrayList
            bound.add(interceptor);
        }
    }
}
```

### Compounding Effect

`getBoundInterceptors()` is **not cached** — it recomputes on every call. It is called from:
- `ComponentsProviderGenerator.initBeanDependencyMap()` — in a loop over all beans
- `BeanDeployment` — in loops checking removable interceptors (lines 429, 434)
- `BeanGenerator`, `SubclassGenerator`, `InterceptionProxyGenerator` — multiple hot paths

This means the O(I²) computation is repeated multiple times per bean per build phase.

## Fix

```java
// Before
List<InterceptorInfo> bound = new ArrayList<>();
// dedup via bound.contains()

// After — use LinkedHashSet for O(1) contains, convert to sorted List at end
Set<InterceptorInfo> boundSet = new LinkedHashSet<>();
for (InterceptionInfo interception : lifecycleInterceptors.values()) {
    for (InterceptorInfo interceptor : interception.interceptors) {
        boundSet.add(interceptor);  // Set.add handles dedup, O(1)
    }
}
for (InterceptionInfo interception : interceptedMethods.values()) {
    for (InterceptorInfo interceptor : interception.interceptors) {
        boundSet.add(interceptor);
    }
}
List<InterceptorInfo> bound = new ArrayList<>(boundSet);
Collections.sort(bound);
```

Same pattern for `getBoundDecorators()`.

Additionally, consider caching the result (memoize after `initialize()` is called).

## Speedup Estimate

For a bean with 10 intercepted methods × 5 interceptors each: 50 × 25 average bound size =
1,250 operations → 50. **25x speedup** per call, multiplied by the number of call sites per build.
