Skip to content

Commit 27353de

Browse files
authored
fix(baggage): ignore legacy baggage keys if not a valid header (#7214)
The legacy baggage implementation is using headers for transporting the entries. That is causing issues in case the used key is not a valid header. Thus, ignore those while still keeping the baggage entries as before for other usage. Fixes: #7043
1 parent d27d8df commit 27353de

2 files changed

Lines changed: 48 additions & 8 deletions

File tree

packages/dd-trace/src/opentracing/propagation/text_map.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)
3636
const baggageExpr = new RegExp(`^${baggagePrefix}(.+)$`)
3737
const tagKeyExpr = /^_dd\.p\.[\x21-\x2B\x2D-\x7E]+$/ // ASCII minus spaces and commas
3838
const tagValueExpr = /^[\x20-\x2B\x2D-\x7E]*$/ // ASCII minus commas
39+
// RFC7230 token (used by HTTP header field-name) and compatible with Node's header name validation.
40+
// See https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
41+
const httpHeaderNameExpr = /^[0-9A-Za-z!#$%&'*+\-.^_`|~]+$/
3942
const traceparentExpr = /^([a-f0-9]{2})-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})(-.*)?$/i
4043
const traceparentKey = 'traceparent'
4144
const tracestateKey = 'tracestate'
@@ -130,9 +133,21 @@ class TextMapPropagator {
130133

131134
_injectBaggageItems (spanContext, carrier) {
132135
if (this._config.legacyBaggageEnabled) {
133-
spanContext?._baggageItems && Object.keys(spanContext._baggageItems).forEach(key => {
134-
carrier[baggagePrefix + key] = String(spanContext._baggageItems[key])
135-
})
136+
const baggageItems = spanContext?._baggageItems
137+
if (baggageItems) {
138+
for (const key of Object.keys(baggageItems)) {
139+
const headerName = baggagePrefix + key
140+
141+
// Legacy OpenTracing baggage is propagated as individual headers (ot-baggage-*),
142+
// so it must always be representable as a valid HTTP header name.
143+
if (!httpHeaderNameExpr.test(headerName)) {
144+
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
145+
continue
146+
}
147+
148+
carrier[headerName] = String(baggageItems[key])
149+
}
150+
}
136151
}
137152
if (this._hasPropagationStyle('inject', 'baggage')) {
138153
let baggage = ''
@@ -646,9 +661,10 @@ class TextMapPropagator {
646661

647662
_extractBaggageItems (carrier, spanContext) {
648663
if (!this._hasPropagationStyle('extract', 'baggage')) return
649-
if (!carrier || !carrier.baggage) return
664+
if (!carrier?.baggage) return
650665
const baggages = carrier.baggage.split(',')
651-
const keysToSpanTag = this._config.baggageTagKeys === '*'
666+
const tagAllKeys = this._config.baggageTagKeys === '*'
667+
const keysToSpanTag = tagAllKeys
652668
? undefined
653669
: new Set(this._config.baggageTagKeys.split(','))
654670
for (const keyValue of baggages) {
@@ -665,7 +681,7 @@ class TextMapPropagator {
665681
removeAllBaggageItems()
666682
return
667683
}
668-
if (spanContext && (this._config.baggageTagKeys === '*' || keysToSpanTag.has(key))) {
684+
if (spanContext && (tagAllKeys || keysToSpanTag?.has(key))) {
669685
spanContext._trace.tags['baggage.' + key] = value
670686
}
671687
setBaggageItem(key, value)

packages/dd-trace/test/opentracing/propagation/text_map.spec.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,25 @@ describe('TextMapPropagator', () => {
126126
assert.strictEqual(carrier.baggage, undefined)
127127
})
128128

129+
it('should skip legacy baggage items that cannot be encoded as a valid HTTP header name', () => {
130+
const carrier = {}
131+
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
132+
const spanContext = createContext({
133+
baggageItems: {
134+
ok: 'yes',
135+
'not ok': 'no'
136+
}
137+
})
138+
139+
propagator.inject(spanContext, carrier)
140+
141+
assert.strictEqual(carrier['ot-baggage-ok'], 'yes')
142+
assert.strictEqual(carrier['ot-baggage-not ok'], undefined)
143+
144+
sinon.assert.calledWith(tracerMetrics.count, 'context_header_style.malformed', ['header_style:baggage'])
145+
sinon.assert.called(tracerMetrics.count().inc)
146+
})
147+
129148
it('should handle special characters in baggage', () => {
130149
const carrier = {}
131150
setBaggageItem('",;\\()/:<=>?@[]{}🐶é我', '",;\\🐶é我')
@@ -135,12 +154,17 @@ describe('TextMapPropagator', () => {
135154
})
136155

137156
it('should drop excess baggage items when there are too many pairs', () => {
157+
/** @type {Record<string, unknown>} */
138158
const carrier = {}
139159
for (let i = 0; i < config.baggageMaxItems + 1; i++) {
140-
setBaggageItem(`key-${i}`, i)
160+
setBaggageItem(`key-${i}`, String(i))
141161
}
142162
propagator.inject(undefined, carrier)
143-
assert.strictEqual(carrier.baggage.split(',').length, config.baggageMaxItems)
163+
const baggage = carrier.baggage
164+
if (typeof baggage !== 'string') {
165+
throw new Error('Expected baggage header to be a string')
166+
}
167+
assert.strictEqual(baggage.split(',').length, config.baggageMaxItems)
144168
})
145169

146170
it('should drop excess baggage items when the resulting baggage header contains many bytes', () => {

0 commit comments

Comments
 (0)