From cf5b85174339376fd3651d464a78a9365499e49b Mon Sep 17 00:00:00 2001 From: Rook Date: Mon, 16 Mar 2026 06:59:45 -0400 Subject: [PATCH] fix: redact sensitive headers in debug logs (fixes #1196) Prevent API keys and other sensitive headers from being logged in plaintext when debug logging is enabled. - Add _redact_sensitive_headers helper method - Use redacted headers in debug logging - Fixes security vulnerability where Authorization headers with API keys were exposed --- src/openai/_base_client.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index cf4571bf45..fa6b18c30f 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -476,6 +476,26 @@ def _prepare_url(self, url: str) -> URL: def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: return SSEDecoder() + def _redact_sensitive_headers(self, headers): + """Redact sensitive headers (API keys, auth tokens) for safe logging.""" + if not headers or not isinstance(headers, dict): + return headers + + redacted = {} + sensitive_keys = { + "authorization", "api-key", "x-api-key", + "x-openai-api-key", "openai-api-key" + } + + for key, value in headers.items(): + key_lower = key.lower() + if any(sensitive in key_lower for sensitive in sensitive_keys): + redacted[key] = "[REDACTED]" + else: + redacted[key] = value + + return redacted + def _build_request( self, options: FinalRequestOptions, @@ -483,11 +503,10 @@ def _build_request( retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): - log.debug( - "Request options: %s", - model_dump( - options, - exclude_unset=True, + safe_options = options.model_dump() + if "headers" in safe_options: + safe_options["headers"] = self._redact_sensitive_headers(safe_options["headers"]) + log.debug("Request options: %s", safe_options) # Pydantic v1 can't dump every type we support in content, so we exclude it for now. exclude={ "content",