From 9f7695de2d89fe8ad6d5bc278715c3b71617d671 Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Wed, 6 May 2026 15:51:06 +0000 Subject: [PATCH 01/10] Fix WSGI invalid request URI handling --- .../instrumentation/wsgi/__init__.py | 4 +-- .../tests/test_wsgi_middleware.py | 33 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index b30423d3bf..d3405ed5cc 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -273,7 +273,6 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, SanitizeValue, - _parse_url_query, detect_synthetic_user_agent, get_custom_headers, normalise_request_header_name, @@ -371,7 +370,8 @@ def collect_request_attributes( if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") if target: - path, query = _parse_url_query(target) + path = environ.get("PATH_INFO") + query = environ.get("QUERY_STRING") _set_http_target(result, target, path, query, sem_conv_opt_in_mode) else: # old semconv v1.20.0 diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index edb6655c5c..139c17edb7 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -821,8 +821,7 @@ def test_request_attributes_with_full_request_uri(self): HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", } expected_new = { - URL_PATH: "/3/library/urllib.parse.html", - URL_QUERY: "highlight=params", + URL_PATH: self.environ["PATH_INFO"], } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), @@ -836,6 +835,36 @@ def test_request_attributes_with_full_request_uri(self): expected_new.items(), ) + def test_request_attributes_with_invalid_request_uri_uses_wsgi_environ(self): + self.environ["REQUEST_URI"] = ( + "http://example.com/\\$%7B#context['xwork.MethodAccessor." + "denyMethodExecution']=!(#_memberAccess['allowStaticMethodAccess']" + "=true),(@java.lang.Runtime@getRuntime()).exec('id').waitFor()}" + '.action"' + ) + self.environ["PATH_INFO"] = "/safe/path" + self.environ["QUERY_STRING"] = "a=b" + + expected_old = { + HTTP_TARGET: self.environ["REQUEST_URI"], + } + expected_new = { + URL_PATH: "/safe/path", + URL_QUERY: "a=b", + } + + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes(self.environ).items(), + expected_old.items(), + ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _StabilityMode.HTTP, + ).items(), + expected_new.items(), + ) + def test_http_user_agent_attribute(self): self.environ["HTTP_USER_AGENT"] = "test-useragent" expected = {HTTP_USER_AGENT: "test-useragent"} From f8cc8351190b692b3e5eca2136bc0461b5aca6b9 Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Wed, 6 May 2026 15:54:57 +0000 Subject: [PATCH 02/10] Simplifying the test request_uri --- .../tests/test_wsgi_middleware.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 139c17edb7..f06ab030b6 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -836,12 +836,8 @@ def test_request_attributes_with_full_request_uri(self): ) def test_request_attributes_with_invalid_request_uri_uses_wsgi_environ(self): - self.environ["REQUEST_URI"] = ( - "http://example.com/\\$%7B#context['xwork.MethodAccessor." - "denyMethodExecution']=!(#_memberAccess['allowStaticMethodAccess']" - "=true),(@java.lang.Runtime@getRuntime()).exec('id').waitFor()}" - '.action"' - ) + # Previously raised ValueError when REQUEST_URI was parsed. + self.environ["REQUEST_URI"] = "http://example.com/[invalid" self.environ["PATH_INFO"] = "/safe/path" self.environ["QUERY_STRING"] = "a=b" From a1ca22a88e43ff03b7cb694a25c97042ce328ee0 Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Fri, 8 May 2026 12:40:34 +0000 Subject: [PATCH 03/10] Adding suggestion --- .../tests/test_wsgi_middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f06ab030b6..e8cf70133b 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -837,7 +837,7 @@ def test_request_attributes_with_full_request_uri(self): def test_request_attributes_with_invalid_request_uri_uses_wsgi_environ(self): # Previously raised ValueError when REQUEST_URI was parsed. - self.environ["REQUEST_URI"] = "http://example.com/[invalid" + self.environ["REQUEST_URI"] = "http://example.com[invalid" self.environ["PATH_INFO"] = "/safe/path" self.environ["QUERY_STRING"] = "a=b" From 9f969ead7a728aa68adca1414f739d888e97218b Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Mon, 11 May 2026 08:50:43 +0000 Subject: [PATCH 04/10] Fixing falcon tests --- .../opentelemetry-instrumentation-falcon/tests/test_falcon.py | 4 ++-- .../tests/test_wsgi_middleware.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 7dbf55724b..7f216a326e 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -247,7 +247,7 @@ def _test_method(self, method, old_semconv=True, new_semconv=False): SERVER_ADDRESS: "falconframework.org", URL_SCHEME: "http", SERVER_PORT: 80, - URL_PATH: "/" if self._has_fixed_http_target else "/hello", + URL_PATH: "/hello", CLIENT_PORT: 65133, NETWORK_PROTOCOL_VERSION: "1.1", "falcon.resource": "HelloWorldResource", @@ -360,7 +360,7 @@ def test_url_template_new_semconv(self): SERVER_ADDRESS: "falconframework.org", URL_SCHEME: "http", SERVER_PORT: 80, - URL_PATH: "/" if self._has_fixed_http_target else "/user/123", + URL_PATH: "/user/123", CLIENT_PORT: 65133, NETWORK_PROTOCOL_VERSION: "1.1", "falcon.resource": "UserResource", diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 8613432fa7..db4468759b 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -824,7 +824,9 @@ def test_request_attributes_with_full_request_uri(self): expected_new.items(), ) - def test_request_attributes_with_invalid_request_uri_uses_wsgi_environ(self): + def test_request_attributes_with_invalid_request_uri_uses_wsgi_environ( + self, + ): # Previously raised ValueError when REQUEST_URI was parsed. self.environ["REQUEST_URI"] = "http://example.com[invalid" self.environ["PATH_INFO"] = "/safe/path" From 2a204d8999d3f80978f658f126e8cd64b895d365 Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Mon, 11 May 2026 09:04:57 +0000 Subject: [PATCH 05/10] Adding explicit query_string for full_request_uri --- .../tests/test_wsgi_middleware.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index db4468759b..f5ff886699 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -805,12 +805,14 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_URI"] = ( "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request ) + self.environ["QUERY_STRING"] = "highlight=params" expected_old = { HTTP_HOST: "127.0.0.1:8080", HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", } expected_new = { URL_PATH: self.environ["PATH_INFO"], + URL_QUERY: "highlight=params", } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), From db1d8aa0bec402b80a41b29c29636fa9f9c4d4ce Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Mon, 11 May 2026 12:34:09 +0000 Subject: [PATCH 06/10] Adding changelog entry --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e27a0887f..838e86ed16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `opentelemetry-instrumentation-wsgi`: Use WSGI `PATH_INFO` and `QUERY_STRING` to derive stable HTTP URL attributes instead of parsing `REQUEST_URI`. + ([#4551](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4551)) - `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x ([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461)) - `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x @@ -73,7 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Enabled the flake8-tidy-import plugins rules for the ruff linter. These rules throw warnings for relative imports in the modules. -([#4395](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4395)) + ([#4395](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4395)) - `opentelemetry-instrumentation-asgi`: Respect `suppress_http_instrumentation` context in ASGI middleware to skip server span creation when HTTP instrumentation is suppressed ([#4375](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4375)) - `opentelemetry-instrumentation-confluent-kafka`: Loosen confluent-kafka upper bound to <3.0.0 @@ -90,7 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `opentelemetry-docker-tests`: Replace deprecated `SpanAttributes` from `opentelemetry.semconv.trace` with `opentelemetry.semconv._incubating.attributes` - ([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339)) + ([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339)) - `opentelemetry-instrumentation-confluent-kafka`: Skip `recv` span creation when `poll()` returns no message or `consume()` returns an empty list, avoiding empty spans on idle polls ([#4349](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4349)) - Fix intermittent `Core Contrib Test` CI failures caused by GitHub git CDN SHA propagation lag by installing core packages from the already-checked-out local copy instead of a second git clone From e6a470d96da0383829974f3be7324995ee340f85 Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Tue, 12 May 2026 07:00:35 +0000 Subject: [PATCH 07/10] Updating test with hardcoded value --- .../tests/test_wsgi_middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f5ff886699..ec60234279 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -811,7 +811,7 @@ def test_request_attributes_with_full_request_uri(self): HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", } expected_new = { - URL_PATH: self.environ["PATH_INFO"], + URL_PATH: "/", URL_QUERY: "highlight=params", } self.assertGreaterEqual( From 6f6c160fe4acbe2f140f77a194f190d53a2be8cb Mon Sep 17 00:00:00 2001 From: Dragos-georgian Ion Date: Wed, 13 May 2026 08:53:35 +0000 Subject: [PATCH 08/10] Add changelog fragment --- .changelog/4551.fixed | 1 + CHANGELOG.md | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 .changelog/4551.fixed diff --git a/.changelog/4551.fixed b/.changelog/4551.fixed new file mode 100644 index 0000000000..36a778bac9 --- /dev/null +++ b/.changelog/4551.fixed @@ -0,0 +1 @@ +`opentelemetry-instrumentation-wsgi`: use PATH_INFO and QUERY_STRING for URL attributes diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc7d71af5..3473b1baa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,8 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `opentelemetry-instrumentation-wsgi`: Use WSGI `PATH_INFO` and `QUERY_STRING` to derive stable HTTP URL attributes instead of parsing `REQUEST_URI`. - ([#4551](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4551)) - `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x ([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461)) - `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x @@ -92,7 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Enabled the flake8-tidy-import plugins rules for the ruff linter. These rules throw warnings for relative imports in the modules. - ([#4395](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4395)) +([#4395](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4395)) - `opentelemetry-instrumentation-asgi`: Respect `suppress_http_instrumentation` context in ASGI middleware to skip server span creation when HTTP instrumentation is suppressed ([#4375](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4375)) - `opentelemetry-instrumentation-confluent-kafka`: Loosen confluent-kafka upper bound to <3.0.0 @@ -113,7 +111,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `opentelemetry-docker-tests`: Replace deprecated `SpanAttributes` from `opentelemetry.semconv.trace` with `opentelemetry.semconv._incubating.attributes` - ([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339)) + ([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339)) - `opentelemetry-instrumentation-confluent-kafka`: Skip `recv` span creation when `poll()` returns no message or `consume()` returns an empty list, avoiding empty spans on idle polls ([#4349](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4349)) - Fix intermittent `Core Contrib Test` CI failures caused by GitHub git CDN SHA propagation lag by installing core packages from the already-checked-out local copy instead of a second git clone From 8738378db4a3a7fb29f69f83c8120b1cec3f1cd0 Mon Sep 17 00:00:00 2001 From: Dragos-Georgian Ion Date: Sat, 16 May 2026 09:58:00 +0700 Subject: [PATCH 09/10] Update test Add PATH_INFO to the environment for CONNECT request. --- .../tests/test_wsgi_middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index ec60234279..dc517d699e 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -805,6 +805,7 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_URI"] = ( "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request ) + self.environ["PATH_INFO"] = "/3/library/urllib.parse.html" self.environ["QUERY_STRING"] = "highlight=params" expected_old = { HTTP_HOST: "127.0.0.1:8080", From 477e831b02077a8ac79c9c22d286d7961dc4965d Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 18 May 2026 10:32:56 +0200 Subject: [PATCH 10/10] Apply suggestions from code review Co-authored-by: Riccardo Magliocchetti --- .changelog/4551.fixed | 2 +- .../tests/test_wsgi_middleware.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/4551.fixed b/.changelog/4551.fixed index 36a778bac9..b876160c66 100644 --- a/.changelog/4551.fixed +++ b/.changelog/4551.fixed @@ -1 +1 @@ -`opentelemetry-instrumentation-wsgi`: use PATH_INFO and QUERY_STRING for URL attributes +`opentelemetry-instrumentation-wsgi`: use `PATH_INFO` and `QUERY_STRING` for URL attributes instead of parsing `RAW_URI` or `REQUEST_URI` diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index dc517d699e..3f4af0c9b5 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -812,7 +812,7 @@ def test_request_attributes_with_full_request_uri(self): HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", } expected_new = { - URL_PATH: "/", + URL_PATH: "/3/library/urllib.parse.html", URL_QUERY: "highlight=params", } self.assertGreaterEqual(