Skip to content
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

Adds logger filter for TOKENS when logging in DEBUG MODE #539

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
532: fixes logic so filter checks the logger record level, updates tests
  • Loading branch information
Jacques Troussard committed Mar 23, 2024
commit 103d28c69d775c2f61e6e4be81c4fd55fa4c3214
48 changes: 34 additions & 14 deletions requests_oauthlib/log_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,42 @@
import re
import logging

class DebugModeTokenFilter(logging.Filter): # <-- inherent from the Filter class
class DebugModeTokenFilter(logging.Filter):
"""
A logging filter that while in DEBUG mode can filter TOKENS dependent on configuration.

This filter uses an environment variable to determine its mode,
which can either mask sensitive tokens in log messages, suppress logging,
or default to standard logging behavior with a warning.

Attributes:
mode (str): The mode of operation based on the environment variable
'DEBUG_MODE_TOKEN_FILTER'. Can be 'MASK', 'SUPPRESS', or 'DEFAULT'.
"""
def __init__(self):
"""
Initializes the DebugModeTokenFilter with the 'DEBUG_MODE_TOKEN_FILTER'
environment variable.
"""
super().__init__()
# set the behavior/configuration of the filter by the environment variable
self.mode = os.getenv('DEBUG_MODE_TOKEN_FILTER', 'DEFAULT').upper()

def filter(self, record):
if self.mode == "MASK":
# While this doesn't directly target the headers as @erlendvollset 's post originally targets
# this wider approach of targeting the "Bearer" key word I believe provides complete coverage.
# However I would still recommend some more research to see if this regex would need to be improved
# to provide a secure/trusted solution.
record.msg = re.sub(r'Bearer (\w+)', '[MASKED]', record.getMessage())
elif self.mode == "SUPPRESS":
return False
elif self.mode == "DEFAULT":
msg = "Your logger, when in DEBUG mode, will log TOKENS"
raise Warning(msg)
return True
"""
Filters logs of TOKENS dependent on the configured mode.

Args:
record (logging.LogRecord): The log record to filter.

Returns:
bool: True if the record should be logged, False otherwise.
"""
if record.levelno == logging.DEBUG:
if self.mode == "MASK":
record.msg = re.sub(r'Bearer (\w+)', '[MASKED]', record.getMessage())
elif self.mode == "SUPPRESS":

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe only suppress log messages which match the above regex? Otherwise this equivalent to disabling the logger entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am hoping a single space would suffice as a replacement? Or maybe just pass None to the msg?

return False
elif self.mode == "DEFAULT":
msg = "Your logger, when in DEBUG mode, will log TOKENS"
raise Warning(msg)
return True
8 changes: 4 additions & 4 deletions tests/test_log_filters.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import unittest
from unittest.mock import patch
from logging import LogRecord
import logging
from requests_oauthlib.log_filters import DebugModeTokenFilter

class TestDebugModeTokenFilter(unittest.TestCase):

def setUp(self):
self.record = LogRecord(name="test", level=20, pathname=None, lineno=None, msg="Bearer i-am-a-token", args=None, exc_info=None)
self.record = logging.LogRecord(name="test", level=logging.DEBUG, pathname=None, lineno=None, msg="Bearer i-am-a-little-token-here-is-my-scope-and-here-is-my-signature", args=None, exc_info=None)

@patch.dict('os.environ', {'DEBUG_MODE_TOKEN_FILTER': 'MASK'})
def test_mask_mode(self):
Expand All @@ -18,10 +18,10 @@ def test_mask_mode(self):
def test_suppress_mode(self):
filter = DebugModeTokenFilter()
result = filter.filter(self.record)
self.assertFalse(result) # Check that nothing is logged
self.assertFalse(result) # No logging

@patch.dict('os.environ', {'DEBUG_MODE_TOKEN_FILTER': 'DEFAULT'})
def test_default_mode(self):
def test_default_mode_raises_warning(self):
filter = DebugModeTokenFilter()
with self.assertRaises(Warning) as context:
filter.filter(self.record)
Expand Down
Loading