Skip to content

Commit 7d3bcc0

Browse files
committed
Make a deeper copy of the vars object
1 parent 901a5e8 commit 7d3bcc0

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

sentry_sdk/serializer.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
format_timestamp,
1111
safe_repr,
1212
strip_string,
13+
serializable_str_types,
1314
)
1415
from sentry_sdk._types import TYPE_CHECKING
1516

@@ -33,10 +34,6 @@
3334
Segment = Union[str, int]
3435

3536

36-
# Bytes are technically not strings in Python 3, but we can serialize them
37-
serializable_str_types = (str, bytes, bytearray, memoryview)
38-
39-
4037
# Maximum length of JSON-serialized event payloads that can be safely sent
4138
# before the server may reject the event due to its size. This is not intended
4239
# to reflect actual values defined server-side, but rather only be an upper

sentry_sdk/utils.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import sys
1111
import threading
1212
import time
13+
from copy import copy
1314
from collections import namedtuple
15+
from collections.abc import Mapping, Sequence, Set
1416
from datetime import datetime
1517
from decimal import Decimal
1618
from functools import partial, partialmethod, wraps
@@ -71,6 +73,9 @@
7173

7274
SENSITIVE_DATA_SUBSTITUTE = "[Filtered]"
7375

76+
# Bytes are technically not strings in Python 3, but we can serialize them
77+
serializable_str_types = (str, bytes, bytearray, memoryview)
78+
7479

7580
def json_dumps(data):
7681
# type: (Any) -> bytes
@@ -614,11 +619,35 @@ def serialize_frame(
614619
)
615620

616621
if include_local_variables:
617-
rv["vars"] = frame.f_locals.copy()
622+
rv["vars"] = _copy_vars_dict(frame.f_locals)
618623

619624
return rv
620625

621626

627+
def _copy_vars_dict(f_locals):
628+
# type: (Mapping[str, Any]) -> Dict[str, Any]
629+
vars = {}
630+
for k, v in f_locals.items():
631+
vars[k] = _copy_var_value(v)
632+
return vars
633+
634+
def _copy_vars_list(list):
635+
# type: (Union[Sequence, Set]) -> List
636+
vars = []
637+
for x in list:
638+
vars.append(_copy_var_value(x))
639+
return vars
640+
641+
def _copy_var_value(value):
642+
# type: (Any) -> Any
643+
if isinstance(value, Mapping):
644+
return _copy_vars_dict(value)
645+
elif not isinstance(value, serializable_str_types) and isinstance(value, (Set, Sequence)):
646+
return _copy_vars_list(value)
647+
else:
648+
return copy(value)
649+
650+
622651
def current_stacktrace(
623652
include_local_variables=True, # type: bool
624653
include_source_context=True, # type: bool

tests/test_scrubber.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,20 @@ def test_recursive_event_scrubber(sentry_init, capture_events):
187187

188188
(event,) = events
189189
assert event["extra"]["deep"]["deeper"][0]["deepest"]["password"] == "'[Filtered]'"
190+
191+
192+
def test_recursive_scrubber_does_not_override_original(sentry_init, capture_events):
193+
sentry_init(event_scrubber=EventScrubber(recursive=True))
194+
events = capture_events()
195+
196+
data = {"csrf": "secret"}
197+
try:
198+
raise RuntimeError("An error")
199+
except Exception:
200+
capture_exception()
201+
202+
(event,) = events
203+
frames = event["exception"]["values"][0]["stacktrace"]["frames"]
204+
(frame,) = frames
205+
assert data["csrf"] == "secret"
206+
assert frame["vars"]["data"]["csrf"] == "[Filtered]"

0 commit comments

Comments
 (0)