feat(data-collection): create DataCollection option in client#6702
feat(data-collection): create DataCollection option in client#6702ericapisani wants to merge 20 commits into
6 issues
find-bugs: Found 7 issues (1 medium, 6 low)
Medium
`typing.Literal` imported at runtime, causes `ImportError` on Python 3.6 and 3.7 - `sentry_sdk/data_collection.py:26`
Move Literal into the if TYPE_CHECKING: block (or import from typing_extensions) — it's only used in a # type: comment and its runtime import raises ImportError on Python < 3.8, which the SDK explicitly supports (python_requires=">=3.6").
Low
`graphql.document` docstring contradicts implementation in `_map_from_send_default_pii` - `sentry_sdk/_types.py:173-174`
The docstring of _map_from_send_default_pii (data_collection.py:93) states that in the send_default_pii legacy path, graphql.document stays True, while graphql.variables and database.query_params follow send_default_pii. However, the implementation at data_collection.py:110 sets "graphql": {"document": send_default_pii, "variables": send_default_pii}, so graphql.document becomes False when send_default_pii=False. The explicit-config path (_graphql_from_value) defaults document to True, treating the GraphQL document as non-PII. The two resolution paths therefore disagree, and either the docstring or the legacy-path implementation is wrong. This is a code-quality/correctness inconsistency, not a security issue.
Also found at:
sentry_sdk/client.py:26sentry_sdk/data_collection.py:110tests/test_data_collection.py:183-190
DeprecationWarning for `send_default_pii` points to internal SDK code instead of user call site - `sentry_sdk/client.py:77`
The warnings.warn(..., stacklevel=2) in resolve_data_collection (sentry_sdk/data_collection.py) reports the warning as originating from _get_options in sentry_sdk/client.py (the call site of resolve_data_collection) rather than the user's sentry_sdk.init() call, making it hard for users to identify which line of their own code to fix. Because resolve_data_collection is a nested helper, stacklevel=2 only unwinds to _get_options itself, one frame short of even the existing enable_tracing warning which reaches _Client.__init__.
EventScrubber not updated when spotlight overrides data_collection - `sentry_sdk/client.py:356-358`
In _get_options() the EventScrubber is created with send_default_pii=rv["data_collection"]["user_info"], which is False by default, so DEFAULT_PII_DENYLIST fields (ip_address, x_forwarded_for, x_real_ip, remote_addr) are added to the scrubber denylist. In DSN-less spotlight mode, Client.__init__ later re-derives data_collection with user_info=True but does not recreate event_scrubber. As a result, spotlight events continue to have PII fields (e.g. user IP address) scrubbed even though PII collection is intentionally enabled for local debugging. Impact is limited to spotlight local-dev mode and the effect is over-scrubbing (data removal, not disclosure), so this is a functional inconsistency rather than a security issue.
Also found at:
sentry_sdk/client.py:638-645sentry_sdk/consts.py:1282
`data_collection` property raises `KeyError` for clients pickled by a pre-PR SDK version - `sentry_sdk/client.py:761`
The data_collection property (sentry_sdk/client.py:761) uses a hard subscript self.options["data_collection"] instead of self.options.get("data_collection"). Within any single SDK version the key is always populated by _get_options() (line 353, via resolve_data_collection), so normal usage is safe. However, _Client.__setstate__ restores the raw pickled options dict and re-runs only _init_impl(), never _get_options(). A client pickled by a pre-PR SDK version (whose options dict has no data_collection key) will therefore lack the key after unpickling, and any access to this property will raise KeyError. Note the DSN-less/spotlight branch in _init_impl() (line 637) has the same latent issue. Using .get(...) with a sensible default, as should_send_default_pii() does, would avoid this.
User-supplied `http_bodies` list elements are not validated against allowed values - `sentry_sdk/consts.py:1282`
In data_collection.py's _resolve_explicit, arbitrary strings in the user-supplied http_bodies list are accepted and copied verbatim without checking membership in ALL_HTTP_BODY_TYPES. Unlike key-value collection modes (which raise ValueError for invalid values in _kvcb_from_value), invalid body-type strings are stored silently and will never match any real body category downstream, so a user typo (e.g. "incomming_request") silently disables collection for that category with no error.
DSN-less Spotlight forces `send_default_pii=True` unconditionally, desyncing it from an explicit `data_collection` restriction - `tests/test_data_collection.py:283-290`
In DSN-less Spotlight mode (client.py:626-645), self.options["send_default_pii"] = True is set unconditionally (line 628), but the accompanying re-derivation of data_collection is guarded by if not self.options["data_collection"]["provided_by_user"] (line 637). When a user provides an explicit data_collection (e.g. {"user_info": False}), the guard correctly preserves their restricted data_collection, yet send_default_pii is still flipped to True. As a result should_send_default_pii() returns True and disagrees with the resolved data_collection (user_info=False). Integrations that still gate PII on should_send_default_pii() (cookies, headers, GraphQL variables, DB params, AI inputs/outputs) will collect PII contrary to the user's explicit intent. Impact is bounded because DSN-less Spotlight is a local-only debugging mode (no DSN means events are not sent to Sentry), but the two settings should be kept consistent. The fix is to guard the send_default_pii = True assignment with the same if not self.options["data_collection"]["provided_by_user"] condition already used for re-deriving data_collection.
⏱ 29m 23s · 7.6M in / 249.3k out · $9.19
Annotations
Check warning on line 26 in sentry_sdk/data_collection.py
sentry-warden / warden: find-bugs
`typing.Literal` imported at runtime, causes `ImportError` on Python 3.6 and 3.7
Move `Literal` into the `if TYPE_CHECKING:` block (or import from `typing_extensions`) — it's only used in a `# type:` comment and its runtime import raises `ImportError` on Python < 3.8, which the SDK explicitly supports (`python_requires=">=3.6"`).