Skip to content

Bound jaeger-baggage parsing work by tokens rather than accepted entries - #8702

Open
trask wants to merge 4 commits into
open-telemetry:mainfrom
trask:trask-bound-jaeger-baggage-parsing-by-tokens
Open

Bound jaeger-baggage parsing work by tokens rather than accepted entries#8702
trask wants to merge 4 commits into
open-telemetry:mainfrom
trask:trask-bound-jaeger-baggage-parsing-by-tokens

Conversation

@trask

@trask trask commented Aug 6, 2026

Copy link
Copy Markdown
Member

JaegerPropagator.parseBaggageHeader only incremented entriesAdded for well-formed key=value tokens, so the maxEntries loop guard never engaged for a header consisting entirely of malformed tokens (e.g. a,a,a,...) and the loop ran to the end of the input. This bounds the loop on tokens parsed instead, so the per-header parse budget applies regardless of token validity.

Also guards the malformed-token log message with isLoggable, since its string concatenation was previously evaluated on every malformed token whether or not FINE was enabled.

This is a user-visible behavior change for pathological input: a header of 64+ malformed tokens followed by a valid entry no longer extracts that entry. Tests cover both sides of the boundary (63 vs. 64 malformed tokens).

Found while analyzing GHSA-r3rj-5mf2-3rcg. That report claimed the String.split pre-tokenization in this method is a DoS vector; I don't think it is (allocation is linear in the input at a flat ~17x, bounded by the transport header limit, and JaegerPropagator is deprecated and not a default propagator), so I've left the split in place and am closing that advisory separately. These two items are ordinary code quality fixes, not a security fix.

trask and others added 2 commits August 6, 2026 16:07
parseBaggageHeader only incremented entriesAdded for well-formed key=value
tokens, so the maxEntries loop guard never engaged for a header consisting
entirely of malformed tokens and the loop ran to the end of the input. Bound
the loop on tokens parsed instead, and guard the malformed-token log message
with isLoggable so its string concatenation is not evaluated when FINE is
disabled.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68223853-cb33-47c8-8486-b880f9f8b4e8
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68223853-cb33-47c8-8486-b880f9f8b4e8

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens JaegerPropagator baggage parsing so the per-header parse budget applies even when the header contains malformed tokens, and avoids unnecessary log-message string concatenation when FINE logging is disabled.

Changes:

  • Bound JaegerPropagator.parseBaggageHeader by tokens parsed (not just successfully added entries) and guard malformed-token logging with isLoggable(Level.FINE).
  • Added tests covering behavior at the malformed-token boundary (63 vs 64 malformed tokens).
  • Documented the user-visible behavior change in CHANGELOG.md.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
extensions/trace-propagators/src/main/java/io/opentelemetry/extension/trace/propagation/JaegerPropagator.java Enforces token-based parsing budget and avoids unnecessary log string concatenation.
extensions/trace-propagators/src/test/java/io/opentelemetry/extension/trace/propagation/JaegerPropagatorTest.java Adds coverage for malformed-token boundary behavior.
CHANGELOG.md Notes the new token-based stop condition for jaeger-baggage parsing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.64%. Comparing base (db1d6be) to head (2d0d3e9).

Files with missing lines Patch % Lines
.../extension/trace/propagation/JaegerPropagator.java 50.00% 0 Missing and 2 partials ⚠️

❌ Your patch check has failed because the patch coverage (50.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8702      +/-   ##
============================================
+ Coverage     91.63%   91.64%   +0.01%     
- Complexity    10349    10350       +1     
============================================
  Files          1003     1003              
  Lines         27207    27210       +3     
  Branches       3198     3199       +1     
============================================
+ Hits          24931    24937       +6     
+ Misses         1572     1566       -6     
- Partials        704      707       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

> extensions/trace-propagators/src/main/java/io/opentelemetry/extension/trace/propagation/JaegerPropagator.java: The Javadoc says parsing stops once `maxBytes` is exceeded, but the implementation breaks before exceeding `maxBytes` (it stops when the next entry would exceed the budget). Also, `maxEntries` now represents a token budget rather than "entries added", so renaming the parameter would make the intent clearer for future readers/maintainers.

> extensions/trace-propagators/src/test/java/io/opentelemetry/extension/trace/propagation/JaegerPropagatorTest.java: This parameterized-test case description says "entry limit", but the behavior being exercised is a token-parse budget (malformed tokens now count toward the limit). Updating the description avoids confusion when reading test failures.

Analysis: Both comments have the same root cause: after switching the per-header bound from accepted entries to parsed tokens, the surrounding naming and prose still described the old entry-limit semantics. The Javadoc now says parsing stops once the next entry would exceed maxBytes, which is what the byte check inside the loop actually does. The parameter is renamed maxEntries -> maxTokens so its name matches the tokensParsed counter it bounds. The loop-guard clause bytesAdded > maxBytes was also dropped because it was unreachable: bytesAdded only grows through the guarded branch that breaks whenever the next entry would push it past maxBytes, so bytesAdded can never exceed maxBytes on a subsequent iteration. Removing it leaves exactly one byte-budget check and makes code and Javadoc describe the same rule. The test argument-set name now says "token budget" instead of "entry limit", matching the malformed-token behavior it exercises.

Upsides: The Javadoc, the parameter name, the loop structure, and the test case name all describe one consistent rule, so a future reader does not have to reverse-engineer whether the bound counts entries or tokens. Dropping the unreachable clause removes a condition that contradicted the documented behavior. A failure of the malformed-token case now names the budget it actually tests.

Downsides: No material downside identified. The changes are naming, documentation, and removal of an unreachable condition; extract behavior is unchanged and the existing JaegerPropagatorTest cases, including both malformed-token budget cases, still pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (2)

extensions/trace-propagators/src/main/java/io/opentelemetry/extension/trace/propagation/JaegerPropagator.java:305

  • maxTokens is currently used as both a token-parse budget and an implicit “remaining entries” budget (since the only caller passes MAX_BAGGAGE_ENTRIES - entriesAdded). This can make the per-header token budget much smaller than 64 when some uberctx- entries were already accepted, and it can drop otherwise-eligible header entries that appear after malformed tokens (e.g., 63 uberctx- entries + jaeger-baggage header starting with one malformed token then a valid token would parse only 1 token and never reach the valid one). If the intent is a fixed per-header token budget of 64 (as described in the PR/changelog), consider separating maxTokens (work bound) from maxEntriesRemaining (acceptance bound) and guarding both inside the loop; then the caller can pass a constant token budget while still enforcing the overall 64-entry limit.
  /**
   * Parses a single {@code jaeger-baggage} header, stopping after {@code maxTokens} tokens or once
   * the next entry would exceed {@code maxBytes}. The token bound is per header and counts
   * malformed tokens, so a header of entirely malformed tokens cannot keep the loop running to the
   * end of the input.

CHANGELOG.md:10

  • Changelog heading level looks inconsistent in the Unreleased section: #### Extensions nests Extensions under ### API, but the surrounding top-level sections are ### API, ### SDK, ### Shims, etc. If this entry is meant to be its own top-level section (consistent with the rest of the changelog), it should be ### Extensions.
#### Extensions

> `maxTokens` is currently used as both a token-parse budget and an implicit "remaining entries" budget (since the only caller passes `MAX_BAGGAGE_ENTRIES - entriesAdded`). This can make the per-header token budget much smaller than 64 when some `uberctx-` entries were already accepted, and it can drop otherwise-eligible header entries that appear after malformed tokens (e.g., 63 `uberctx-` entries + `jaeger-baggage` header starting with one malformed token then a valid token would parse only 1 token and never reach the valid one). If the intent is a fixed per-header token budget of 64 (as described in the PR/changelog), consider separating `maxTokens` (work bound) from `maxEntriesRemaining` (acceptance bound) and guarding both inside the loop; then the caller can pass a constant token budget while still enforcing the overall 64-entry limit.

Analysis: The comment is correct. Switching the loop guard from entriesAdded to tokensParsed while still passing MAX_BAGGAGE_ENTRIES - entriesAdded collapsed two distinct bounds into one argument, so already-accepted uberctx- entries shrank the per-header parse budget below the 64 tokens the changelog documents, and a single malformed token could permanently consume an entry slot that was still available. parseBaggageHeader now takes maxTokens and maxEntries separately and guards both at the top of the loop. The caller passes the new MAX_BAGGAGE_HEADER_TOKENS constant for the work bound and MAX_BAGGAGE_ENTRIES - entriesAdded for the acceptance bound, so the overall 64-entry and 8192-byte caps are still enforced exactly as before. A new parameterized case covers the reviewer's scenario: 63 uberctx- entries followed by a header of one malformed token and one valid token now yields 64 entries; it fails against the previous single-budget code.

Upsides: The per-header token budget is now the fixed 64 the PR description and changelog promise, independent of how many prefix entries preceded it. Malformed tokens no longer discard entry slots that valid entries could have used, removing an extraction regression relative to the pre-PR behavior. The two bounds are named and guarded separately, so neither can silently change the other's meaning again. Parse work stays bounded at 64 tokens per header, which was the point of the original change.

Downsides: parseBaggageHeader gains a fifth parameter, and the worst-case token count per header rises from "remaining entries" to a constant 64, so a carrier with 63 prefix entries plus a fully malformed header now parses up to 64 tokens instead of 1. That ceiling is the one the change set out to establish and is negligible next to the header length itself. Extraction results are unchanged for every previously covered case.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@trask
trask marked this pull request as ready for review August 7, 2026 14:50
@trask
trask requested a review from a team as a code owner August 7, 2026 14:50
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Aug 7, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on maintainers · refreshed 2026-08-07 15:07 UTC

Merge when ready.

Non-blocking check failure: codecov/patch

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

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.

3 participants