# UNDF: UNDF-2026-000000005
# ansible-0001: Role.get_vars() seen-list O(D²) deduplication

## Classification
- **Severity**: MEDIUM
- **CWE**: CWE-407 (Algorithmic Complexity — Inefficient Algorithmic Complexity)
- **Component**: `lib/ansible/playbook/role/__init__.py`

## Location
`lib/ansible/playbook/role/__init__.py`, method `get_vars()`, lines 539–546

```python
seen = []
for dep in self.get_all_dependencies():
    # Avoid rerunning dupe deps since they can have vars from previous invocations
    if dep not in seen:
        all_vars = combine_vars(all_vars, dep.get_vars(include_params=False, only_exports=True))
        seen.append(dep)
```

## Pattern
`seen` is initialized as a Python `list`. The membership test `dep not in seen` is O(D) for each of D dependencies → total O(D²). In a large Ansible playbook with deeply nested roles (e.g. enterprise roles with D=100+ transitive dependencies), this produces D(D-1)/2 comparisons.

## Speedup
At D=200 dependencies: 19,900 comparisons → 200 comparisons (99.5x reduction)

## Patch

```diff
--- a/lib/ansible/playbook/role/__init__.py
+++ b/lib/ansible/playbook/role/__init__.py
@@ -536,10 +536,10 @@ class Role(Base, Become, Conditional, Taggable, CollectionSearch):
         # get exported variables from meta/dependencies
-        seen = []
+        seen = set()
         for dep in self.get_all_dependencies():
             # Avoid rerunning dupe deps since they can have vars from previous invocations and they accumulate in deps
             # TODO: re-examine dep loading to see if we are somehow improperly adding the same dep too many times
             if dep not in seen:
                 # only take 'exportable' vars from deps
                 all_vars = combine_vars(all_vars, dep.get_vars(include_params=False, only_exports=True))
-                seen.append(dep)
+                seen.add(dep)
```

Note: `Role` objects are used as set members; Python uses identity (`id()`) by default for unhashed objects, which is correct here — same object in memory = same dep. If Role doesn't define `__hash__`, Python uses the default identity hash.

## Complexity
- Before: O(D²) — D = number of transitive role dependencies
- After: O(D) — set membership is O(1) amortized
