Skip to content

fix: Enforce W3C Baggage limits on the extract path - #2298

Open
serhiy-bzhezytskyy wants to merge 3 commits into
open-telemetry:mainfrom
serhiy-bzhezytskyy:fix/baggage-extract-limits
Open

fix: Enforce W3C Baggage limits on the extract path#2298
serhiy-bzhezytskyy wants to merge 3 commits into
open-telemetry:mainfrom
serhiy-bzhezytskyy:fix/baggage-extract-limits

Conversation

@serhiy-bzhezytskyy

Copy link
Copy Markdown

The baggage extract path decodes the inbound header without applying the MAX_ENTRIES (180), MAX_ENTRY_LENGTH (4096) and MAX_TOTAL_LENGTH (8192) limits that the inject path (#encode) enforces, so an unbounded inbound header is parsed in full. This is the class the Java runtime fixed as CVE-2026-45292 (unbounded memory/CPU via W3C Baggage parsing, fixed in opentelemetry-java 1.62.0). Fixes #2163.

extract now enforces the same three limits through a small private helper mirroring #encode: over-limit entries are dropped where the limit is reached, and the entries decoded before that are kept. Three extract-side tests mirror the existing inject-side limit tests; each fails on main and passes with the change.

Assisted-By: Claude Fable 5

The inject path (#encode) applies MAX_ENTRIES, MAX_ENTRY_LENGTH and
MAX_TOTAL_LENGTH, but extract parsed the inbound header with none of
them, decoding an unbounded baggage header in full — the class the Java
runtime tracks as CVE-2026-45292. extract now mirrors the inject limits
through a private helper: over-limit entries are dropped at the point
the limit is reached and already-decoded entries are kept. Adds three
extract-side tests mirroring the existing inject-side limit tests.

Fixes open-telemetry#2163

Assisted-By: Claude Fable 5
@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 17, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: serhiy-bzhezytskyy / name: serhiy-bzhezytskyy (282fcdd)

@simi

simi commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

notice this still reads full header into memory

decode_entries already stopped after 180 entries, but the header was split
eagerly first, so a large header materialised every entry before any limit
applied. Walk it lazily and stop at the entry limit instead. On a 7.8 MB header
with 500k entries that is 180 strings instead of 500,000.

Whitespace is now stripped per entry rather than over the whole header, which
keeps the parsing semantics and drops a copy of the header. Empty entries are
skipped explicitly: without the global gsub a whitespace-only header yields one
empty entry, which raised inside decode_entries and was swallowed by the rescue.

Assisted-By: Claude Fable 5
The allocation-count test covered the bound only indirectly. Extract a
1,000-entry header and a 100,000-entry one and assert the second allocates less
than twice the first, so the assertion is relative and does not depend on a Ruby
version's absolute counts.

Assisted-By: Claude Fable 5
@serhiy-bzhezytskyy

Copy link
Copy Markdown
Author

Thanks, you're right. Fixed and covered it with two tests: extract now walks the header lazily and stops at 180 entries, so nothing past the 180th is scanned or copied. The tests assert the property, that allocations do not grow with the header length; the numbers I measured separately are 180 strings instead of 500,000 and 0.04 ms instead of 53 ms on a 7.8 MB header with 500k entries.

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.

W3C Baggage extract does not enforce the MAX_ENTRIES / MAX_ENTRY_LENGTH / MAX_TOTAL_LENGTH constants the inject path enforces

2 participants