Skip to content

Commit 5b2f0b8

Browse files
authored
fix(agentless): enforce 64-bit trace IDs for agentless intake (#7820)
fix(agentless): enforce 64-bit trace IDs for agentless intake The agentless intake only accepts 64-bit trace IDs. This change ensures 64-bit enforcement at three layers: 1. Config: disable 128-bit trace ID generation when agentless is enabled 2. Encoder: strip _dd.p.tid meta tag (upper 64 bits of 128-bit IDs) 3. Encoder: truncate trace_id to lower 64 bits via .slice(-16) The config disable is in the calculated layer so env vars can still override it if needed; the encoder provides defense-in-depth regardless. Co-Authored-By: Claude Opus 4.6 <[email protected]> Co-authored-by: bryan.english <[email protected]>
1 parent ac15a16 commit 5b2f0b8

File tree

4 files changed

+38
-7
lines changed

4 files changed

+38
-7
lines changed

packages/dd-trace/src/config/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,8 @@ class Config {
11251125
setBoolean(calc, 'reportHostname', true)
11261126
// Clear sampling rules - server-side sampling handles this
11271127
calc['sampler.rules'] = []
1128+
// Agentless intake only accepts 64-bit trace IDs; disable 128-bit generation
1129+
setBoolean(calc, 'traceId128BitGenerationEnabled', false)
11281130
}
11291131

11301132
if (this.#isCiVisibility()) {

packages/dd-trace/src/encode/agentless-json.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ const SOFT_LIMIT = 8 * 1024 * 1024 // 8MB
1616
function formatSpan (span, isFirstSpan) {
1717
span = normalizeSpan(truncateSpan(span, false))
1818

19+
// Remove _dd.p.tid (the upper 64 bits of a 128-bit trace ID) since trace_id is truncated to lower 64 bits
20+
delete span.meta['_dd.p.tid']
21+
1922
if (span.span_events) {
2023
span.meta.events = JSON.stringify(span.span_events)
2124
delete span.span_events
@@ -45,7 +48,7 @@ function formatSpan (span, isFirstSpan) {
4548
*/
4649
function spanToJSON (span) {
4750
const result = {
48-
trace_id: span.trace_id.toString(16).toLowerCase(),
51+
trace_id: span.trace_id.toString(16).toLowerCase().slice(-16),
4952
span_id: span.span_id.toString(16).toLowerCase(),
5053
parent_id: span.parent_id.toString(16).toLowerCase(),
5154
name: span.name,

packages/dd-trace/test/config/index.spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3869,6 +3869,20 @@ rules:
38693869
assert.deepStrictEqual(config.sampler.rules, [])
38703870
})
38713871

3872+
it('should disable 128-bit trace ID generation when agentless is enabled', () => {
3873+
process.env._DD_APM_TRACING_AGENTLESS_ENABLED = 'true'
3874+
const config = getConfig()
3875+
assert.strictEqual(config.traceId128BitGenerationEnabled, false)
3876+
})
3877+
3878+
it('should allow env var to override agentless 128-bit disable', () => {
3879+
process.env._DD_APM_TRACING_AGENTLESS_ENABLED = 'true'
3880+
process.env.DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED = 'true'
3881+
const config = getConfig()
3882+
// Env var has higher priority than calculated; encoder truncation is the safety net
3883+
assert.strictEqual(config.traceId128BitGenerationEnabled, true)
3884+
})
3885+
38723886
it('should not affect other config when agentless is disabled', () => {
38733887
process.env._DD_APM_TRACING_AGENTLESS_ENABLED = 'false'
38743888
const config = getConfig()

packages/dd-trace/test/encode/agentless-json.spec.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,31 @@ describe('AgentlessJSONEncoder', () => {
9191
})
9292
})
9393

94-
it('should encode 128-bit trace IDs correctly', () => {
95-
// 128-bit trace IDs are used in W3C Trace Context
96-
data[0].trace_id = id('0123456789abcdef0123456789abcdef')
94+
it('should truncate 128-bit trace IDs to 64-bit', () => {
95+
// 128-bit trace IDs (e.g. from W3C Trace Context or 128-bit generation) should be truncated
96+
data[0].trace_id = id('aaaaaaaaaaaaaaaa0123456789abcdef')
9797

9898
encoder.encode(data)
9999

100100
const buffer = encoder.makePayload()
101101
const decoded = JSON.parse(buffer.toString())
102102
const span = decoded.traces[0].spans[0]
103103

104-
// Should be full 32-character hex string
105-
assert.strictEqual(span.trace_id, '0123456789abcdef0123456789abcdef')
106-
assert.strictEqual(span.trace_id.length, 32)
104+
// Should be lower 64 bits only (16-character hex string)
105+
assert.strictEqual(span.trace_id, '0123456789abcdef')
106+
assert.strictEqual(span.trace_id.length, 16)
107+
})
108+
109+
it('should strip _dd.p.tid from meta', () => {
110+
data[0].meta['_dd.p.tid'] = '0123456700000000'
111+
112+
encoder.encode(data)
113+
114+
const buffer = encoder.makePayload()
115+
const decoded = JSON.parse(buffer.toString())
116+
const span = decoded.traces[0].spans[0]
117+
118+
assert.strictEqual(span.meta['_dd.p.tid'], undefined)
107119
})
108120

109121
it('should include span fields with start time converted to seconds', () => {

0 commit comments

Comments
 (0)