48 lines
2 KiB
Diff
48 lines
2 KiB
Diff
# UNDF: UNDF-2026-000000228
|
|
Fixes pylons-0001: TopologicalSorter.add()/sorted() — `if name in self.names` list scan O(N²) — CWE-407.
|
|
|
|
--- a/src/pyramid/util.py
|
|
+++ b/src/pyramid/util.py
|
|
|
|
@@ DEFECT pylons-0001: TopologicalSorter.__init__/add/remove/sorted lines 432, 449, 481, 577
|
|
|
|
class TopologicalSorter:
|
|
def __init__(self, default_before=LAST, default_after=None, first=FIRST, last=LAST):
|
|
self.names = []
|
|
+ self.names_set = set() # FIX pylons-0001: O(1) membership shadow set
|
|
self.req_before = set()
|
|
self.req_after = set()
|
|
...
|
|
|
|
def remove(self, name):
|
|
"""Remove a node from the sort input"""
|
|
- self.names.remove(name) # O(n) list scan — CWE-407
|
|
+ self.names_set.discard(name) # O(1) — fixed
|
|
+ self.names.remove(name) # O(n) but confirmed present; acceptable once
|
|
del self.name2val[name]
|
|
...
|
|
|
|
def add(self, name, val, after=None, before=None):
|
|
- if name in self.names: # O(n) list scan — CWE-407
|
|
+ if name in self.names_set: # O(1) set lookup — fixed
|
|
self.remove(name)
|
|
self.names.append(name)
|
|
+ self.names_set.add(name) # maintain shadow set
|
|
...
|
|
|
|
def sorted(self):
|
|
...
|
|
result = []
|
|
for name in sorted_names:
|
|
- if name in self.names: # O(n) list scan inside O(n) loop — CWE-407
|
|
+ if name in self.names_set: # O(1) set lookup — fixed
|
|
result.append((name, self.name2val[name]))
|
|
return result
|
|
|
|
# SUMMARY:
|
|
# add(): if name in self.names O(n) → O(1) via names_set
|
|
# sorted(): if name in self.names O(n) → O(1) via names_set [called in O(n) loop]
|
|
# remove(): self.names.remove() O(n) still (confirmed present; fires once per remove)
|
|
#
|
|
# Net: O(N²) → O(N) for add() sequence; O(N²) → O(N) for sorted() result loop.
|
|
# names list is preserved for iteration order (sorted() iterates self.names at line 507).
|