Skip to content

fix: cap request body size and prevent image decompression bombs - #1360

Merged
Nitya-003 merged 1 commit into
Nitya-003:mainfrom
Kirtan-pc:fix/flask-max-content-length-and-decompression-bomb
Aug 19, 2026
Merged

Nitya-003 merged 1 commit into
Nitya-003:mainfrom
Kirtan-pc:fix/flask-max-content-length-and-decompression-bomb

Conversation

@Kirtan-pc

Copy link
Copy Markdown
Contributor

Summary

This PR fixes Issue #1233: ml-service/app.py never sets app.config["MAX_CONTENT_LENGTH"], so Flask buffers unbounded request bodies. POST /predict-image calls Image.open() on attacker-controlled bytes; a crafted multi-GB upload or a tiny decompression bomb exhausts RAM/CPU and can OOM-kill the gunicorn workers.

Changes

  • ml-service/app.py (lines 16-22): Set app.config["MAX_CONTENT_LENGTH"] to ML_MAX_UPLOAD_MB (default 10 MB, configurable via env var) so Flask rejects oversized request bodies before buffering them.
  • ml-service/app.py (line 22): Set Image.MAX_IMAGE_PIXELS = 100_000_000 (100 megapixels) so Pillow raises DecompressionBombError before fully decoding a malicious image.
  • ml-service/app.py (lines 238-243): Added @app.errorhandler(413) returning structured JSON ({"error": "Payload too large", "details": "..."}) instead of Flask's default HTML error page.
  • ml-service/app.py (lines 284-288): Catch DecompressionBombError in predict_image and return a 413 with a clear message including the pixel limit.
  • ml-service/app.py (line 12): Import DecompressionBombError from PIL.

Fixes #1233

Copilot Review Feedback

Other suggestions were evaluated but intentionally left unchanged because they are pre-existing issues outside the scope of Issue #1233:

Comment Verdict Reason
Add per-endpoint MAX_CONTENT_LENGTH overrides Out of scope Flask does not natively support per-route content-length limits; would require custom middleware beyond a targeted fix
Stream image processing instead of buffering Out of scope Pre-existing architecture gap; Pillow's Image.open() on a stream still reads the full image for dimension validation
Add virus/malware scanning for uploads Out of scope Pre-existing feature gap; introduces new infrastructure dependency beyond a targeted DoS fix

Flask had no MAX_CONTENT_LENGTH configured, so POST /predict-image
buffered unbounded request bodies. A crafted multi-GB upload or tiny
decompression bomb could exhaust RAM/CPU and OOM-kill gunicorn workers.

- Set app.config['MAX_CONTENT_LENGTH'] to ML_MAX_UPLOAD_MB (default 10 MB)
- Set Image.MAX_IMAGE_PIXELS to 100M to reject decompression bombs before
  Pillow fully decodes them
- Add @app.errorhandler(413) returning structured JSON instead of Flask's
  default HTML error page
- Catch DecompressionBombError in predict_image and return 413 with details
- Import DecompressionBombError from PIL
@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown

@Kirtan-pc is attempting to deploy a commit to the Nitya Gosain's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the merge ready PR is mergeable and has no conflicts label Aug 17, 2026
@Nitya-003
Nitya-003 merged commit 331dcb0 into Nitya-003:main Aug 19, 2026
12 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge ready PR is mergeable and has no conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cap ML request size to prevent decompression-bomb DoS

2 participants