opentelemetry-util-http: add redact_query_string and apply it in the wsgi instrumentation - #4944
opentelemetry-util-http: add redact_query_string and apply it in the wsgi instrumentation#4944henry3260 wants to merge 2 commits into
Conversation
8d760d4 to
2d38b63
Compare
…wsgi instrumentation so sensitive query parameter values do not leak through http.target and url.query
2d38b63 to
dab53ab
Compare
Pull request dashboard statusWaiting on reviewers · refreshed 2026-08-21 22:20 UTC Review the latest changes. Status above doesn't look right?
|
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds safer query-string redaction to prevent presigned-URL credentials from leaking into WSGI span attributes (http.target / url.query), aligning WSGI instrumentation with other server instrumentations and semantic conventions.
Changes:
- Introduces
redact_query_string()inopentelemetry-util-httpto redact sensitive keys while preserving non-redacted bytes verbatim. - Applies query-value redaction to raw WSGI targets (
RAW_URI/REQUEST_URI) and toQUERY_STRINGbefore_set_http_target. - Adds unit tests for both the new utility function and WSGI attribute collection behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| util/opentelemetry-util-http/src/opentelemetry/util/http/init.py | Adds redact_query_string() and supporting import for safe redaction without URL parsing. |
| util/opentelemetry-util-http/tests/test_redact_query_string.py | Adds direct unit coverage for edge cases and byte-preserving behavior of new redaction helper. |
| instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/init.py | Redacts sensitive query values in request target and QUERY_STRING to prevent attribute leaks. |
| instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py | Adds tests ensuring WSGI does not leak secrets across semconv modes and target sources. |
| .changelog/4944.fixed | Notes the WSGI leak fix in changelog. |
| .changelog/4944.added | Notes the new util helper in changelog. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| def _setup_sensitive_request(self, target_key="RAW_URI"): | ||
| self.environ["PATH_INFO"] = "/download" | ||
| self.environ["QUERY_STRING"] = self._SENSITIVE_QUERY |
…h redaction test exercises the target source it names
Description
opentelemetry-instrumentation-wsgiwrites the raw query string intohttp.targetandurl.querywithout redaction, so presigned-URL credentials such asSignatureandAWSAccessKeyIdreach the trace backend.redact_urlis only called on theelsebranch ofcollect_request_attributes, which is taken when neitherRAW_URInorREQUEST_URIis set. gunicorn setsRAW_URIand uWSGI setsREQUEST_URI, so in production that branch is never reached; the werkzeug development server is what exercises it, which is why the gap does not show up locally.The semantic conventions say query string values for
AWSAccessKeyId,Signature,sigandX-Goog-SignatureSHOULD be redacted by default and replaced byREDACTED, with case-sensitive matching.tornadoandaiohttp-serveralready redact before calling_set_http_target; this bringswsgiin line.The existing
redact_query_parametersis not usable here: it callsurlparseon what is an attacker-controlled target, returning it unchanged when parsing fails, and itsparse_qs/urlencoderound trip drops valueless parameters and rewrites%20as+. This PR addsredact_query_stringtoopentelemetry-util-http, which takes a bare query string, splits on&and=without parsing a URL, percent-decodes parameter names before matching (so%53ignatureis redacted likeSignature), and leaves everything it does not match byte for byte.wsgiredacts the target's own query andQUERY_STRINGseparately, since PEP 3333 does not require a server to supply both.url.pathstill comes fromPATH_INFOand is unchanged.Type of change
How Has This Been Tested?
uv run tox -e py312-test-util-http— 74 passed. 15 new tests forredact_query_stringcovering each parameter inPARAMS_TO_REDACT, multiple matches in one query, case-sensitive matching, percent-encoded parameter names (%53ignature,%53%69gnature), parity withredact_query_parameterson a percent-encoded name, repeated parameters, valueless and blank parameters, a value containing[that would breakurlparse, and byte-for-byte round-tripping when nothing matches.uv run tox -e py312-test-instrumentation-wsgi— 61 passed. 9 new tests coveringdefault,httpandhttp/dupsemconv modes,RAW_URIandREQUEST_URIas the target source, a target that is not a parseable URL, an environ with noQUERY_STRING, a percent-encoded parameter name,url.pathstill reporting thePATH_INFOvalue, and a query with no sensitive parameters round-tripping unchanged. Each redaction test also asserts that no secret value appears in any attribute of the returned dict.Does This PR Require a Core Repo Change?
Checklist:
See contributing.md for styleguide, changelog guidelines, and more.