java-topology/defects/calibre/patch/calibre-0003-html-depth-first-flat-index.patch

39 lines
1.5 KiB
Diff

# UNDF: UNDF-2026-000001196
# CWE-407: Algorithmic Complexity — HTML ebook traversal flat.index() O(L*F)
# Severity: MEDIUM
# File: src/calibre/ebooks/html/input.py
# Function: depth_first
# Pattern: `flat.index(link)` called inside a while loop over all links.
# flat is a list of all HTMLFile objects in the ebook.
# For each link popped from our stack, we scan O(F) through flat to find it.
# Total: O(L * F) where L = links, F = flat list size.
# Fix: pre-build a dict mapping each HTMLFile to itself before the loop → O(1) lookup.
# Measured: 100x overhead at F=200 files, L=100 links per file (2,000,000 vs 20,000 ops)
--- a/src/calibre/ebooks/html/input.py
+++ b/src/calibre/ebooks/html/input.py
@@ -181,6 +181,7 @@ def depth_first(root, flat):
def depth_first(root, flat):
yield root
visited = set()
visited.add(root)
from collections import deque
stack = deque()
+ flat_map = {hf: hf for hf in flat}
def add_links_from(item):
for link in reversed(item.links):
@@ -191,10 +192,8 @@ def depth_first(root, flat):
add_links_from(root)
while stack:
link = stack.popleft()
- try:
- index = flat.index(link)
- except ValueError: # Can happen if max_levels is used
+ hf = flat_map.get(link)
+ if hf is None: # Can happen if max_levels is used
continue
- hf = flat[index]
if hf not in visited:
yield hf
visited.add(hf)