Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions list_fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,20 @@ def list_extract_dir(bot):
return candidate


def _fetch_file_size_budget():
"""MAX_FETCH_FILE_SIZE, resolved the way dcc_fetch.py's own admission
check already reads it: 0 means no limit (#302), not a real zero-byte
ceiling. Read here rather than left as the raw setting because this
module uses the value twice - once as the zip-bomb sum cap in
_validate_zip_members(), once as the running extraction budget in
process_fetched_list_zip() - and a `budget` that starts at a literal 0
would fail the very first byte written, the same bug either call site
would have on its own (#937 was the sibling of this one, on
MAX_FETCH_LIST_FILE_SIZE)."""
raw = int(getattr(config, "MAX_FETCH_FILE_SIZE", 200 * 1024 * 1024))
return raw if raw > 0 else float("inf")


def _validate_zip_members(infolist, extract_dir):
"""Check EVERY member before anything is extracted. Returns a short
rejection reason string, or None if the whole archive is clear to
Expand All @@ -229,7 +243,7 @@ def _validate_zip_members(infolist, extract_dir):
f"{MAX_LIST_ZIP_ENTRIES} a real master-list archive should "
f"ever need (zip-bomb-shaped guard)")

max_total = int(getattr(config, "MAX_FETCH_FILE_SIZE", 200 * 1024 * 1024))
max_total = _fetch_file_size_budget()
total_uncompressed = 0
for info in infolist:
if info.is_dir():
Expand Down Expand Up @@ -683,8 +697,7 @@ def _extract_and_locate_list_file(zip_path, extract_dir):
shutil.rmtree(platform_compat.long_path(extract_dir), ignore_errors=True)
return None, reason

max_total = int(getattr(config, "MAX_FETCH_FILE_SIZE", 200 * 1024 * 1024))
budget = max_total
budget = _fetch_file_size_budget()
for info in infolist:
if info.is_dir():
continue
Expand Down
16 changes: 14 additions & 2 deletions tests/test_list_fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,20 @@ def test_a_declared_total_size_over_the_cap_is_rejected_before_extracting(self):

self.assertFalse(ok)
self.assertIn("exceeds", reason)
self.assertNotIn("bigbot", config.fetched_bot_lists)
self.assertFalse(os.path.exists(list_fetch.list_extract_dir("bigbot")))

def test_zero_means_no_cap_on_the_declared_total_either(self):
"""#937's sibling: MAX_FETCH_FILE_SIZE = 0 is "no limit" here too, not
a zero-byte zip-bomb ceiling that rejects any real list - and the same
value is the running extraction budget, so a fix that only touched the
sum check and left `budget` starting at a literal 0 would still fail
on the very first byte written."""
self.set_config(MAX_FETCH_FILE_SIZE=0)
big_txt = _list_txt() + ("!OtherBot Filler.flac ::INFO:: 1.0MB\n" * 50)
_write_zip(self.zip_path, [("OtherBot-2026-08-27.txt", big_txt)])

ok, reason = list_fetch.process_fetched_list_zip("bigbot", self.zip_path)

self.assertTrue(ok, reason)

def test_an_oversized_zip_on_disk_is_rejected_before_it_is_opened(self):
"""#162 finding #10, belt-to-braces half: MAX_LIST_ZIP_ENTRIES and the
Expand Down
Loading