MAX_FETCH_FILE_SIZE = 0 (no limit) trips the list zip-bomb guard too (#939) - #940
Conversation
Sibling of #937/#938 (#939). list_fetch.py reads MAX_FETCH_FILE_SIZE twice as a zip-bomb guard on a fetched list's declared total uncompressed size, and again as the running extraction budget - neither read treated 0 as no limit, so a fetched list with the setting at 0 was rejected on the very first byte, either at the pre-extraction sum check or, if that had been fixed alone, at the first member's write. Reproduced live right after #938 shipped: a real fetch from SamothMetal refused with 'zip's declared total uncompressed size exceeds MAX_FETCH_FILE_SIZE (0 bytes) - refusing to extract (zip-bomb guard)'. Fix: one helper, _fetch_file_size_budget(), resolving 0 to no limit (float('inf')) at the single point both call sites read the setting from. tests/test_list_fetch.py: test_zero_means_no_cap_on_the_declared_total_either. Mutation-checked against both sub-bugs: reverting the helper entirely, and fixing only the sum check while leaving the extraction budget read raw, each fail the test with the exact message that shape of the bug produces.
|
Review. The bug is real and the helper is the right shape, but there are two things to fix before merging: 1. The new test accidentally deletes two assertions from the test above it. In self.assertNotIn("bigbot", config.fetched_bot_lists)
self.assertFalse(os.path.exists(list_fetch.list_extract_dir("bigbot")))Those lines are what prove a rejected zip leaves nothing behind (no registry entry, no extract dir). Please put them back. 2. With 0 resolved to
An operator who sets "no limit" on file downloads isn't asking to switch off zip-bomb protection on list archives. My suggestion: when the setting is 0, the list budget falls back to what a real list archive can hold anyway: def _fetch_file_size_budget():
raw = int(getattr(config, "MAX_FETCH_FILE_SIZE", 200 * 1024 * 1024))
# 0 is "no limit" for FILES (#302). For a LIST archive the zip-bomb guard
# still needs a bound: every list over max_list_text_size() is rejected
# after extraction anyway, so this refuses nothing a real list needs.
return raw if raw > 0 else max_list_text_size() * MAX_LISTS_PER_ARCHIVEWith the defaults that's 128 MB × 8 = 1 GB. Your live SamothMetal case (482 MB uncompressed) still passes the guard and is still stopped by Everything else looks good. #937's |
Closes #939. Sibling of #937/#938, found immediately after that fix shipped.
list_fetch.pyreadsMAX_FETCH_FILE_SIZEtwice - once as the zip-bomb guard on a fetched list's declared total uncompressed size (_validate_zip_members()), once as the running extraction budget (process_fetched_list_zip()) - and neither read treated 0 as "no limit" the waydcc_fetch.py's own admission check does (#302).Reproduced live right after #938 shipped: a real fetch from SamothMetal refused with
zip's declared total uncompressed size exceeds MAX_FETCH_FILE_SIZE (0 bytes) - refusing to extract (zip-bomb guard), the setting explicitly at 0.Fix: one helper,
_fetch_file_size_budget(), resolving 0 to no limit (float('inf')) at the single point both call sites read the setting from - a fix that only patched the sum-check comparison would still fail on the very first byte written, sincebudgetis seeded from the same value.Test:
test_zero_means_no_cap_on_the_declared_total_either. Mutation-checked against both sub-bugs (reverting the helper entirely, and fixing only the sum check while leaving the extraction budget raw) - each fails with the exact message that shape of the bug produces.Verified live on the operator's own bot before this PR: deployed the branch, and a real fetch from SamothMetal (82,487,470-byte zip, 482,789,465 bytes uncompressed) that previously died at the zip-bomb guard now passes it cleanly and is instead correctly stopped by the unrelated, already-correct
MAX_LIST_TEXT_SIZEceiling (that list genuinely exceeds it).Full suite 6698 OK.
🤖 Generated with Claude Code