Skip to content

Conversation

@XSAM
Copy link
Member

@XSAM XSAM commented Feb 6, 2026

Updates the baggage implementation to comply with https://www.w3.org/TR/baggage/#limits:

  • Changed maxMembers from 180 to 64 (the W3C compliance requirement)

    The resulting baggage-string contains 64 list-members or less.

  • Removed maxBytesPerMembers (4096) - this per-member limit was not part of the W3C spec

  • Added limit checking in extractMultiBaggage for multiple baggage headers:

    • Checks combined byte size across all headers (max 8192 bytes)
    • Checks combined member count across all headers (max 64 members)
    • Keep the first N complete members that fit within limits.

@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.3%. Comparing base (bcdf978) to head (bcf12b0).

Files with missing lines Patch % Lines
propagation/baggage.go 89.4% 1 Missing and 1 partial ⚠️
baggage/baggage.go 88.8% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##            main   #7880     +/-   ##
=======================================
- Coverage   81.7%   81.3%   -0.4%     
=======================================
  Files        304     304             
  Lines      23260   23287     +27     
=======================================
- Hits       19012   18947     -65     
- Misses      3862    3870      +8     
- Partials     386     470     +84     
Files with missing lines Coverage Δ
baggage/baggage.go 76.6% <88.8%> (-22.8%) ⬇️
propagation/baggage.go 82.3% <89.4%> (-17.7%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@XSAM XSAM marked this pull request as ready for review February 6, 2026 02:05
@MrAlias
Copy link
Contributor

MrAlias commented Feb 6, 2026

  • Drops all baggage if limits are exceeded per W3C spec

    If a platform cannot propagate all baggage, it MUST NOT propagate any partial list-members"

I don't think dropping all baggage is the correct interpretation of this requirement. We cannot propagate any "partial list-members", but it doesn't mean we cannot propagate complete list-members. Infact, the specification specifically states:

If either of the above conditions is not met, a platform MAY drop list-members until both conditions are met. The selection of which list-members to drop and their order is unspecified and left to the implementer.

While dropping all may indeed be a valid "selection" of list-members, I think we can do better and just take the first, or last n list-members the fit within the limits. Right?

@XSAM
Copy link
Member Author

XSAM commented Feb 7, 2026

While dropping all may indeed be a valid "selection" of list-members, I think we can do better and just take the first, or last n list-members the fit within the limits. Right?

Yeah. And, this will need to refactor baggage.Parse.

https://github.com/XSAM/opentelemetry-go/blob/0b29d8f4d883f334ce68981e269d34354ca494e1/baggage/baggage.go#L472

Copy link
Contributor

@MrAlias MrAlias left a comment

Choose a reason for hiding this comment

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

Overall this looks good. It addresses the critical security issue, looks W3C specification compliant, and is well tested. The error handling of Parse can be improved, and the issue in extractMultiBaggage, where it silently drops headers, needs to be fixed.

Comment on lines +85 to +87
// Header failed parsing (e.g., invalid format).
// Stop processing to maintain "first N" semantics.
break
Copy link
Contributor

Choose a reason for hiding this comment

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

Failing to parse the header value does not necessarily mean we have reached the size limit. There are other reasons a parse can fail (i.e. invalid member definitions).

Breaking here means that valid baggage that follows this header will be silently dropped:

Request with multiple Baggage headers:
  Baggage: key1=val1     <- Processed
  Baggage: invalid       <- Parse error: stops here
  Baggage: key2=val2     <- Not processed

Result: Only key1 is in baggage (key2 is LOST)

Instead this should continue to the next header. The size validation can still be checked for the result below.

Suggested change
// Header failed parsing (e.g., invalid format).
// Stop processing to maintain "first N" semantics.
break
continue

Or, the error returned from baggage.Parse could be compared with some known error exposed from that API to check what kind of error this is.

Copy link
Contributor

Choose a reason for hiding this comment

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

This also indicates a missing test case where an invalid header is sandwiched between two valid headers.

Comment on lines +90 to +92
// Non-empty header produced no members (e.g., all members exceeded limits).
// Stop processing to maintain "first N" semantics.
break
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, this may not always be the reason for the empty response. And it shouldn't mean all subsequent headers are dropped.

Suggested change
// Non-empty header produced no members (e.g., all members exceeded limits).
// Stop processing to maintain "first N" semantics.
break
continue

for memberStr := range strings.SplitSeq(bStr, listDelimiter) {
// Check member count limit.
if len(b) >= maxMembers {
break
Copy link
Contributor

Choose a reason for hiding this comment

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

Just breaking here means that the dropped members will be done silently.

Instead, can this return a partial result and an appropriate error describing the dropped member state?

memberBytes++ // comma separator
}
if totalBytes+memberBytes > maxBytesPerBaggageString {
break
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar here.

}

b, err := baggage.New(members...)
if err != nil || b.Len() == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this support a partial baggage if returned? Maybe send the error to the global error handler?

Copy link
Member Author

Choose a reason for hiding this comment

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

Should this support a partial baggage if returned?

I am not sure it is necessary as the limits have been enforced before baggage.New. We just use baggage.New to construct baggage.

Maybe send the error to the global error handler?

This can't be done as importing go.opentelemetry.io/otel would cause an import cycle.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use

func GetErrorHandler() ErrorHandler {
directly?

Copy link
Contributor

Choose a reason for hiding this comment

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

Regardless of the error handling, doesn't it make sense to return the partial baggage even in the error case? We are trying to comply with the baggage specification and just returning the parent context here when we may have truncated does not seem appropriate. It doesn't resolve the security issue trying to be resolved.

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.

4 participants