Skip to content

Commit 2db1b27

Browse files
committed
Serialize vars earlier in the pipeline
We really shouldn't be holding references to `frame.f_locals` throughout our SDK, this has all sorts of breakage potential.
1 parent 901a5e8 commit 2db1b27

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-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: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import threading
1212
import time
1313
from collections import namedtuple
14+
from collections.abc import Mapping, Sequence, Set
1415
from datetime import datetime
1516
from decimal import Decimal
1617
from functools import partial, partialmethod, wraps
@@ -71,6 +72,9 @@
7172

7273
SENSITIVE_DATA_SUBSTITUTE = "[Filtered]"
7374

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

7579
def json_dumps(data):
7680
# type: (Any) -> bytes
@@ -614,11 +618,37 @@ def serialize_frame(
614618
)
615619

616620
if include_local_variables:
617-
rv["vars"] = frame.f_locals.copy()
621+
rv["vars"] = _serialize_vars_dict(frame.f_locals)
618622

619623
return rv
620624

621625

626+
def _serialize_vars_dict(f_locals):
627+
# type: (Mapping[str, Any]) -> Dict[str, Any]
628+
vars = {}
629+
for k, v in f_locals.items():
630+
vars[k] = _serialize_var_value(v)
631+
return vars
632+
633+
def _serialize_vars_list(list):
634+
# type: (Union[Sequence, Set]) -> List
635+
vars = []
636+
for x in list:
637+
vars.append(_serialize_var_value(x))
638+
return vars
639+
640+
def _serialize_var_value(value):
641+
# type: (Any) -> Any
642+
if isinstance(value, serializable_str_types):
643+
return value
644+
elif isinstance(value, Mapping):
645+
return _serialize_vars_dict(value)
646+
elif not isinstance(value, serializable_str_types) and isinstance(value, (Set, Sequence)):
647+
return _serialize_vars_list(value)
648+
else:
649+
return safe_repr(value)
650+
651+
622652
def current_stacktrace(
623653
include_local_variables=True, # type: bool
624654
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)