52 lines
2.3 KiB
Diff
52 lines
2.3 KiB
Diff
# UNDF: UNDF-2026-000000025
|
||
From: agent-blackops <blackops@unturf.com>
|
||
Date: Fri, 27 Mar 2026 00:00:00 +0000
|
||
Subject: [PATCH] canvas: replace list membership test in append_to_list_option with set mirror
|
||
|
||
CWE-407: Algorithmic complexity via O(L) linear membership test inside chain
|
||
build loops. append_to_list_option() uses `value not in items` where items
|
||
is a plain list, and this method is called inside O(T) task loops and O(E)
|
||
errback loops during Chain construction, producing O(T×E×L) total comparisons.
|
||
|
||
Fix: store a parallel set alongside each list option for O(1) average membership
|
||
test. The list is preserved for ordering; the set is used only for deduplication
|
||
guard. Uses a dict-based shadow store keyed by the option key name.
|
||
|
||
Defect-Id: CEL-001
|
||
Severity: MEDIUM
|
||
CWE: CWE-407 (Inefficient Algorithmic Complexity)
|
||
---
|
||
celery/canvas.py | 18 +++++++++++++-----
|
||
1 file changed, 13 insertions(+), 5 deletions(-)
|
||
|
||
diff --git a/celery/canvas.py b/celery/canvas.py
|
||
index xxxxxxx..yyyyyyy 100644
|
||
--- a/celery/canvas.py
|
||
+++ b/celery/canvas.py
|
||
@@ -685,10 +685,18 @@ class Signature(dict):
|
||
def _with_list_option(self, key):
|
||
items = self.options.setdefault(key, [])
|
||
if not isinstance(items, MutableSequence):
|
||
items = self.options[key] = [items]
|
||
return items
|
||
|
||
+ def _with_list_option_set(self, key):
|
||
+ """Returns (list, set) pair; set mirrors list for O(1) membership."""
|
||
+ items = self._with_list_option(key)
|
||
+ shadow_key = f"__set_{key}"
|
||
+ items_set = self.options.setdefault(shadow_key, set())
|
||
+ if len(items_set) != len(items): # CWE-407 fix: sync if needed
|
||
+ items_set.clear()
|
||
+ items_set.update(id(v) for v in items)
|
||
+ return items, items_set
|
||
+
|
||
def append_to_list_option(self, key, value):
|
||
"""Appends the given value to the list at the given key in self.options."""
|
||
- items = self._with_list_option(key)
|
||
- if value not in items: # CWE-407: O(L) linear scan
|
||
+ items, items_set = self._with_list_option_set(key)
|
||
+ value_id = id(value)
|
||
+ if value_id not in items_set: # CWE-407 fix: O(1) average
|
||
items.append(value)
|
||
+ items_set.add(value_id)
|
||
return value
|