-
-
Notifications
You must be signed in to change notification settings - Fork 33
fix: validate traceparent before using trace context #1897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,7 +9,20 @@ export function getTraceContext(traceContext: string | undefined) { | |||||||||||||||||||||||||||||||
| return { traceId: null, spanId: null, sampled: null }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const [_version, traceId, spanId, flags] = parts; | ||||||||||||||||||||||||||||||||
| const [version, traceId, spanId, flags] = parts; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const isValidTraceContext = | ||||||||||||||||||||||||||||||||
| /^[0-9a-f]{2}$/.test(version) && | ||||||||||||||||||||||||||||||||
| /^[0-9a-f]{32}$/.test(traceId) && | ||||||||||||||||||||||||||||||||
| traceId !== '00000000000000000000000000000000' && | ||||||||||||||||||||||||||||||||
| /^[0-9a-f]{16}$/.test(spanId) && | ||||||||||||||||||||||||||||||||
| spanId !== '0000000000000000' && | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: the 32/16 zeros are hard to read, should we simplify with as the preceding regex already checks for hex of the right length? |
||||||||||||||||||||||||||||||||
| /^[0-9a-f]{2}$/.test(flags); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+14
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, in the W3C Trace Context Citations:
Reject reserved The version field currently accepts Suggested patch const isValidTraceContext =
/^[0-9a-f]{2}$/.test(version) &&
+ version !== 'ff' &&
/^[0-9a-f]{32}$/.test(traceId) &&
traceId !== '00000000000000000000000000000000' &&
/^[0-9a-f]{16}$/.test(spanId) &&
spanId !== '0000000000000000' &&
/^[0-9a-f]{2}$/.test(flags);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment from Rabbit looks valid / worth addressing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
const isValidTraceContext =
/^[0-9a-f]{2}$/.test(version) &&
+ version !== 'ff' &&
/^[0-9a-f]{32}$/.test(traceId) &&
traceId !== '00000000000000000000000000000000' &&
/^[0-9a-f]{16}$/.test(spanId) &&
spanId !== '0000000000000000' &&
/^[0-9a-f]{2}$/.test(flags);Don't forget to add a corresponding test case for |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!isValidTraceContext) { | ||||||||||||||||||||||||||||||||
| return { traceId: null, spanId: null, sampled: null }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const sampled = (Number.parseInt(flags, 16) & 0x1) === 1; | ||||||||||||||||||||||||||||||||
| return { traceId, spanId, sampled }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { describe, expect, it } from 'vitest'; | ||
|
|
||
| import { getTraceContext } from '@/helpers/context-header'; | ||
|
|
||
| describe('getTraceContext', () => { | ||
| const nullTraceContext = { | ||
| traceId: null, | ||
| spanId: null, | ||
| sampled: null, | ||
| }; | ||
|
|
||
| it('returns null fields when traceparent is missing', () => { | ||
| expect(getTraceContext(undefined)).toEqual(nullTraceContext); | ||
| }); | ||
|
|
||
| it('parses a valid sampled traceparent header', () => { | ||
| expect( | ||
| getTraceContext( | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', | ||
| ), | ||
| ).toEqual({ | ||
| traceId: '4bf92f3577b34da6a3ce929d0e0e4736', | ||
| spanId: '00f067aa0ba902b7', | ||
| sampled: true, | ||
| }); | ||
| }); | ||
|
|
||
| it('parses a valid unsampled traceparent header', () => { | ||
| expect( | ||
| getTraceContext( | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00', | ||
| ), | ||
| ).toEqual({ | ||
| traceId: '4bf92f3577b34da6a3ce929d0e0e4736', | ||
| spanId: '00f067aa0ba902b7', | ||
| sampled: false, | ||
| }); | ||
| }); | ||
|
|
||
| it.each([ | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7', | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-extra', | ||
| 'zz-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', | ||
| '00-xyz-00f067aa0ba902b7-01', | ||
| '00-00000000000000000000000000000000-00f067aa0ba902b7-01', | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-xyz-01', | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000000-01', | ||
| '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-zz', | ||
| ])('returns null fields for invalid traceparent %s', (traceparent) => { | ||
|
Comment on lines
+40
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a regression case for invalid version The invalid-input matrix is missing the reserved-version case, so the strict-validation objective isn’t fully pinned by tests. Suggested patch it.each([
'00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7',
'00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-extra',
+ 'ff-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
'zz-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
'00-xyz-00f067aa0ba902b7-01',🤖 Prompt for AI Agents |
||
| expect(getTraceContext(traceparent)).toEqual(nullTraceContext); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would be worth adding an inline comment here to explain why we're doing this
e.g. "Reject malformed/spoofed traceparent so we don't emit junk trace IDs to Cloud Logging"