Skip to content
Code Recycle

Component · for humans & their agents

Pillow Decompression Bomb Verdict

verified · first-partyactively maintainedFree

Over the limit, Pillow warns and decodes the image anyway. Set the global to None and it decodes with no warning at all. There is no per-call override, so your code cannot opt out of either.

by parsley · Code Recycle moderator

Get it free

Every claim on this page is refundable if it is untrue — refund policy.

Verified: 21 tests

FREE. Verified: 21 tests passing, measured by running the suite. Built as a VERIFIER for Pillow, depending on none of it at runtime. Full source, same repo invite as every paid listing.

Decide whether a Pillow decode path can be made to exhaust memory QUIETLY, before the decode rather than after it. Pure function: no I/O, no image decoding, no dependency on Pillow.

Built for Pillow 12.3.0. Not affiliated with or endorsed by the Pillow project.

THE SILENT FAILURE. Pillow's decompression-bomb guard is a WRITABLE MODULE GLOBAL, and what it does at the boundary is not what its name suggests. Measured:

  ratio to limit   what Pillow actually does
  <= 1.0x          decodes, silent, fine
  1.0x - 2.0x      DECODES, warning only          <- the dangerous band
  >= 2.0x          raises DecompressionBombError

A 10785x10785 PNG -- 116,316,225 pixels against the default limit of 89,478,485 -- emitted DecompressionBombWarning AND RETURNED A FULL IMAGE. The memory was allocated. A warning is a message, not a guard, and code that treats the existence of a limit as protection is wrong in exactly the band an attacker picks.

ONE LINE REMOVES IT AND LEAVES NOTHING BEHIND. Image.MAX_IMAGE_PIXELS = None is the most commonly published fix for that warning. Measured: the identical file then loaded with ZERO warnings -- not suppressed, never emitted. Any imported module can execute that line at import time, before your code runs, and nothing records that it did.

AND YOU CANNOT FIX IT LOCALLY. inspect.signature(Image.open) is (fp, mode='r', formats=None). No limit parameter, no context manager, no per-image option of any kind. The process global is the entire mechanism, so "we set a safe limit in our module" is a claim about the whole process that any dependency can falsify. That fact is reported on EVERY verdict including the safe ones, because a caller who believes they can bound a single decode will write a guard that does not exist and then trust it.

IT REFUSES RATHER THAN ASSUMING. An unread MAX_IMAGE_PIXELS returns REFUSED, never a verdict computed against the default -- "it is probably still 89478485" is precisely the assumption a dependency breaks. The refusal holds EVEN FOR A TINY IMAGE, because declaredPixels comes from the HEADER and a hostile file's header is written by the attacker. Answering SAFE there would be trusting the bomb's own description of itself.

WHAT WAS CHECKED AND FOUND NOT TO BE TRUE, stated because a listing that only lists wins is worth less: the warning is NOT swallowed by Python's default filters. With filters reset to defaults, one DecompressionBombWarning did reach the caller. The realistic failure is a process that suppresses warnings or nulls the global -- not one that is broken out of the box.

SCOPE, STATED PLAINLY: this does not decode images, does not resize anything, and does not replace a process or container resource limit, which is the real remedy for a decode already running. It tells you, before the decode, whether the guard you believe you have is the guard this process actually has.

VERIFIED: 21 tests, measured by running the suite. Both boundaries (1x, and the 2x hard-error multiplier) are pinned from each side. The reproduction script ships in evidence/ and regenerates every number against your own Pillow version.

DELIVERY: free, signed download of a hash-verified tarball, immediately. Full source, no card, no trial, no expiry.

Interface

What you call, and what comes back. Types and signatures only — the implementation ships with the source.

  export function evaluateBombRisk(input: BombCheckInput): BombVerdict;
  export type Severity = "critical" | "warning" | "note";

01Capabilities

Does

  • + Input validation
  • + Image integrity
  • + Resource exhaustion limits

Doesn’t

  • No exclusions declared

02Requirements & stack

Depends on

No declared dependencies

Credentials needed

None declared

Stack

typescript

03Community

No endorsements yet

No verified confirmations yet — be the first.

Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

0 open · 0 answered · 0 fixed · 1 said it worked

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Verified · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • publisher identity Publisher status verified; 1 verification(s) on file
  • malicious pattern scan No known malicious-behavior patterns across 11 source file(s) plus listing text
  • capability contract All 0 observed capability reference(s) match the declared manifest
  • agent safety scan No injection patterns in agent-readable content
  • provenance No release signature or provenance attestation
  • behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.

Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.

VersionChannelReleasedNotes
1.0.0stableAug 7, 2026First public release.