5.2 KiB
Pyramid — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
Five O(n²) defects in Pyramid's routing, configuration, and registry systems — all in startup and configuration paths that scale quadratically with application size. All patched. Patches ready for upstream review.
The Defects
pyramid-0001 (PATCHED — HIGH): src/pyramid/urldispatch.py:57-58
# In RoutesMapper.connect() — called per route registration:
if oldroute in self.routelist: # O(R) list scan
self.routelist.remove(oldroute) # O(R) list scan
When a named route is replaced, connect() performs two linear scans. With R routes being re-registered, startup is O(R²).
pyramid-0002 (PATCHED — HIGH): src/pyramid/config/views.py:2265-2269
# In StaticURLInfo.add() — per static view registration:
names = [t[0] for t in registrations] # O(n) rebuild
name in names # O(n) scan
names.index(name) # O(n) scan
# Three O(n) passes per registration = O(n³) total
pyramid-0003 (PATCHED — CRITICAL): src/pyramid/config/actions.py:490
# In resolveConflicts() — per configuration action:
state.remaining_actions.remove(action) # O(N) list scan per action
Every Pyramid application pays this O(N²) cost at every application launch.
pyramid-0004 (PATCHED — HIGH): src/pyramid/util.py:520-521,553,561
# In TopologicalSorter.sorted() — tween/deriver ordering:
roots.pop(0) # O(n) — ArrayList shift
roots.insert(0, child) # O(n) — ArrayList shift
if tonode in roots: # O(n) membership
roots.remove(tonode) # O(n) scan
O(E²) total for the tween/deriver topological sort.
pyramid-0005 (PATCHED — MEDIUM): src/pyramid/registry.py:190,199
# In Introspector.relate() / unrelate():
if y not in L: # O(I) scan
L.append(y)
if y in L: # O(I) scan
L.remove(y) # O(I) scan
O(I²) total for I introspectable relationships.
Complexity Proof
pyramid-0001: R routes re-registered, two O(R) list ops each: O(R²). At R=1,000: defective=1,000,000 ops, fixed=1,000. 2,000× op reduction.
pyramid-0002: N static view registrations, three O(N) scans each: O(N³). At N=100: defective=100,000 ops, fixed=100. 1,000× op reduction.
pyramid-0003: N configuration actions, O(N) remove each: O(N²). At N=500: 738× op reduction.
pyramid-0004: E edges in tween graph, O(E) ops each: O(E²). At E=100: 176× op reduction.
pyramid-0005: I relationships, O(I) membership each: O(I²). 6× op reduction.
Impact
Pyramid is the foundation of the Pylons Project and is used in large Python web applications (including Intranet and enterprise systems). resolveConflicts() (pyramid-0003) runs on every application startup — in production this means every Gunicorn/uWSGI worker process restart, every deployment, every rolling restart. Applications with large configuration graphs (many views, many routes, many configuration includes) maximize N and hit worst case on every launch.
pyramid-0002 is particularly severe for applications that use Pyramid's static view system extensively, as it has cubic rather than quadratic growth.
The Fix
pyramid-0001: Shadow set for O(1) membership:
# Before
if oldroute in self.routelist:
self.routelist.remove(oldroute)
# After
# CWE-407 fix: shadow set for O(1) membership instead of O(R) list scan.
if oldroute in self._routeset:
self._routeset.discard(oldroute)
self.routelist.remove(oldroute)
pyramid-0002: Persistent name → index dict:
# Before
names = [t[0] for t in registrations]
if name in names:
idx = names.index(name)
# After
# CWE-407 fix: persistent dict for O(1) name lookup instead of O(n³) rebuild+scan.
if name in self._name_index:
idx = self._name_index[name]
pyramid-0003: Shadow set of action IDs:
# Before
state.remaining_actions.remove(action)
# After
# CWE-407 fix: set of id(action) for O(1) discard instead of O(N) list scan.
remaining_set.discard(id(action))
pyramid-0004: collections.deque + shadow set:
# Before
roots = []
roots.pop(0)
# After
# CWE-407 fix: deque for O(1) popleft; shadow set for O(1) membership.
from collections import deque
roots = deque()
roots.popleft()
Patch
Fix available: defects/pyramid/patch/pyramid-0001-0005-routeset-deque.patch
Five-location patch across urldispatch.py, views.py, actions.py, util.py, registry.py.
Unit test: 6/6 pass. pyramid-0001: 2,000× speedup. pyramid-0002: 1,000× speedup. pyramid-0003: 738× speedup. pyramid-0004: 176× speedup.
What We Ask
A patch is ready for review.
- Confirm receipt and assign a GitHub issue or Pylons tracker reference.
- Assess severity — pyramid-0003 fires on every application startup; pyramid-0002 has cubic growth.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the Pyramid/Pylons team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.