Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions googleapiclient/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import functools
import inspect
import logging
import re
import urllib.parse

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -205,3 +206,25 @@ def _add_query_parameter(url, name, value):
return url
else:
return update_query_params(url, {name: value})


# The developer API key travels on every request as the ``key`` query
# parameter. It is a credential and must not be written to logs, even at
# DEBUG level (CWE-532).
_SENSITIVE_QUERY_PARAM_RE = re.compile(r"([?&]key=)[^&#]*", re.IGNORECASE)


def _redact_sensitive_url(url):
"""Redact credential query parameters from a URL before logging it.

Args:
url: string, the request URL that may carry an API key.

Returns:
A copy of the URL with the ``key`` query parameter value replaced by
``REDACTED``. Non-string input is returned unchanged.
"""
try:
return _SENSITIVE_QUERY_PARAM_RE.sub(r"\1REDACTED", url)
except TypeError:
return url
21 changes: 17 additions & 4 deletions googleapiclient/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@

# Local imports
from googleapiclient import _auth, mimeparse
from googleapiclient._helpers import _add_query_parameter, positional
from googleapiclient._helpers import (
_add_query_parameter,
_redact_sensitive_url,
positional,
)
from googleapiclient.errors import (
HttpError,
InvalidJsonError,
Expand Down Expand Up @@ -432,7 +436,8 @@ def _retrieve_discovery_doc(
actual_url = _add_query_parameter(url, "userIp", os.environ["REMOTE_ADDR"])
if developerKey:
actual_url = _add_query_parameter(url, "key", developerKey)
logger.debug("URL being requested: GET %s", actual_url)
if logger.isEnabledFor(logging.DEBUG):
logger.debug("URL being requested: GET %s", _redact_sensitive_url(actual_url))

# Execute this request with retries build into HttpRequest
# Note that it will already raise an error if we don't get a 2xx response
Expand Down Expand Up @@ -1263,7 +1268,10 @@ def method(self, **kwargs):
) % multipart_boundary
url = _add_query_parameter(url, "uploadType", "multipart")

logger.debug("URL being requested: %s %s" % (httpMethod, url))
if logger.isEnabledFor(logging.DEBUG):
logger.debug(
"URL being requested: %s %s", httpMethod, _redact_sensitive_url(url)
)
return self._requestBuilder(
self._http,
model.response,
Expand Down Expand Up @@ -1383,7 +1391,12 @@ def methodNext(self, previous_request, previous_response):
request.uri = _add_query_parameter(
request.uri, pageTokenName, nextPageToken
)
logger.debug("Next page request URL: %s %s" % (methodName, request.uri))
if logger.isEnabledFor(logging.DEBUG):
logger.debug(
"Next page request URL: %s %s",
methodName,
_redact_sensitive_url(request.uri),
)
else:
# Replace pageToken value in request body
model = self._model
Expand Down
Loading
Loading