# UNDF: UNDF-2026-000000602
# UNDF: (pending)
# artemis-0002: FileConfigurationParser.parseSecurityRoles — O(N×R) multiple ArrayList.contains in role loop

## CWE-407 — Algorithmic Complexity: Unnecessary Quadratic Complexity (List membership inside loop)

| Field | Value |
|-------|-------|
| ID | artemis-0002 |
| Severity | MEDIUM |
| Ecosystem | activemq-artemis |
| Package | org.apache.activemq.artemis.core.deployers.impl |
| File | `artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java` |
| Lines | 1212–1275 |
| Complexity | O(N×R) |
| Hot path | broker startup / configuration reload |

## Defect

`FileConfigurationParser` parses broker security roles from XML configuration.
It builds per-permission `ArrayList<String>` collections (send, consume,
createDurableQueue, deleteDurableQueue, createNonDurableQueue, deleteNonDurableQueue,
manageRoles, browseRoles, createAddressRoles, deleteAddressRoles, viewRoles,
editRoles), then for each role in `allRoles` (N roles) it calls `.contains(role)`
on up to 12 of these lists. Each call is O(R) where R is the maximum number of
roles in any single permission list. Total cost: O(N × 12 × R) = O(N²) in the
degenerate case where all roles have all permissions.

Additionally, `allRoles` itself is populated using `!allRoles.contains(role.trim())`
which is O(N) per insertion — another O(N²) scan.

```java
// FileConfigurationParser.java lines 1212-1275
List<String> send = new ArrayList<>();
List<String> consume = new ArrayList<>();
// ... 10 more ArrayList<String> ...
List<String> allRoles = new ArrayList<>();

// ... XML parsing loop populates the lists ...
if (!allRoles.contains(role.trim())) {   // O(N) per role — O(N²) total
    allRoles.add(role.trim());
}

for (String role : allRoles) {
    securityRoles.add(new Role(role,
        send.contains(role),             // O(R) × 12 per role
        consume.contains(role),
        createDurableQueue.contains(role),
        deleteDurableQueue.contains(role),
        createNonDurableQueue.contains(role),
        deleteNonDurableQueue.contains(role),
        manageRoles.contains(role),
        browseRoles.contains(role),
        createAddressRoles.contains(role),
        deleteAddressRoles.contains(role),
        viewRoles.contains(role),
        editRoles.contains(role)));
}
```

## Fix

Use `HashSet<String>` (or `LinkedHashSet<String>` to preserve order) for all the
permission lists, and for `allRoles`:

```java
Set<String> send = new LinkedHashSet<>();
Set<String> consume = new LinkedHashSet<>();
Set<String> createDurableQueue = new LinkedHashSet<>();
Set<String> deleteDurableQueue = new LinkedHashSet<>();
Set<String> createNonDurableQueue = new LinkedHashSet<>();
Set<String> deleteNonDurableQueue = new LinkedHashSet<>();
Set<String> manageRoles = new LinkedHashSet<>();
Set<String> browseRoles = new LinkedHashSet<>();
Set<String> createAddressRoles = new LinkedHashSet<>();
Set<String> deleteAddressRoles = new LinkedHashSet<>();
Set<String> viewRoles = new LinkedHashSet<>();
Set<String> editRoles = new LinkedHashSet<>();
Set<String> allRoles = new LinkedHashSet<>();

// allRoles population: add() already deduplicates in a Set — no contains() check needed
allRoles.add(role.trim());   // replaces the !allRoles.contains() guard

// Role construction loop unchanged — Set.contains() is O(1)
for (String role : allRoles) {
    securityRoles.add(new Role(role,
        send.contains(role), consume.contains(role), ...));
}
```

## Speedup

| N (roles) | R (roles per permission) | Before (ops) | After (ops) | Speedup |
|-----------|--------------------------|-------------|-------------|---------|
| 50 | 50 | ~30,000 | ~600 | 50× |
| 200 | 200 | ~480,000 | ~2,400 | 200× |
| 1,000 | 1,000 | ~12,000,000 | ~12,000 | 1,000× |

Most Artemis deployments have modest role counts (10–50) but enterprise LDAP-backed
configurations with fine-grained per-address permissions can have hundreds of roles.
The defect is hit on every broker startup and every live configuration reload.
