Browse Source

Merge pull request #1416 from wyattisimo/master

allow API client to specify additional fields to sanitize
David Cramer 10 years ago
parent
commit
2d1e25931f

+ 6 - 0
src/sentry/api/endpoints/project_details.py

@@ -40,6 +40,8 @@ class ProjectDetailsEndpoint(Endpoint):
         data['options'] = {
             'sentry:origins': '\n'.join(project.get_option('sentry:origins', None) or []),
             'sentry:resolve_age': int(project.get_option('sentry:resolve_age', 0)),
+            'sentry:scrub_data': bool(project.get_option('sentry:scrub_data', True)),
+            'sentry:sensitive_fields': project.get_option('sentry:sensitive_fields', []),
         }
 
         return Response(data)
@@ -74,6 +76,10 @@ class ProjectDetailsEndpoint(Endpoint):
                 project.update_option('sentry:origins', options['sentry:origins'].split('\n'))
             if 'sentry:resolve_age' in options:
                 project.update_option('sentry:resolve_age', int(options['sentry:resolve_age']))
+            if 'sentry:scrub_data' in options:
+                project.update_option('sentry:scrub_data', bool(options['sentry:scrub_data']))
+            if 'sentry:sensitive_fields' in options:
+                project.update_option('sentry:sensitive_fields', options['sentry:sensitive_fields'])
 
             AuditLogEntry.objects.create(
                 organization=project.organization,

+ 5 - 2
src/sentry/utils/data_scrubber.py

@@ -41,11 +41,14 @@ class SensitiveDataFilter(object):
     and API keys in frames, http, and basic extra data.
     """
     MASK = '*' * 8
-    FIELDS = frozenset([
+    DEFAULT_FIELDS = frozenset([
         'password', 'secret', 'passwd', 'authorization', 'api_key', 'apikey'
     ])
     VALUES_RE = re.compile(r'\b(?:\d[ -]*?){13,16}\b')
 
+    def __init__(self, additional_fields=[]):
+        self.fields = frozenset(self.DEFAULT_FIELDS | set(additional_fields))
+
     def apply(self, data):
         if 'stacktrace' in data:
             self.filter_stacktrace(data['stacktrace'])
@@ -71,7 +74,7 @@ class SensitiveDataFilter(object):
             return value
 
         key = key.lower()
-        for field in self.FIELDS:
+        for field in self.fields:
             if field in key:
                 # store mask as a fixed length for security
                 return self.MASK

+ 1 - 1
src/sentry/web/api.py

@@ -377,7 +377,7 @@ class StoreView(APIView):
 
         if project.get_option('sentry:scrub_data', True):
             # We filter data immediately before it ever gets into the queue
-            inst = SensitiveDataFilter()
+            inst = SensitiveDataFilter(project.get_option('sentry:sensitive_fields', []))
             inst.apply(data)
 
         if scrub_ip_address:

+ 20 - 0
tests/sentry/api/endpoints/test_project_details.py

@@ -32,6 +32,26 @@ class ProjectUpdateTest(APITestCase):
         assert project.name == 'hello world'
         assert project.slug == 'foobar'
 
+    def test_options(self):
+        project = self.project  # force creation
+        self.login_as(user=self.user)
+        url = reverse('sentry-api-0-project-details', kwargs={'project_id': project.id})
+        options = {
+            'sentry:origins': 'foo\nbar',
+            'sentry:resolve_age': 1,
+            'sentry:scrub_data': False,
+            'sentry:sensitive_fields': ['foo', 'bar']
+        }
+        resp = self.client.put(url, data={
+            'options': options
+        })
+        assert resp.status_code == 200, resp.content
+        project = Project.objects.get(id=project.id)
+        assert project.get_option('sentry:origins', []) == options['sentry:origins'].split('\n')
+        assert project.get_option('sentry:resolve_age', 0) == options['sentry:resolve_age']
+        assert project.get_option('sentry:scrub_data', True) == options['sentry:scrub_data']
+        assert project.get_option('sentry:sensitive_fields', []) == options['sentry:sensitive_fields']
+
 
 class ProjectDeleteTest(APITestCase):
     @mock.patch('sentry.api.endpoints.project_details.delete_project')

+ 17 - 0
tests/sentry/utils/test_data_scrubber.py

@@ -113,6 +113,23 @@ class SensitiveDataFilterTest(TestCase):
         http = data['request']
         self.assertEquals(http['query_string'], 'foo=bar&password&baz=bar' % dict(m=proc.MASK))
 
+    def test_sanitize_additional_sensitive_fields(self):
+        additional_sensitive_dict = {
+            'fieldy_field': 'value',
+            'moar_other_field': 'another value'
+        }
+        data = {
+            'extra': dict(VARS.items() + additional_sensitive_dict.items())
+        }
+
+        proc = SensitiveDataFilter(additional_sensitive_dict.keys())
+        proc.apply(data)
+
+        for field in additional_sensitive_dict.keys():
+            self.assertEquals(data['extra'][field], proc.MASK)
+
+        self._check_vars_sanitized(data['extra'], proc)
+
     def test_sanitize_credit_card(self):
         proc = SensitiveDataFilter()
         result = proc.sanitize('foo', '4242424242424242')