Skip to content

fix: limit decompressed webhook payloads to 10 MiB - #246

Open
mogita wants to merge 1 commit into
masterfrom
fix/webhook-gzip-limit
Open

mogita wants to merge 1 commit into
masterfrom
fix/webhook-gzip-limit

Conversation

@mogita

@mogita mogita commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Submit a pull request

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Problem

gunzip_payload inflated a gzip body with no size limit before the signature check, so an unsigned request of a few hundred KB could expand to hundreds of MB and exhaust memory on a webhook endpoint.

Solution

Decompression now stops at 10 MiB and raises InvalidWebhookError ("gzip decompression failed: payload exceeds 10485760 bytes"). The same helper backs verify_and_parse_webhook, parse_sqs and parse_sns. Raise the cap if your events are larger:

import stream_chat.webhook

stream_chat.webhook.MAX_WEBHOOK_PAYLOAD_BYTES = 50 * 1024 * 1024

Uncompressed bodies are not limited, and multi-member gzip still inflates as before. Corrupt deflate data now raises InvalidWebhookError instead of leaking zlib.error. This matches the default and error message of the other server SDKs.

How to verify

  1. pytest stream_chat/tests/test_webhook_compression.py (passes locally on Python 3.8 and 3.12).
  2. The new tests inflate a body of exactly 10 MiB, reject 10 MiB + 1 byte through verify_and_parse_webhook and parse_sqs, and honour a custom limit.

Note

Medium Risk
Touches security-sensitive webhook ingestion (decompression runs before signature verification); behavior change rejects oversized gzip payloads that previously might have exhausted memory.

Overview
Adds a decompression size cap on webhook gzip handling so a small compressed body cannot expand without bound before HMAC verification.

gunzip_payload now reads through gzip.GzipFile with a 10 MiB ceiling (MAX_WEBHOOK_PAYLOAD_BYTES, overridable on stream_chat.webhook). Payloads that decompress larger raise InvalidWebhookError with a clear “payload exceeds … bytes” message—the same path used by verify_and_parse_webhook, parse_sqs, and parse_sns. Malformed deflate is surfaced as InvalidWebhookError instead of raw zlib.error.

Tests cover corrupt gzip, multi-member streams, exact-limit success, gzip-bomb rejection before signature checks, and a tunable limit via monkeypatch.

Reviewed by Cursor Bugbot for commit 1e51067. Bugbot is set up for automated code reviews on this repo. Configure here.

gunzip_payload runs before the signature check, so an unsigned gzip bomb could exhaust memory. Decompression now stops at MAX_WEBHOOK_PAYLOAD_BYTES (default 10 MiB) and raises InvalidWebhookError. Corrupt deflate data now raises InvalidWebhookError instead of zlib.error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant