Skip to content

Commit

Permalink
Merge pull request #87 from MindscapeHQ/tl/ignore-environment-variables
Browse files Browse the repository at this point in the history
Config option to not include environment variables
  • Loading branch information
UberMouse authored Jun 5, 2019
2 parents 8b33308 + 6767234 commit 2b935f9
Show file tree
Hide file tree
Showing 16 changed files with 190 additions and 22 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.3.0 (06/06/2019):
Features:
- Added a new config option, `transmit_environment_variables`, to control sending any environment variables at all
- Added support to `filter_keys` config option for ignoring keys with a simple wildcard approach. See README for more information

## 4.2.3 (28/03/2019):
Bugfixes
- Add request rawData to the build_wsgi_compliant_request utilities to fix a bug where rawData is set manually then overwritten by an empty object.
Expand Down
6 changes: 4 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ The above configuration is the minimal required setup. The full set of options s
'ignored_exceptions': [],
'transmit_global_variables': True,
'transmit_local_variables': True,
'transmit_environment_variables:': True,
'userversion': "Not defined",
'user': None
}
Expand Down Expand Up @@ -195,11 +196,12 @@ Initialization options
'ignored_exceptions': [],
'transmit_global_variables': True,
'transmit_local_variables': True,
'transmit_environment_variables:': True,
'userversion': "Not defined",
'user': None
})
For the local/global variables, if their options are set to False the corresponding variables will not be sent with exception payloads.
For the local/global/environment variables, if their options are set to False the corresponding variables will not be sent with exception payloads.

httpTimeout controls the maximum time the HTTP request can take when POSTing to the Raygun API, and is of type 'float'.

Expand Down Expand Up @@ -258,7 +260,7 @@ Config and data functions
| filter_keys | keys | List |
+--------------------+---------------+--------------------+

If you want to filter sensitive data out of the payload that is sent to Raygun, pass in a list of keys here. Any matching keys on the top level Raygun message object, or within dictionaries on the top level Raygun message object (including dictionaries nested within dictionaries) will have their value replaced with :code:`<filtered>` - useful for passwords, credit card data etc.
If you want to filter sensitive data out of the payload that is sent to Raygun, pass in a list of keys here. Any matching keys on the top level Raygun message object, or within dictionaries on the top level Raygun message object (including dictionaries nested within dictionaries) will have their value replaced with :code:`<filtered>` - useful for passwords, credit card data etc. Supports * at the end of a key to indicate you want to filter any key that contains that key, ie foo_* will filter foo_bar, foo_qux, foo_baz etc

+------------------+---------------+--------------------+
| Function | Arguments | Type |
Expand Down
8 changes: 6 additions & 2 deletions python2/raygun4py/raygunmsgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@

class RaygunMessageBuilder(object):

def __init__(self):
def __init__(self, options):
self.raygunMessage = RaygunMessage()
self.options = options

def new(self):
return RaygunMessageBuilder()
return RaygunMessageBuilder(self.options)

def build(self):
return self.raygunMessage
Expand All @@ -37,6 +38,9 @@ def set_environment_details(self, extra_environment_data):
"runtimeVersion": 'Python ' + sys.version
}

if self.options.get('transmit_environment_variables', True) is False:
self.raygunMessage.details['environment']['environmentVariables'] = None

# Wrap these so we gracefully fail if we cannot access the system details for any reason
try:
self.raygunMessage.details['environment']["processorCount"] = (
Expand Down
7 changes: 6 additions & 1 deletion python2/raygun4py/raygunprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
'proxy': None,
'transmit_global_variables': True,
'transmit_local_variables': True,
'transmit_environment_variables': True,
'userversion': "Not defined",
'user': None,
'http_timeout': 10.0
Expand Down Expand Up @@ -123,7 +124,11 @@ def _parse_args(self, kwargs):
return tags, custom_data, http_request, extra_environment_data

def _create_message(self, raygunExceptionMessage, tags, user_custom_data, http_request, extra_environment_data):
return raygunmsgs.RaygunMessageBuilder().new() \
options = {
'transmit_environment_variables': self.transmit_environment_variables
}

return raygunmsgs.RaygunMessageBuilder(options).new() \
.set_machine_name(socket.gethostname()) \
.set_version(self.userversion) \
.set_client_details() \
Expand Down
15 changes: 11 additions & 4 deletions python2/raygun4py/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ def filter_keys(filtered_keys, object):
iteration_target = object.__dict__

for key in iteration_target.iterkeys():
if key in filtered_keys:
iteration_target[key] = '<filtered>'

elif isinstance(iteration_target[key], dict):
if isinstance(iteration_target[key], dict):
iteration_target[key] = filter_keys(filtered_keys, iteration_target[key])
else:
for filter_key in filtered_keys:
if key in filtered_keys:
iteration_target[key] = '<filtered>'
elif '*' in filter_key:
sanitised_key = filter_key.replace('*', '')

if sanitised_key in key:
iteration_target[key] = '<filtered>'


return iteration_target

Expand Down
2 changes: 1 addition & 1 deletion python2/raygun4py/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '4.2.3'
__version__ = '4.3.0'
15 changes: 14 additions & 1 deletion python2/tests/test_raygunmsgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class TestRaygunMessageBuilder(unittest.TestCase):

def setUp(self):
self.builder = raygunmsgs.RaygunMessageBuilder().new()
self.builder = raygunmsgs.RaygunMessageBuilder({}).new()
self.request = {
"headers": {
"referer": "localhost",
Expand Down Expand Up @@ -112,6 +112,19 @@ def test_wsgi_standard_header_names(self):
self.assertEqual(self.builder.raygunMessage.details['request']['headers']['Referer'],
"https://www.google.com/")

def test_environment_variables(self):
self.builder.set_environment_details(None)

self.assertIsNotNone(self.builder.raygunMessage.details['environment']['environmentVariables'])

def test_environment_variables_are_ignored(self):
self.builder.options = {
'transmit_environment_variables': False
}
self.builder.set_environment_details(None)

self.assertIsNone(self.builder.raygunMessage.details['environment']['environmentVariables'])


class TestRaygunErrorMessage(unittest.TestCase):
class ParentError(Exception):
Expand Down
2 changes: 1 addition & 1 deletion python2/tests/test_raygunprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def the_callback(self, raygun_message):
def create_dummy_message(self):
self.sender = raygunprovider.RaygunSender('apikey')

msg = raygunmsgs.RaygunMessageBuilder().new()
msg = raygunmsgs.RaygunMessageBuilder({}).new()
errorMessage = raygunmsgs.RaygunErrorMessage(Exception, None, None, {})
msg.set_exception_details(errorMessage)
return msg.build()
Expand Down
52 changes: 52 additions & 0 deletions python2/tests/test_utilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import sys
import unittest2 as unittest
import socket
import inspect

from raygun4py import utilities

class TestRaygunUtilities(unittest.TestCase):
def test_filter_keys(self):
test_obj = {
'foo': 'bar',
'baz': 'qux'
}

utilities.filter_keys(['foo'], test_obj)

self.assertEqual(test_obj['foo'], '<filtered>')

def test_filter_keys_recursive(self):
test_obj = {
'foo': 'bar',
'baz': 'qux',
'boo': {
'foo': 'qux'
}
}

utilities.filter_keys(['foo'], test_obj)

self.assertEqual(test_obj['foo'], '<filtered>')
self.assertEqual(test_obj['boo']['foo'], '<filtered>')


def test_filter_keys_with_wildcard(self):
test_obj = {
'foobr': 'bar',
'foobz': 'baz',
'fooqx': 'foo',
'baz': 'qux'
}

utilities.filter_keys(['foo*'], test_obj)

self.assertEqual(test_obj['foobr'], '<filtered>')
self.assertEqual(test_obj['foobz'], '<filtered>')
self.assertEqual(test_obj['fooqx'], '<filtered>')

def main():
unittest.main()

if __name__ == '__main__':
main()
8 changes: 6 additions & 2 deletions python3/raygun4py/raygunmsgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@

class RaygunMessageBuilder(object):

def __init__(self):
def __init__(self, options):
self.raygunMessage = RaygunMessage()
self.options = options

def new(self):
return RaygunMessageBuilder()
return RaygunMessageBuilder(self.options)

def build(self):
return self.raygunMessage
Expand All @@ -37,6 +38,9 @@ def set_environment_details(self, extra_environment_data):
"runtimeVersion": 'Python ' + sys.version
}

if self.options.get('transmit_environment_variables', True) is False:
self.raygunMessage.details['environment']['environmentVariables'] = None

# Wrap these so we gracefully fail if we cannot access the system details for any reason
try:
self.raygunMessage.details['environment']["processorCount"] = (
Expand Down
7 changes: 6 additions & 1 deletion python3/raygun4py/raygunprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'proxy': None,
'transmit_global_variables': True,
'transmit_local_variables': True,
'transmit_environment_variables': True,
'userversion': "Not defined",
'user': None,
'http_timeout': 10.0
Expand Down Expand Up @@ -113,7 +114,11 @@ def _parse_args(self, kwargs):
return tags, custom_data, http_request, extra_environment_data

def _create_message(self, raygunExceptionMessage, tags, user_custom_data, http_request, extra_environment_data):
return raygunmsgs.RaygunMessageBuilder().new() \
options = {
'transmit_environment_variables': self.transmit_environment_variables
}

return raygunmsgs.RaygunMessageBuilder(options).new() \
.set_machine_name(socket.gethostname()) \
.set_version(self.userversion) \
.set_client_details() \
Expand Down
15 changes: 11 additions & 4 deletions python3/raygun4py/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ def filter_keys(filtered_keys, object):
iteration_target = object.__dict__

for key in iter(iteration_target.keys()):
if key in filtered_keys:
iteration_target[key] = '<filtered>'

elif isinstance(iteration_target[key], dict):
if isinstance(iteration_target[key], dict):
iteration_target[key] = filter_keys(filtered_keys, iteration_target[key])
else:
for filter_key in filtered_keys:
if key in filtered_keys:
iteration_target[key] = '<filtered>'
elif '*' in filter_key:
sanitised_key = filter_key.replace('*', '')

if sanitised_key in key:
iteration_target[key] = '<filtered>'


return iteration_target

Expand Down
2 changes: 1 addition & 1 deletion python3/raygun4py/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '4.2.3'
__version__ = '4.3.0'
15 changes: 14 additions & 1 deletion python3/tests/test_raygunmsgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class TestRaygunMessageBuilder(unittest.TestCase):

def setUp(self):
self.builder = raygunmsgs.RaygunMessageBuilder().new()
self.builder = raygunmsgs.RaygunMessageBuilder({}).new()
self.request = {
"headers": {
"referer": "localhost",
Expand Down Expand Up @@ -114,6 +114,19 @@ def test_set_request_details_allows_chaining(self):
.set_request_details(self.raw_wsgi_request) \
.set_tags(['foo', 'bar'])

def test_environment_variables(self):
self.builder.set_environment_details(None)

self.assertIsNotNone(self.builder.raygunMessage.details['environment']['environmentVariables'])

def test_environment_variables_are_ignored(self):
self.builder.options = {
'transmit_environment_variables': False
}
self.builder.set_environment_details(None)

self.assertIsNone(self.builder.raygunMessage.details['environment']['environmentVariables'])


class TestRaygunErrorMessage(unittest.TestCase):
class GrandchildError(Exception):
Expand Down
2 changes: 1 addition & 1 deletion python3/tests/test_raygunprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def the_callback(self, raygun_message):
def create_dummy_message(self):
self.sender = raygunprovider.RaygunSender('apikey')

msg = raygunmsgs.RaygunMessageBuilder().new()
msg = raygunmsgs.RaygunMessageBuilder({}).new()
errorMessage = raygunmsgs.RaygunErrorMessage(Exception, None, None, {})
msg.set_exception_details(errorMessage)
return msg.build()
Expand Down
51 changes: 51 additions & 0 deletions python3/tests/test_utilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest, sys
import socket
import inspect

from raygun4py import utilities

class TestRaygunUtilities(unittest.TestCase):
def test_filter_keys(self):
test_obj = {
'foo': 'bar',
'baz': 'qux'
}

utilities.filter_keys(['foo'], test_obj)

self.assertEqual(test_obj['foo'], '<filtered>')

def test_filter_keys_recursive(self):
test_obj = {
'foo': 'bar',
'baz': 'qux',
'boo': {
'foo': 'qux'
}
}

utilities.filter_keys(['foo'], test_obj)

self.assertEqual(test_obj['foo'], '<filtered>')
self.assertEqual(test_obj['boo']['foo'], '<filtered>')


def test_filter_keys_with_wildcard(self):
test_obj = {
'foobr': 'bar',
'foobz': 'baz',
'fooqx': 'foo',
'baz': 'qux'
}

utilities.filter_keys(['foo*'], test_obj)

self.assertEqual(test_obj['foobr'], '<filtered>')
self.assertEqual(test_obj['foobz'], '<filtered>')
self.assertEqual(test_obj['fooqx'], '<filtered>')

def main():
unittest.main()

if __name__ == '__main__':
main()

0 comments on commit 2b935f9

Please sign in to comment.