Defence-in-depth follow-up from VULN-2964. Not a bounty-eligible vuln: default Relay datascrubbing still filters these keys before storage. The SDK-side gap is still worth closing because it is the only layer that prevents transmission.
Problem
EventScrubber.scrub_dict uses exact set membership (k.lower() in self.denylist). Locals capture is on by default. Compound names such as api_token, access_token, auth_token, refresh_token, client_secret, and secret_key therefore leave the process unscrubbed, while password, api_key, private_key, and token do not.
DEFAULT_DENYLIST is commented as stolen from Relay, but Relay's @password:filter matches keys by substring (token, secret, …). The SDK copied the word list, not the matching semantics.
Relevant code:
sentry_sdk/scrubber.py (DEFAULT_DENYLIST, scrub_dict)
sentry_sdk/client.py (default scrubber install, scrub_event)
sentry_sdk/consts.py (include_local_variables defaults True)
Suggested fix
Either is fine:
- Match keys by substring / root terms (
token, secret, auth, password, key, credentials), similar to _SENSITIVE_DENYLIST in sentry_sdk/data_collection.py, or
- Keep exact match and add the missing literals.
Substring matching over-scrubs (tokenizer, keyboard, author). Relay already uses anchors (^otp$, ^two[-_]factor$) for that reason. Expanding the literal list is equally defensible. Silently diverging from the list we copied is not.
Related, not this ticket
Enabling _experiments={"data_collection": ...} uninstalls EventScrubber (client.py ~355–368) and never key-filters frame["vars"]. That path is worse than default, including for password / api_key. Raise it before data_collection becomes default.
Related: PY-1691 / sentry-python#4441.
Defence-in-depth follow-up from VULN-2964. Not a bounty-eligible vuln: default Relay datascrubbing still filters these keys before storage. The SDK-side gap is still worth closing because it is the only layer that prevents transmission.
Problem
EventScrubber.scrub_dictuses exact set membership (k.lower() in self.denylist). Locals capture is on by default. Compound names such asapi_token,access_token,auth_token,refresh_token,client_secret, andsecret_keytherefore leave the process unscrubbed, whilepassword,api_key,private_key, andtokendo not.DEFAULT_DENYLISTis commented as stolen from Relay, but Relay's@password:filtermatches keys by substring (token,secret, …). The SDK copied the word list, not the matching semantics.Relevant code:
sentry_sdk/scrubber.py(DEFAULT_DENYLIST,scrub_dict)sentry_sdk/client.py(default scrubber install,scrub_event)sentry_sdk/consts.py(include_local_variablesdefaults True)Suggested fix
Either is fine:
token,secret,auth,password,key,credentials), similar to_SENSITIVE_DENYLISTinsentry_sdk/data_collection.py, orSubstring matching over-scrubs (
tokenizer,keyboard,author). Relay already uses anchors (^otp$,^two[-_]factor$) for that reason. Expanding the literal list is equally defensible. Silently diverging from the list we copied is not.Related, not this ticket
Enabling
_experiments={"data_collection": ...}uninstallsEventScrubber(client.py~355–368) and never key-filtersframe["vars"]. That path is worse than default, including forpassword/api_key. Raise it beforedata_collectionbecomes default.Related: PY-1691 / sentry-python#4441.