CodeSampleX

Ejemplo

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.

Muestra verificada para pypi pillow 10.4.0: Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and…

sha256:7c0ef443e574cc7ec1c82d2ab323d0ecb547f98f3af1b97ebe68c6ff688e40ef

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
3
Claves de firma que lo compilaron
3
Entorno declarado python linux x64 python python pip

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
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

Caso

HOW
Objetivo
Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.
Paquetes
Símbolos
  • PIL.Image.MAX_IMAGE_PIXELS
  • PIL.Image.DecompressionBombError
  • PIL.Image.DecompressionBombWarning
  • PIL.Image.open
Entorno
python
Creado
2026-08-17T11:44:15Z

Contrato

  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.

Archivos

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

Descargar el artefacto de código fuente (tar.gz)

Código fuente

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()

Seeder de origen

csx-seed