Skip to content

Commit d75041c

Browse files
committed
Merge branch 'master' into sentry-sdk-2.0
2 parents e9f9d0e + e07a128 commit d75041c

File tree

4 files changed

+52
-9
lines changed

4 files changed

+52
-9
lines changed

sentry_sdk/metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656
_in_metrics = ContextVar("in_metrics", default=False)
5757
_sanitize_key = partial(re.compile(r"[^a-zA-Z0-9_/.-]+").sub, "_")
58-
_sanitize_value = partial(re.compile(r"[^\w\d_:/@\.{}\[\]$-]+", re.UNICODE).sub, "_")
58+
_sanitize_value = partial(re.compile(r"[^\w\d\s_:/@\.{}\[\]$-]+", re.UNICODE).sub, "")
5959
_set = set # set is shadowed below
6060

6161
GOOD_TRANSACTION_SOURCES = frozenset(

sentry_sdk/scrubber.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,43 @@
5757
]
5858

5959

60-
class EventScrubber:
61-
def __init__(self, denylist=None):
62-
# type: (Optional[List[str]]) -> None
60+
class EventScrubber(object):
61+
def __init__(self, denylist=None, recursive=False):
62+
# type: (Optional[List[str]], bool) -> None
6363
self.denylist = DEFAULT_DENYLIST if denylist is None else denylist
6464
self.denylist = [x.lower() for x in self.denylist]
65+
self.recursive = recursive
66+
67+
def scrub_list(self, lst):
68+
# type: (List[Any]) -> None
69+
"""
70+
If a list is passed to this method, the method recursively searches the list and any
71+
nested lists for any dictionaries. The method calls scrub_dict on all dictionaries
72+
it finds.
73+
If the parameter passed to this method is not a list, the method does nothing.
74+
"""
75+
if not isinstance(lst, list):
76+
return
77+
78+
for v in lst:
79+
if isinstance(v, dict):
80+
self.scrub_dict(v)
81+
elif isinstance(v, list):
82+
self.scrub_list(v)
6583

6684
def scrub_dict(self, d):
6785
# type: (Dict[str, Any]) -> None
6886
if not isinstance(d, dict):
6987
return
7088

71-
for k in d.keys():
89+
for k, v in d.items():
7290
if isinstance(k, str) and k.lower() in self.denylist:
7391
d[k] = AnnotatedValue.substituted_because_contains_sensitive_data()
92+
elif self.recursive:
93+
if isinstance(v, dict):
94+
self.scrub_dict(v)
95+
elif isinstance(v, list):
96+
self.scrub_list(v)
7497

7598
def scrub_request(self, event):
7699
# type: (Event) -> None

tests/test_metrics.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,8 @@ def test_tag_normalization(
807807
metrics.distribution("a", 1.0, tags={"foo-bar": "%$foo"}, timestamp=ts)
808808
metrics.distribution("b", 1.0, tags={"foo$$$bar": "blah{}"}, timestamp=ts)
809809
metrics.distribution("c", 1.0, tags={"foö-bar": "snöwmän"}, timestamp=ts)
810+
metrics.distribution("d", 1.0, tags={"route": "GET /foo"}, timestamp=ts)
811+
810812
Hub.current.flush()
811813

812814
(envelope,) = envelopes
@@ -815,24 +817,27 @@ def test_tag_normalization(
815817
assert envelope.items[0].headers["type"] == "statsd"
816818
m = parse_metrics(envelope.items[0].payload.get_bytes())
817819

818-
assert len(m) == 3
820+
assert len(m) == 4
819821
assert m[0][4] == {
820-
"foo-bar": "_$foo",
822+
"foo-bar": "$foo",
821823
"release": "[email protected]",
822824
"environment": "not-fun-env",
823825
}
824-
825826
assert m[1][4] == {
826827
"foo_bar": "blah{}",
827828
"release": "[email protected]",
828829
"environment": "not-fun-env",
829830
}
830-
831831
assert m[2][4] == {
832832
"fo_-bar": "snöwmän",
833833
"release": "[email protected]",
834834
"environment": "not-fun-env",
835835
}
836+
assert m[3][4] == {
837+
"release": "[email protected]",
838+
"environment": "not-fun-env",
839+
"route": "GET /foo",
840+
}
836841

837842

838843
@minimum_python_37_with_gevent

tests/test_scrubber.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,18 @@ def test_scrubbing_doesnt_affect_local_vars(sentry_init, capture_events):
169169
(frame,) = frames
170170
assert frame["vars"]["password"] == "[Filtered]"
171171
assert password == "cat123"
172+
173+
174+
def test_recursive_event_scrubber(sentry_init, capture_events):
175+
sentry_init(event_scrubber=EventScrubber(recursive=True))
176+
events = capture_events()
177+
complex_structure = {
178+
"deep": {
179+
"deeper": [{"deepest": {"password": "my_darkest_secret"}}],
180+
},
181+
}
182+
183+
capture_event({"extra": complex_structure})
184+
185+
(event,) = events
186+
assert event["extra"]["deep"]["deeper"][0]["deepest"]["password"] == "'[Filtered]'"

0 commit comments

Comments
 (0)