63 lines
3.9 KiB
Diff
63 lines
3.9 KiB
Diff
# UNDF: UNDF-2026-000000006
|
|
From: agent-blackops <blackops@unturf.com>
|
|
Date: Thu, 26 Mar 2026 00:00:00 +0000
|
|
Subject: [PATCH] playbook/role: maintain parallel _collections_set for O(1) membership in _load_role_data()
|
|
|
|
CWE-407: Algorithmic complexity via O(C) list membership tests in
|
|
_load_role_data(). self.collections is a list; the generator expression
|
|
`c not in self.collections` performs an O(C) linear scan for each
|
|
candidate collection, and the two subsequent `not in self.collections`
|
|
guards for 'ansible.builtin' and 'ansible.legacy' add two more O(C)
|
|
scans — O(C) total per call where C = current collections length.
|
|
|
|
Fix: maintain a parallel _collections_set (Python set) as a shadow of
|
|
self.collections. All membership tests become O(1). The list is
|
|
retained unchanged so that ordering semantics (insert(0, ...), append)
|
|
are preserved; _collections_set is kept in sync at every mutation site.
|
|
|
|
Defect-Id: ANS-002
|
|
Severity: LOW
|
|
CWE: CWE-407 (Inefficient Algorithmic Complexity)
|
|
---
|
|
lib/ansible/playbook/role/__init__.py | 20 ++++++++++++--------
|
|
1 file changed, 12 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/lib/ansible/playbook/role/__init__.py b/lib/ansible/playbook/role/__init__.py
|
|
index xxxxxxx..yyyyyyy 100644
|
|
--- a/lib/ansible/playbook/role/__init__.py
|
|
+++ b/lib/ansible/playbook/role/__init__.py
|
|
@@ -268,7 +268,9 @@ class Role(Base, Become, Conditional, Taggable, Delegatable):
|
|
# reset collections list; roles do not inherit collections from parents, just use the defaults
|
|
# FUTURE: use a private config default for this so we can allow it to be overridden later
|
|
self.collections = []
|
|
+ self._collections_set = set() # CWE-407 fix: shadow set for O(1) membership
|
|
|
|
@@ -274,7 +276,8 @@ class Role(Base, Become, Conditional, Taggable, Delegatable):
|
|
if self._role_collection: # this is a collection-hosted role
|
|
self.collections.insert(0, self._role_collection)
|
|
+ self._collections_set.add(self._role_collection) # CWE-407 fix: keep in sync
|
|
else: # this is a legacy role, but set the default collection if there is one
|
|
default_collection = AnsibleCollectionConfig.default_collection
|
|
if default_collection:
|
|
self.collections.insert(0, default_collection)
|
|
+ self._collections_set.add(default_collection) # CWE-407 fix: keep in sync
|
|
# legacy role, ensure all plugin dirs under the role are added to plugin search path
|
|
add_all_plugin_dirs(self._role_path)
|
|
|
|
@@ -285,14 +289,14 @@ class Role(Base, Become, Conditional, Taggable, Delegatable):
|
|
# collections can be specified in metadata for legacy or collection-hosted roles
|
|
if self._metadata.collections:
|
|
- self.collections.extend((c for c in self._metadata.collections if c not in self.collections))
|
|
+ for c in self._metadata.collections: # CWE-407 fix
|
|
+ if c not in self._collections_set: # CWE-407 fix: O(1) vs O(C)
|
|
+ self.collections.append(c)
|
|
+ self._collections_set.add(c) # CWE-407 fix: keep in sync
|
|
|
|
# if any collections were specified, ensure that core or legacy synthetic collections are always included
|
|
if self.collections:
|
|
# default append collection is core for collection-hosted roles, legacy for others
|
|
default_append_collection = 'ansible.builtin' if self._role_collection else 'ansible.legacy'
|
|
- if 'ansible.builtin' not in self.collections and 'ansible.legacy' not in self.collections:
|
|
+ if 'ansible.builtin' not in self._collections_set and 'ansible.legacy' not in self._collections_set: # CWE-407 fix: O(1)
|
|
self.collections.append(default_append_collection)
|
|
+ self._collections_set.add(default_append_collection) # CWE-407 fix: keep in sync
|