Skip to content

feat(data-collection): create DataCollection option in client#6702

Draft
ericapisani wants to merge 20 commits into
masterfrom
ep/db-spec-experiement-foundation-dict
Draft

feat(data-collection): create DataCollection option in client#6702
ericapisani wants to merge 20 commits into
masterfrom
ep/db-spec-experiement-foundation-dict

fix: import Literal for mypy type comment

726edeb
Select commit
Loading
Failed to load commit list.
@sentry/warden / warden: find-bugs completed Jul 2, 2026 in 0s

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:26
  • sentry_sdk/data_collection.py:110
  • tests/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-645
  • sentry_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

See this annotation in the file changed.

@sentry-warden 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 &lt; 3.8, which the SDK explicitly supports (`python_requires=">=3.6"`).