- Move High Quality / Fast segmented toggle out of the clickable upload
zone into a standalone .render-control so clicking the drop box only uploads
- Update JS selector to .render-control; progressive disclosure keeps the
toggle hidden for non-office files, visible for .docx/.odt/.rtf
- Version bump 2.0.0 -> 2.1.0 (dev-test branch save rule: bump version on save)
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).
WeasyPrint's image loader swallows per-fetch errors and renders a broken
image, so a job could report DONE while a remote URL was contacted
(SSRF/data egress against the no-data-egress guarantee). Add
scan_for_remote() which rejects http(s)/ftp/ws/wss/file resource
references up-front (ValueError -> job error) before WeasyPrint ever
runs; local data: URIs still render via the default loader. Also add
tests/test_review_findings_20260903.py (remote -> job error, valid
data: -> job done).