From 9828d17d076b17bf7a56acac79969fd51c687352 Mon Sep 17 00:00:00 2001 From: Prasun Srivastav Date: Mon, 9 Feb 2026 19:14:45 +0530 Subject: [PATCH 1/8] fix(opentelemetry): validate x-request-id before using it as trace_id --- apisix/plugins/opentelemetry.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 487c143819ce..fe39270e8a15 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -56,6 +56,13 @@ local lrucache = core.lrucache.new({ local asterisk = string.byte("*", 1) +local function is_valid_trace_id(trace_id) + if not trace_id or #trace_id ~= 32 then + return false + end + return trace_id:match("^[0-9a-fA-F]+$") ~= nil +end + local metadata_schema = { type = "object", properties = { @@ -230,12 +237,22 @@ end local function create_tracer_obj(conf, plugin_info) - if plugin_info.trace_id_source == "x-request-id" then - id_generator.new_ids = function() - local trace_id = core.request.headers()["x-request-id"] or ngx_var.request_id - return trace_id, id_generator.new_span_id() + if plugin_info.trace_id_source == "x-request-id" then + local original_new_ids = id_generator.new_ids + + id_generator.new_ids = function() + local header_trace_id = core.request.headers()["x-request-id"] + or ngx_var.request_id + + if is_valid_trace_id(header_trace_id) then + return header_trace_id, id_generator.new_span_id() end + + -- fallback to default generator for invalid values (e.g. UUID) + return original_new_ids() end +end + -- create exporter local exporter = otlp_exporter_new(exporter_client_new(plugin_info.collector.address, plugin_info.collector.request_timeout, From 3f93a20f5c292b827b921eb42a9ee72b479fec22 Mon Sep 17 00:00:00 2001 From: Prasun Srivastav Date: Thu, 12 Feb 2026 14:18:48 +0530 Subject: [PATCH 2/8] test(opentelemetry): add test for invalid x-request-id handling --- t/plugin/opentelemetry.t | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index daf91e39e8bd..6454e40dc136 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -434,3 +434,16 @@ HEAD /specific_status tail -n 1 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*\/specific_status.*/ + + + +=== TEST 20: invalid x-request-id should not crash +--- request +GET /opentracing +--- more_headers +X-Request-Id: 550e8400-e29b-41d4-a716-446655440000 +--- wait: 2 +--- response_body +opentracing +--- no_error_log +[error] \ No newline at end of file From 28e8dbcdda9003ab7fe1f427cdc298ba702350ad Mon Sep 17 00:00:00 2001 From: Prasun Srivastav Date: Fri, 13 Feb 2026 11:55:31 +0530 Subject: [PATCH 3/8] test(opentelemetry): add test for invalid x-request-id handling --- t/plugin/opentelemetry.t | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index 6454e40dc136..1fef963059a6 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -437,7 +437,43 @@ qr/.*\/specific_status.*/ -=== TEST 20: invalid x-request-id should not crash +=== TEST 20: recreate route for invalid x-request-id test +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/opentracing" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t + + + +=== TEST 21: invalid x-request-id should not crash --- request GET /opentracing --- more_headers @@ -446,4 +482,4 @@ X-Request-Id: 550e8400-e29b-41d4-a716-446655440000 --- response_body opentracing --- no_error_log -[error] \ No newline at end of file +[error] From b0bd8fb0970a4f68cc3c48ae9bf5386d1d13f4cf Mon Sep 17 00:00:00 2001 From: Prasun Srivastav Date: Fri, 13 Feb 2026 23:57:58 +0530 Subject: [PATCH 4/8] fix(opentelemetry): validate x-request-id trace_id and add fallback - Add is_valid_trace_id() to validate 32-char hex trace IDs - Reject all-zero trace IDs per W3C spec - Prevent multiple id_generator overrides - Add tests for invalid X-Request-Id fallback behavior Signed-off-by: Prasun Srivastav --- apisix/plugins/opentelemetry.lua | 22 ++++++++++++++++++---- t/plugin/opentelemetry.t | 13 +++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index fe39270e8a15..bdddb3e1b454 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -29,6 +29,8 @@ local exporter_client_new = require("opentelemetry.trace.exporter.http_client"). local otlp_exporter_new = require("opentelemetry.trace.exporter.otlp").new local batch_span_processor_new = require("opentelemetry.trace.batch_span_processor").new local id_generator = require("opentelemetry.trace.id_generator") +local original_new_ids = id_generator.new_ids +local id_generator_overridden = false local tracer_provider_new = require("opentelemetry.trace.tracer_provider").new local span_kind = require("opentelemetry.trace.span_kind") @@ -60,7 +62,18 @@ local function is_valid_trace_id(trace_id) if not trace_id or #trace_id ~= 32 then return false end - return trace_id:match("^[0-9a-fA-F]+$") ~= nil + + -- must be lowercase hex + if not trace_id:match("^[0-9a-fA-F]+$") then + return false + end + + -- W3C Trace Context: all-zero trace_id is invalid + if trace_id == "00000000000000000000000000000000" then + return false + end + + return true end local metadata_schema = { @@ -237,10 +250,10 @@ end local function create_tracer_obj(conf, plugin_info) - if plugin_info.trace_id_source == "x-request-id" then - local original_new_ids = id_generator.new_ids - + if plugin_info.trace_id_source == "x-request-id" + and not id_generator_overridden then id_generator.new_ids = function() + local header_trace_id = core.request.headers()["x-request-id"] or ngx_var.request_id @@ -251,6 +264,7 @@ local function create_tracer_obj(conf, plugin_info) -- fallback to default generator for invalid values (e.g. UUID) return original_new_ids() end + id_generator_overridden = true end -- create exporter diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index 1fef963059a6..233beeccc792 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -483,3 +483,16 @@ X-Request-Id: 550e8400-e29b-41d4-a716-446655440000 opentracing --- no_error_log [error] + + + +=== TEST 22: invalid x-request-id should still generate a valid trace +--- request +GET /opentracing +--- more_headers +X-Request-Id: 550e8400-e29b-41d4-a716-446655440000 +--- wait: 2 +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/"traceId":"[0-9a-f]{32}"/ From a7a32259b962572c726384dcc1aff402b4e1a2c5 Mon Sep 17 00:00:00 2001 From: prasun srivastav Date: Sat, 14 Feb 2026 13:43:15 +0530 Subject: [PATCH 5/8] Update t/plugin/opentelemetry.t Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- t/plugin/opentelemetry.t | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index 233beeccc792..a12268cd9c0b 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -495,4 +495,40 @@ X-Request-Id: 550e8400-e29b-41d4-a716-446655440000 --- exec tail -n 1 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval -qr/"traceId":"[0-9a-f]{32}"/ +qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ + + +=== TEST 23: all-zero x-request-id should not be used as trace id +--- request +GET /opentracing +--- more_headers +X-Request-Id: 00000000000000000000000000000000 +--- wait: 2 +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ + + +=== TEST 24: uppercase x-request-id should still generate a valid trace id +--- request +GET /opentracing +--- more_headers +X-Request-Id: 550E8400-E29B-41D4-A716-446655440000 +--- wait: 2 +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ + + +=== TEST 25: malformed length x-request-id should still generate a valid trace id +--- request +GET /opentracing +--- more_headers +X-Request-Id: 550e8400e29b41d4a7164466 +--- wait: 2 +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ From ccde264040ea4400bc899f0a62284a725d102bcd Mon Sep 17 00:00:00 2001 From: prasun srivastav Date: Sat, 14 Feb 2026 13:43:28 +0530 Subject: [PATCH 6/8] Update apisix/plugins/opentelemetry.lua Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apisix/plugins/opentelemetry.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index bdddb3e1b454..251bc5e809cb 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -64,7 +64,7 @@ local function is_valid_trace_id(trace_id) end -- must be lowercase hex - if not trace_id:match("^[0-9a-fA-F]+$") then + if not trace_id:match("^[0-9a-f]+$") then return false end From 66a91967e5b7c523ad203db27e4f77be3149f976 Mon Sep 17 00:00:00 2001 From: Prasun Srivastav Date: Tue, 24 Feb 2026 16:42:32 +0530 Subject: [PATCH 7/8] fix: simplify id_generator override and remove global flag --- apisix/plugins/opentelemetry.lua | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 251bc5e809cb..b1c9c53b6d5d 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -29,8 +29,6 @@ local exporter_client_new = require("opentelemetry.trace.exporter.http_client"). local otlp_exporter_new = require("opentelemetry.trace.exporter.otlp").new local batch_span_processor_new = require("opentelemetry.trace.batch_span_processor").new local id_generator = require("opentelemetry.trace.id_generator") -local original_new_ids = id_generator.new_ids -local id_generator_overridden = false local tracer_provider_new = require("opentelemetry.trace.tracer_provider").new local span_kind = require("opentelemetry.trace.span_kind") @@ -250,23 +248,20 @@ end local function create_tracer_obj(conf, plugin_info) - if plugin_info.trace_id_source == "x-request-id" - and not id_generator_overridden then - id_generator.new_ids = function() - - local header_trace_id = core.request.headers()["x-request-id"] - or ngx_var.request_id - - if is_valid_trace_id(header_trace_id) then - return header_trace_id, id_generator.new_span_id() - end + if plugin_info.trace_id_source == "x-request-id" then + local _original_new_ids = id_generator.new_ids - -- fallback to default generator for invalid values (e.g. UUID) - return original_new_ids() - end - id_generator_overridden = true -end + id_generator.new_ids = function() + local trace_id = core.request.headers()["x-request-id"] + or ngx_var.request_id + if is_valid_trace_id(trace_id) then + return trace_id, id_generator.new_span_id() + end + + return _original_new_ids() + end + end -- create exporter local exporter = otlp_exporter_new(exporter_client_new(plugin_info.collector.address, plugin_info.collector.request_timeout, From d21f2433f4edf8713aa2807a7abdbc4827b61ee0 Mon Sep 17 00:00:00 2001 From: Prasun Srivastav Date: Fri, 6 Mar 2026 10:36:46 +0530 Subject: [PATCH 8/8] test: reindex opentelemetry tests to fix CI --- t/plugin/opentelemetry.t | 3 +++ 1 file changed, 3 insertions(+) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index a12268cd9c0b..4c78da4229e2 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -498,6 +498,7 @@ tail -n 1 ci/pod/otelcol-contrib/data-otlp.json qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ + === TEST 23: all-zero x-request-id should not be used as trace id --- request GET /opentracing @@ -510,6 +511,7 @@ tail -n 1 ci/pod/otelcol-contrib/data-otlp.json qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ + === TEST 24: uppercase x-request-id should still generate a valid trace id --- request GET /opentracing @@ -522,6 +524,7 @@ tail -n 1 ci/pod/otelcol-contrib/data-otlp.json qr/"traceId":"(?!0{32})[0-9a-f]{32}"/ + === TEST 25: malformed length x-request-id should still generate a valid trace id --- request GET /opentracing