C1+C3 review findings: streaming decompression-bomb guard + stored-XSS stripping
Test / Tests (Python 3.12) (push) Canceled after 0s
Test / Tests (Python 3.12) (push) Canceled after 0s
C1 (decompression bomb): wire the shared common._copy_stream_with_cap into both zip-extraction paths. app.py._safe_extract_zip now streams each EPUB member with a WRITTEN-byte counter (app.py:456) instead of an unbounded copyfileobj, so a member that inflates a small header into a huge stream is stopped mid-write. pandoc_converter._validate_office_zip now streams every member's decompressed bytes through the same guard (pandoc_converter.py:134) before pandoc reads them. C3 (stored XSS): common.html_to_pdf_bytes now strips stored-XSS vectors via common._strip_scripts (common.py:320) BEFORE WeasyPrint renders, so every non-pandoc render path (html, svg, epub, md, csv, xml) neutralizes <script>/ <iframe> content, on* handlers and javascript:/vbscript: URLs. Regression tests added to tests/test_review_findings_20260903.py (C1 unit+e2e, C3 unit+e2e).
This commit is contained in:
@@ -407,6 +407,10 @@ def _safe_extract_zip(epub_path: Path, extract_dir: Path) -> None:
|
||||
Also enforces the zip-bomb cap: total uncompressed size must stay within
|
||||
max(8x upload size, ZIP_BOMB_MIN_CAP) or the job fails with a clear error.
|
||||
"""
|
||||
# Lazy import: app imports the converters package at module load (app.py:37),
|
||||
# but the common submodule is pulled lazily here to avoid any import-cycle
|
||||
# concern. Provides the C1 streaming decompression-bomb guard.
|
||||
from converters import common
|
||||
upload_size = epub_path.stat().st_size
|
||||
cap = max(8 * upload_size, ZIP_BOMB_MIN_CAP)
|
||||
try:
|
||||
@@ -444,7 +448,12 @@ def _safe_extract_zip(epub_path: Path, extract_dir: Path) -> None:
|
||||
continue
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
with zf.open(info) as src, open(target, "wb") as dst:
|
||||
shutil.copyfileobj(src, dst)
|
||||
# C1 (real enforcement): stream-copy counting WRITTEN bytes so a
|
||||
# member that inflates a small header into a huge stream is stopped
|
||||
# mid-write. A header-only check (lines 418-427) cannot see a member
|
||||
# whose declared size is understated; this counts actual bytes and
|
||||
# mirrors the shared guard wired into pandoc._validate_office_zip.
|
||||
common._copy_stream_with_cap(src, dst, cap)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user