CodeSampleX

샘플

pillow 10.4.0: Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.

검증된 샘플 — pypi pillow 10.4.0: Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning…

sha256:7c0ef443e574cc7ec1c82d2ab323d0ecb547f98f3af1b97ebe68c6ff688e40ef

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
3
빌드한 서명 키
3
선언된 환경 python linux x64 python python pip

검증 실행 환경

환경 컨트랙트 단계 실행일
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-17
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18
python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104…
2026-09-08

케이스

HOW
목표
Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.
패키지
심벌
  • PIL.Image.MAX_IMAGE_PIXELS
  • PIL.Image.DecompressionBombError
  • PIL.Image.DecompressionBombWarning
  • PIL.Image.open
환경
python
생성일
2026-08-17T11:44:15Z

컨트랙트

  1. Image.open eagerly raises PIL.Image.DecompressionBombError during header parsing when total pixels exceed twice Image.MAX_IMAGE_PIXELS without allocating pixel memory.
  2. Image.open issues a PIL.Image.DecompressionBombWarning when total pixels exceed Image.MAX_IMAGE_PIXELS but remain within twice the limit.
  3. Setting PIL.Image.MAX_IMAGE_PIXELS to None disables decompression bomb checks completely, allowing Image.open to succeed without warnings or errors.
  4. Reconfiguring PIL.Image.MAX_IMAGE_PIXELS dynamically updates both the warning and error thresholds for subsequent Image.open calls.

파일

  • NOTES.md
  • csx.json
  • requirements.lock
  • requirements.txt
  • test/contract.py

소스 아티팩트 내려받기 (tar.gz)

소스

NOTES.md
# Pillow Image.MAX_IMAGE_PIXELS Configuration Contract

## Prior Knowledge Search
A search for known solutions for `pillow` returned sample `sha256:1a0c77d1acd1df6aa566ef5dd18ab1b8a9b9245b1e7c6e853fd1c0d339f895e6` (demonstrating `ImageOps.exif_transpose`), but no existing contract investigated Pillow decompression bomb DOS protection or `PIL.Image.MAX_IMAGE_PIXELS` configuration behavior.

## Pinned Release
- Package: `pillow` (PyPI)
- Resolved Version: `10.4.0`

## Observed Behavior and Failure Mode
Pillow provides a module-level setting, `PIL.Image.MAX_IMAGE_PIXELS`, initialized by default to `89478485` (approximately 89.5 million pixels), designed to protect applications from decompression bomb denial-of-service (DOS) attacks.

1. **Eager Header-Level Validation**: When calling `PIL.Image.open()`, Pillow reads only the image header to determine image dimensions `(width, height)` and calculates total pixels `width * height`.
2. **Two-Tier Threshold**:
   - If total pixels exceed `2 * Image.MAX_IMAGE_PIXELS`, `PIL.Image.open()` immediately raises `PIL.Image.DecompressionBombError` (a subclass of `Exception`). This failure occurs eagerly during header parsing before any raster pixel buffers are allocated or decoded in memory.
   - If total pixels exceed `Image.MAX_IMAGE_PIXELS` but remain within `2 * Image.MAX_IMAGE_PIXELS`, `PIL.Image.open()` does not throw an error; instead, it issues a `PIL.Image.DecompressionBombWarning` (a subclass of `RuntimeWarning`).
3. **Disabling Protection**: Setting `PIL.Image.MAX_IMAGE_PIXELS = None` completely disables the decompression bomb check, allowing `PIL.Image.open()` to process images of arbitrary dimensions without warnings or errors.
4. **Dynamic Reconfiguration**: Assigning a new integer value to `PIL.Image.MAX_IMAGE_PIXELS` dynamically adjusts both the warning and error thresholds for subsequent image operations.

### Failure Mode of Common Misconceptions
Naive models often assume that:
- Pillow will fail with a generic `MemoryError` only when pixel rasterization occurs (e.g. during `.load()` or `.tobytes()`), whereas Pillow fails eagerly during `Image.open()` with `DecompressionBombError`.
- Any image exceeding `Image.MAX_IMAGE_PIXELS` immediately throws `DecompressionBombError`, whereas values between `MAX_IMAGE_PIXELS` and `2 * MAX_IMAGE_PIXELS` only issue a warning, failing loudly only when exceeding twice the configured limit.
csx.json
{"case":{"believed":"Image.open checks image size limits only during pixel rasterization and raises a generic MemoryError rather than DecompressionBombError when pixels exceed Image.MAX_IMAGE_PIXELS.","caseId":"case:sha256:c2a45c3bfa2f3fbd3cdec96c98e273b3f70b9bfa92769fa9d70df75c0ca54646","contract":["Image.open eagerly raises PIL.Image.DecompressionBombError during header parsing when total pixels exceed twice Image.MAX_IMAGE_PIXELS without allocating pixel memory.","Image.open issues a PIL.Image.DecompressionBombWarning when total pixels exceed Image.MAX_IMAGE_PIXELS but remain within twice the limit.","Setting PIL.Image.MAX_IMAGE_PIXELS to None disables decompression bomb checks completely, allowing Image.open to succeed without warnings or errors.","Reconfiguring PIL.Image.MAX_IMAGE_PIXELS dynamically updates both the warning and error thresholds for subsequent Image.open calls."],"goal":"Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.","kind":"HOW","packages":["pkg:pypi/pillow@10.4.0"],"schemaVersion":1,"symbols":["PIL.Image.MAX_IMAGE_PIXELS","PIL.Image.DecompressionBombError","PIL.Image.DecompressionBombWarning","PIL.Image.open"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/pillow@10.4.0"],"schemaVersion":1,"symbols":["PIL.Image.MAX_IMAGE_PIXELS","PIL.Image.DecompressionBombError","PIL.Image.DecompressionBombWarning","PIL.Image.open"],"verifierAdapter":"python@1"}
requirements.lock
Pillow==10.4.0
requirements.txt
Pillow==10.4.0
test/contract.py
import io
import unittest
import warnings

import PIL
from PIL import Image


class MaxImagePixelsContract(unittest.TestCase):
    def setUp(self) -> None:
        self.original_max_pixels = Image.MAX_IMAGE_PIXELS

    def tearDown(self) -> None:
        Image.MAX_IMAGE_PIXELS = self.original_max_pixels

    def _create_png_bytes(self, width: int = 100, height: int = 100) -> bytes:
        img = Image.new("RGB", (width, height), color=(0, 128, 255))
        buf = io.BytesIO()
        img.save(buf, format="PNG")
        return buf.getvalue()

    def test_pinned_version_and_default_limit(self) -> None:
        self.assertEqual(PIL.__version__, "10.4.0")
        self.assertEqual(self.original_max_pixels, 89_478_485)

    def test_decompression_bomb_error_eagerly_raised_above_twice_limit(self) -> None:
        # 100x100 = 10,000 pixels. Setting MAX_IMAGE_PIXELS = 1,000 sets 2x limit to 2,000.
        Image.MAX_IMAGE_PIXELS = 1000
        png_data = self._create_png_bytes(100, 100)

        with self.assertRaises(Image.DecompressionBombError) as ctx:
            Image.open(io.BytesIO(png_data))

        self.assertIn("Image size (10000 pixels) exceeds limit of 2000 pixels", str(ctx.exception))
        self.assertIn("decompression bomb DOS attack", str(ctx.exception))

    def test_decompression_bomb_warning_between_limit_and_twice_limit(self) -> None:
        # 100x100 = 10,000 pixels. Setting MAX_IMAGE_PIXELS = 8,000 sets warning range (8,000, 16,000].
        Image.MAX_IMAGE_PIXELS = 8000
        png_data = self._create_png_bytes(100, 100)

        with warnings.catch_warnings(record=True) as captured:
            warnings.simplefilter("always")
            img = Image.open(io.BytesIO(png_data))
            self.assertEqual(img.size, (100, 100))

        self.assertEqual(len(captured), 1)
        self.assertTrue(issubclass(captured[0].category, Image.DecompressionBombWarning))
        self.assertIn("Image size (10000 pixels) exceeds limit of 8000 pixels", str(captured[0].message))

    def test_setting_none_disables_decompression_bomb_checks(self) -> None:
        Image.MAX_IMAGE_PIXELS = None
        png_data = self._create_png_bytes(100, 100)

        with warnings.catch_warnings(record=True) as captured:
            warnings.simplefilter("always")
            img = Image.open(io.BytesIO(png_data))
            self.assertEqual(img.size, (100, 100))

        self.assertEqual(len(captured), 0)

    def test_reconfiguring_limit_dynamically_adjusts_threshold(self) -> None:
        Image.MAX_IMAGE_PIXELS = 20_000
        png_data = self._create_png_bytes(100, 100)

        with warnings.catch_warnings(record=True) as captured:
            warnings.simplefilter("always")
            img = Image.open(io.BytesIO(png_data))
            self.assertEqual(img.size, (100, 100))

        self.assertEqual(len(captured), 0)


if __name__ == "__main__":
    unittest.main()

오리진 시더

csx-seed