Skip to content

add a basic guard against mundane zipbombz #13877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 6, 2023
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
9 changes: 9 additions & 0 deletions tests/unit/forklift/test_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,15 @@ def test_zipfile_unsupported_compression(self, tmpdir, method):

assert not legacy._is_valid_dist_file(f, "")

def test_zipfile_exceeds_compression_threshold(self, tmpdir):
f = str(tmpdir.join("test.zip"))

with zipfile.ZipFile(f, "w") as zfp:
zfp.writestr("PKG-INFO", b"this is the package info")
zfp.writestr("1.dat", b"0" * 10240, zipfile.ZIP_DEFLATED)

assert not legacy._is_valid_dist_file(f, "")

def test_egg_no_pkg_info(self, tmpdir):
f = str(tmpdir.join("test.egg"))

Expand Down
19 changes: 19 additions & 0 deletions warehouse/forklift/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import packaging_legacy.version
import pkg_resources
import requests
import sentry_sdk
import wtforms
import wtforms.validators

Expand Down Expand Up @@ -81,6 +82,8 @@

PATH_HASHER = "blake2_256"

COMPRESSION_RATIO_THRESHOLD = 10


# Wheel platform checking

Expand Down Expand Up @@ -670,6 +673,19 @@ def _is_valid_dist_file(filename, filetype):
# If our file is a zipfile, then ensure that it's members are only
# compressed with supported compression methods.
if zipfile.is_zipfile(filename):
# Ensure the compression ratio is not absurd (decompression bomb)
compressed_size = os.stat(filename).st_size
with zipfile.ZipFile(filename) as zfp:
decompressed_size = sum(e.file_size for e in zfp.infolist())
if decompressed_size / compressed_size > COMPRESSION_RATIO_THRESHOLD:
sentry_sdk.capture_message(
f"File {filename} ({filetype}) exceeds compression ratio "
"of {COMPRESSION_RATIO_THRESHOLD} "
"({decompressed_size}/{compressed_size})"
)
return False

# Check that the compression type is valid
with zipfile.ZipFile(filename) as zfp:
for zinfo in zfp.infolist():
if zinfo.compress_type not in {
Expand All @@ -680,6 +696,9 @@ def _is_valid_dist_file(filename, filetype):

tar_fn_match = _tar_filenames_re.search(filename)
if tar_fn_match:
# TODO: Ideally Ensure the compression ratio is not absurd
# (decompression bomb), like we do for wheel/zip above.

# Ensure that this is a valid tar file, and that it contains PKG-INFO.
z_type = tar_fn_match.group("z_type") or ""
try:
Expand Down