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 9 commits into
base: master
Choose a base branch
from
Next Next commit
532: adds a logger filter for tokens in debug mode
Co-authored-by: Erlend vollset <[email protected]>
  • Loading branch information
Jacques Troussard and erlendvollset committed Mar 23, 2024
commit e0e20cdf7459cf1e3ac9a240f8cfac9dcabf127c
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ Maintainers & Contributors
- Sylvain Marie <[email protected]>
- Craig Anderson <[email protected]>
- Hugo van Kemenade <https://github.com/hugovk>
- Jacques Troussard <https://github.com/jtroussard>
19 changes: 19 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,22 @@ To install requests and requests_oauthlib you can use pip:
:alt: Documentation Status
:scale: 100%
:target: https://requests-oauthlib.readthedocs.io/

Advanced Configurations
-----------------------

### Logger Configuration Framework

`requests-oauthlib` now includes a flexible framework for applying custom filters and configurations to the logger, enhancing control over logging behavior and improving security.

#### Custom Filters

- **Debug Mode Token Filter**: To enhance security and provide more control over logging of sensitive information, requests-oauthlib introduces the Debug Mode Token Filter. This feature is controlled via the DEBUG_MODE_TOKEN_FILTER environment variable, allowing the suppression or masking of sensitive data in logs.

##### Configuring the Debug Mode Token Filter

- **Environment Variable**: `DEBUG_MODE_TOKEN_FILTER`
- **Options**:
- `DEFAULT`: No alteration to logging behavior.
- `MASK`: Masks sensitive tokens in logs.
- `SUPPRESS`: Prevents logging of potentially sensitive information. (logger ignores these logs entirely)
3 changes: 3 additions & 0 deletions requests_oauthlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from .oauth2_auth import OAuth2
from .oauth2_session import OAuth2Session, TokenUpdated

from .log_filters import DebugModeTokenFilter

__version__ = "2.0.0"

import requests
Expand All @@ -18,3 +20,4 @@
raise Warning(msg % requests.__version__)

logging.getLogger("requests_oauthlib").addHandler(logging.NullHandler())
logging.getLogger("requests_oauthlib").addFilter(DebugModeTokenFilter())
23 changes: 23 additions & 0 deletions requests_oauthlib/log_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import re
import logging

class DebugModeTokenFilter(logging.Filter): # <-- inherent from the Filter class
def __init__(self):
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
31 changes: 31 additions & 0 deletions tests/test_log_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import unittest
from unittest.mock import patch
from logging import LogRecord
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)

@patch.dict('os.environ', {'DEBUG_MODE_TOKEN_FILTER': 'MASK'})
def test_mask_mode(self):
filter = DebugModeTokenFilter()
filter.filter(self.record)
self.assertIn('[MASKED]', self.record.msg)

@patch.dict('os.environ', {'DEBUG_MODE_TOKEN_FILTER': 'SUPPRESS'})
def test_suppress_mode(self):
filter = DebugModeTokenFilter()
result = filter.filter(self.record)
self.assertFalse(result) # Check that nothing is logged

@patch.dict('os.environ', {'DEBUG_MODE_TOKEN_FILTER': 'DEFAULT'})
def test_default_mode(self):
filter = DebugModeTokenFilter()
with self.assertRaises(Warning) as context:
filter.filter(self.record)
self.assertTrue("Your logger, when in DEBUG mode, will log TOKENS" in str(context.exception))

if __name__ == '__main__':
unittest.main()