Skip to content

Commit 9f6b546

Browse files
committed
Add separate pii_denylist to EventScrubber and run it always
1 parent 269d96d commit 9f6b546

File tree

5 files changed

+69
-13
lines changed

5 files changed

+69
-13
lines changed

sentry_sdk/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def _get_options(*args, **kwargs):
125125
rv["traces_sample_rate"] = 1.0
126126

127127
if rv["event_scrubber"] is None:
128-
rv["event_scrubber"] = EventScrubber()
128+
rv["event_scrubber"] = EventScrubber(send_default_pii=rv["send_default_pii"])
129129

130130
if rv["socket_options"] and not isinstance(rv["socket_options"], list):
131131
logger.warning(
@@ -526,7 +526,7 @@ def _prepare_event(
526526

527527
if event is not None:
528528
event_scrubber = self.options["event_scrubber"]
529-
if event_scrubber and not self.options["send_default_pii"]:
529+
if event_scrubber:
530530
event_scrubber.scrub_event(event)
531531

532532
# Postprocess the event here so that annotated types do

sentry_sdk/scrubber.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,17 @@
2424
"privatekey",
2525
"private_key",
2626
"token",
27-
"ip_address",
2827
"session",
2928
# django
3029
"csrftoken",
3130
"sessionid",
3231
# wsgi
33-
"remote_addr",
3432
"x_csrftoken",
3533
"x_forwarded_for",
3634
"set_cookie",
3735
"cookie",
3836
"authorization",
3937
"x_api_key",
40-
"x_forwarded_for",
41-
"x_real_ip",
4238
# other common names used in the wild
4339
"aiohttp_session", # aiohttp
4440
"connect.sid", # Express
@@ -54,11 +50,35 @@
5450
"XSRF-TOKEN", # Angular, Laravel
5551
]
5652

53+
DEFAULT_PII_DENYLIST = [
54+
"x_forwarded_for",
55+
"x_real_ip",
56+
"ip_address",
57+
"remote_addr",
58+
]
59+
5760

5861
class EventScrubber(object):
59-
def __init__(self, denylist=None, recursive=False):
60-
# type: (Optional[List[str]], bool) -> None
61-
self.denylist = DEFAULT_DENYLIST if denylist is None else denylist
62+
def __init__(
63+
self, denylist=None, recursive=False, send_default_pii=False, pii_denylist=None
64+
):
65+
# type: (Optional[List[str]], bool, bool, Optional[List[str]]) -> None
66+
"""
67+
A scrubber that goes through the event payload and removes sensitive data configured through denylists.
68+
69+
:param denylist: A security denylist that is always scrubbed, defaults to DEFAULT_DENYLIST.
70+
:param recursive: Whether to scrub the event payload recursively, default False.
71+
:param send_default_pii: Whether pii is sending is on, pii fields are not scrubbed.
72+
:param pii_denylist: The denylist to use for scrubbing when pii is not sent, defaults to DEFAULT_PII_DENYLIST.
73+
"""
74+
self.denylist = DEFAULT_DENYLIST.copy() if denylist is None else denylist
75+
76+
if not send_default_pii:
77+
pii_denylist = (
78+
DEFAULT_PII_DENYLIST.copy() if pii_denylist is None else pii_denylist
79+
)
80+
self.denylist += pii_denylist
81+
6282
self.denylist = [x.lower() for x in self.denylist]
6383
self.recursive = recursive
6484

tests/integrations/django/asgi/test_asgi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ async def test_trace_from_headers_if_performance_disabled(sentry_init, capture_e
434434
[(b"content-type", b"application/json")],
435435
"post_echo_async",
436436
b'{"username":"xyz","password":"xyz"}',
437-
{"username": "xyz", "password": "xyz"},
437+
{"username": "xyz", "password": "[Filtered]"},
438438
),
439439
(
440440
True,
@@ -453,7 +453,7 @@ async def test_trace_from_headers_if_performance_disabled(sentry_init, capture_e
453453
],
454454
"post_echo_async",
455455
BODY_FORM,
456-
{"password": "hello123", "photo": "", "username": "Jane"},
456+
{"password": "[Filtered]", "photo": "", "username": "Jane"},
457457
),
458458
(
459459
False,

tests/test_scrubber.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def test_request_scrubbing(sentry_init, capture_events):
2525
"COOKIE": "secret",
2626
"authorization": "Bearer bla",
2727
"ORIGIN": "google.com",
28+
"ip_address": "127.0.0.1",
2829
},
2930
"cookies": {
3031
"sessionid": "secret",
@@ -45,6 +46,7 @@ def test_request_scrubbing(sentry_init, capture_events):
4546
"COOKIE": "[Filtered]",
4647
"authorization": "[Filtered]",
4748
"ORIGIN": "google.com",
49+
"ip_address": "[Filtered]",
4850
},
4951
"cookies": {"sessionid": "[Filtered]", "foo": "bar"},
5052
"data": {"token": "[Filtered]", "foo": "bar"},
@@ -54,12 +56,39 @@ def test_request_scrubbing(sentry_init, capture_events):
5456
"headers": {
5557
"COOKIE": {"": {"rem": [["!config", "s"]]}},
5658
"authorization": {"": {"rem": [["!config", "s"]]}},
59+
"ip_address": {"": {"rem": [["!config", "s"]]}},
5760
},
5861
"cookies": {"sessionid": {"": {"rem": [["!config", "s"]]}}},
5962
"data": {"token": {"": {"rem": [["!config", "s"]]}}},
6063
}
6164

6265

66+
def test_ip_address_not_scrubbed_when_pii_enabled(sentry_init, capture_events):
67+
sentry_init(send_default_pii=True)
68+
events = capture_events()
69+
70+
try:
71+
1 / 0
72+
except ZeroDivisionError:
73+
ev, _hint = event_from_exception(sys.exc_info())
74+
75+
ev["request"] = {"headers": {"COOKIE": "secret", "ip_address": "127.0.0.1"}}
76+
77+
capture_event(ev)
78+
79+
(event,) = events
80+
81+
assert event["request"] == {
82+
"headers": {"COOKIE": "[Filtered]", "ip_address": "127.0.0.1"}
83+
}
84+
85+
assert event["_meta"]["request"] == {
86+
"headers": {
87+
"COOKIE": {"": {"rem": [["!config", "s"]]}},
88+
}
89+
}
90+
91+
6392
def test_stack_var_scrubbing(sentry_init, capture_events):
6493
sentry_init()
6594
events = capture_events()
@@ -131,11 +160,16 @@ def test_span_data_scrubbing(sentry_init, capture_events):
131160

132161

133162
def test_custom_denylist(sentry_init, capture_events):
134-
sentry_init(event_scrubber=EventScrubber(denylist=["my_sensitive_var"]))
163+
sentry_init(
164+
event_scrubber=EventScrubber(
165+
denylist=["my_sensitive_var"], pii_denylist=["my_pii_var"]
166+
)
167+
)
135168
events = capture_events()
136169

137170
try:
138171
my_sensitive_var = "secret" # noqa
172+
my_pii_var = "jane.doe" # noqa
139173
safe = "keepthis" # noqa
140174
1 / 0
141175
except ZeroDivisionError:
@@ -146,13 +180,15 @@ def test_custom_denylist(sentry_init, capture_events):
146180
frames = event["exception"]["values"][0]["stacktrace"]["frames"]
147181
(frame,) = frames
148182
assert frame["vars"]["my_sensitive_var"] == "[Filtered]"
183+
assert frame["vars"]["my_pii_var"] == "[Filtered]"
149184
assert frame["vars"]["safe"] == "'keepthis'"
150185

151186
meta = event["_meta"]["exception"]["values"]["0"]["stacktrace"]["frames"]["0"][
152187
"vars"
153188
]
154189
assert meta == {
155190
"my_sensitive_var": {"": {"rem": [["!config", "s"]]}},
191+
"my_pii_var": {"": {"rem": [["!config", "s"]]}},
156192
}
157193

158194

tests/tracing/test_decorator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def test_trace_decorator_no_trx():
3939
assert result == "return_of_sync_function"
4040

4141
result2 = start_child_span_decorator(my_example_function)()
42-
fake_debug.assert_called_once_with(
42+
fake_debug.assert_called_with(
4343
"Cannot create a child span for %s. "
4444
"Please start a Sentry transaction before calling this function.",
4545
"test_decorator.my_example_function",

0 commit comments

Comments
 (0)