-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
{Logging} Redact token headers from SDK HTTP log #17671
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,98 @@ | ||||||
# -------------------------------------------------------------------------------------------- | ||||||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||||||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||||||
# -------------------------------------------------------------------------------------------- | ||||||
|
||||||
import logging | ||||||
import re | ||||||
import types | ||||||
|
||||||
from azure.core.pipeline.policies import SansIOHTTPPolicy | ||||||
from knack.log import get_logger | ||||||
|
||||||
_LOGGER = get_logger(__name__) | ||||||
|
||||||
|
||||||
class SafeNetworkTraceLoggingPolicy(SansIOHTTPPolicy): | ||||||
"""The logging policy that redacts specified headers. | ||||||
Based on azure.core.pipeline.policies._universal.NetworkTraceLoggingPolicy | ||||||
""" | ||||||
|
||||||
def __init__(self, headers_to_redact=None): | ||||||
""" | ||||||
:param list[str] headers_to_redact: headers that should be redacted from the log. | ||||||
Default to 'Authorization', 'x-ms-authorization-auxiliary'. | ||||||
""" | ||||||
if headers_to_redact is not None: | ||||||
self.headers_to_redact = headers_to_redact | ||||||
else: | ||||||
self.headers_to_redact = ['authorization', 'x-ms-authorization-auxiliary'] | ||||||
|
||||||
def on_request(self, request): | ||||||
http_request = request.http_request | ||||||
options = request.context.options | ||||||
logging_enable = options.pop("logging_enable", True) | ||||||
request.context["logging_enable"] = logging_enable | ||||||
if logging_enable: | ||||||
if not _LOGGER.isEnabledFor(logging.DEBUG): | ||||||
return | ||||||
|
||||||
try: | ||||||
_LOGGER.debug("Request URL: %r", http_request.url) | ||||||
_LOGGER.debug("Request method: %r", http_request.method) | ||||||
_LOGGER.debug("Request headers:") | ||||||
for header, value in http_request.headers.items(): | ||||||
if header.lower() in self.headers_to_redact: | ||||||
value = '*****' | ||||||
_LOGGER.debug(" %r: %r", header, value) | ||||||
_LOGGER.debug("Request body:") | ||||||
|
||||||
# We don't want to log the binary data of a file upload. | ||||||
if isinstance(http_request.body, types.GeneratorType): | ||||||
_LOGGER.debug("File upload") | ||||||
return | ||||||
try: | ||||||
if isinstance(http_request.body, types.AsyncGeneratorType): | ||||||
_LOGGER.debug("File upload") | ||||||
return | ||||||
Comment on lines
+51
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In storage track1 data plane SDK, for such file, it will logging with file size. Could we also support it here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This If storage data-plane SDK or other data-plane SDKs requires additional/special logic, we may apply Another solution is to define your own policy and override azure-cli/src/azure-cli-core/azure/cli/core/commands/client_factory.py Lines 155 to 156 in 36e3d15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could keep current design then. |
||||||
except AttributeError: | ||||||
pass | ||||||
if http_request.body: | ||||||
_LOGGER.debug(str(http_request.body)) | ||||||
return | ||||||
_LOGGER.debug("This request has no body") | ||||||
except Exception as err: # pylint: disable=broad-except | ||||||
_LOGGER.debug("Failed to log request: %r", err) | ||||||
|
||||||
def on_response(self, request, response): # pylint: disable=unused-argument, no-self-use | ||||||
http_response = response.http_response | ||||||
try: | ||||||
logging_enable = response.context["logging_enable"] | ||||||
if logging_enable: | ||||||
if not _LOGGER.isEnabledFor(logging.DEBUG): | ||||||
return | ||||||
|
||||||
_LOGGER.debug("Response status: %r", http_response.status_code) | ||||||
_LOGGER.debug("Response headers:") | ||||||
for res_header, value in http_response.headers.items(): | ||||||
_LOGGER.debug(" %r: %r", res_header, value) | ||||||
|
||||||
# We don't want to log binary data if the response is a file. | ||||||
_LOGGER.debug("Response content:") | ||||||
pattern = re.compile(r'attachment; ?filename=["\w.]+', re.IGNORECASE) | ||||||
header = http_response.headers.get('content-disposition') | ||||||
|
||||||
if header and pattern.match(header): | ||||||
filename = header.partition('=')[2] | ||||||
_LOGGER.debug("File attachments: %s", filename) | ||||||
elif http_response.headers.get("content-type", "").endswith("octet-stream"): | ||||||
_LOGGER.debug("Body contains binary data.") | ||||||
elif http_response.headers.get("content-type", "").startswith("image"): | ||||||
_LOGGER.debug("Body contains image data.") | ||||||
else: | ||||||
if response.context.options.get('stream', False): | ||||||
_LOGGER.debug("Body is streamable") | ||||||
else: | ||||||
_LOGGER.debug(response.http_response.text()) | ||||||
except Exception as err: # pylint: disable=broad-except | ||||||
_LOGGER.debug("Failed to log response: %s", repr(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, always Bearer token here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The comment at L182 explains that:
azure-cli/src/azure-cli-core/azure/cli/core/commands/client_factory.py
Line 182 in 36e3d15