Skip to content

Commit 7541460

Browse files
committed
ref(integrations): route HTTP header filtering through data collection config
`_filter_headers` previously used a hardcoded sensitive-header tuple and a `send_default_pii`/`use_annotated_value` toggle. It now delegates to `_apply_key_value_collection_filtering` from `sentry_sdk.data_collection`, so header scrubbing respects the new `data_collection.http_headers.request` allowlist/denylist/off configuration. Cookie and set-cookie headers are always redacted regardless of mode. Drops the now-unused `use_annotated_value` parameter from all call sites. Work to scrub cookies in a more granular way will be tackled as part of PY-2581/#6741. Fixes PY-2584 Fixes #6744
1 parent 0cc4505 commit 7541460

13 files changed

Lines changed: 361 additions & 90 deletions

File tree

sentry_sdk/data_collection.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
"""
2424

2525
import warnings
26-
from typing import TYPE_CHECKING, cast
26+
from typing import TYPE_CHECKING, List, Mapping, Optional, cast
27+
28+
from sentry_sdk._types import SENSITIVE_DATA_SUBSTITUTE
2729

2830
if TYPE_CHECKING:
2931
from typing import Any, Dict, Literal
@@ -77,6 +79,62 @@
7779
]
7880

7981

82+
def _is_sensitive_key(key: str, extra_terms: "Optional[List[str]]" = None) -> bool:
83+
"""
84+
Return whether ``key`` matches the sensitive denylist using a partial,
85+
case-insensitive substring match.
86+
87+
:param extra_terms: additional deny terms (e.g. user-provided) to consider
88+
alongside the built-in `_SENSITIVE_DENYLIST`.
89+
"""
90+
lowered = key.lower()
91+
for term in _SENSITIVE_DENYLIST:
92+
if term in lowered:
93+
return True
94+
if extra_terms:
95+
for term in extra_terms:
96+
if term and term.lower() in lowered:
97+
return True
98+
return False
99+
100+
101+
def _apply_key_value_collection_filtering(
102+
items: "Mapping[str, Any]",
103+
behaviour: "KeyValueCollectionBehaviour",
104+
substitute: "Any" = SENSITIVE_DATA_SUBSTITUTE,
105+
) -> "Dict[str, Any]":
106+
107+
if behaviour["mode"] == "off":
108+
return {}
109+
110+
result: "Dict[str, Any]" = {}
111+
112+
if behaviour["mode"] == "allowlist":
113+
for key, value in items.items():
114+
is_allowed = False
115+
if isinstance(key, str):
116+
lowered = key.lower()
117+
is_allowed = any(
118+
term and term.lower() in lowered
119+
for term in behaviour.get("terms", [])
120+
)
121+
if is_allowed and not _is_sensitive_key(key):
122+
result[key] = value
123+
else:
124+
result[key] = substitute
125+
return result
126+
127+
# denylist behaviour
128+
for key, value in items.items():
129+
if isinstance(key, str) and _is_sensitive_key(
130+
key=key, extra_terms=behaviour.get("terms", [])
131+
):
132+
result[key] = substitute
133+
else:
134+
result[key] = value
135+
return result
136+
137+
80138
def _map_from_send_default_pii(
81139
*,
82140
send_default_pii: bool,

sentry_sdk/integrations/_asgi_common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def _get_request_attributes(
148148
if asgi_scope.get("method"):
149149
attributes["http.request.method"] = asgi_scope["method"].upper()
150150

151-
headers = _filter_headers(_get_headers(asgi_scope), use_annotated_value=False)
151+
headers = _filter_headers(_get_headers(asgi_scope))
152152
for header, value in headers.items():
153153
attributes[f"http.request.header.{header.lower()}"] = value
154154

sentry_sdk/integrations/_wsgi_common.py

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import sentry_sdk
66
from sentry_sdk._types import SENSITIVE_DATA_SUBSTITUTE
7+
from sentry_sdk.data_collection import _apply_key_value_collection_filtering
78
from sentry_sdk.scope import should_send_default_pii
89
from sentry_sdk.utils import AnnotatedValue, logger
910

@@ -23,22 +24,6 @@
2324
from sentry_sdk._types import Event, HttpStatusCodeRange
2425

2526

26-
SENSITIVE_ENV_KEYS = (
27-
"REMOTE_ADDR",
28-
"HTTP_X_FORWARDED_FOR",
29-
"HTTP_SET_COOKIE",
30-
"HTTP_COOKIE",
31-
"HTTP_AUTHORIZATION",
32-
"HTTP_PROXY_AUTHORIZATION",
33-
"HTTP_X_API_KEY",
34-
"HTTP_X_FORWARDED_FOR",
35-
"HTTP_X_REAL_IP",
36-
)
37-
38-
SENSITIVE_HEADERS = tuple(
39-
x[len("HTTP_") :] for x in SENSITIVE_ENV_KEYS if x.startswith("HTTP_")
40-
)
41-
4227
DEFAULT_HTTP_METHODS_TO_CAPTURE = (
4328
"CONNECT",
4429
"DELETE",
@@ -211,21 +196,19 @@ def _is_json_content_type(ct: "Optional[str]") -> bool:
211196

212197
def _filter_headers(
213198
headers: "Mapping[str, str]",
214-
use_annotated_value: bool = True,
215-
) -> "Mapping[str, Union[AnnotatedValue, str]]":
216-
if should_send_default_pii():
217-
return headers
218-
219-
substitute: "Union[AnnotatedValue, str]" = (
220-
SENSITIVE_DATA_SUBSTITUTE
221-
if not use_annotated_value
222-
else AnnotatedValue.removed_because_over_size_limit()
199+
) -> "Mapping[str, str]":
200+
data_collection_configuration = sentry_sdk.get_client().options["data_collection"]
201+
202+
filtered = _apply_key_value_collection_filtering(
203+
items=headers,
204+
behaviour=data_collection_configuration["http_headers"]["request"],
223205
)
224206

225-
return {
226-
k: (v if k.upper().replace("-", "_") not in SENSITIVE_HEADERS else substitute)
227-
for k, v in headers.items()
228-
}
207+
for key in filtered:
208+
if isinstance(key, str) and key.lower() in ("cookie", "set-cookie"):
209+
filtered[key] = SENSITIVE_DATA_SUBSTITUTE
210+
211+
return filtered
229212

230213

231214
def _in_http_status_code_range(

sentry_sdk/integrations/aiohttp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ async def sentry_app_handle(
150150

151151
header_attributes: "dict[str, Any]" = {}
152152
for header, header_value in _filter_headers(
153-
headers, use_annotated_value=False
153+
headers
154154
).items():
155155
header_attributes[
156156
f"http.request.header.{header.lower()}"

sentry_sdk/integrations/aws_lambda.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def sentry_handler(
152152

153153
header_attributes: "dict[str, Any]" = {}
154154
for header, header_value in _filter_headers(
155-
headers, use_annotated_value=False
155+
headers
156156
).items():
157157
header_attributes[f"http.request.header.{header.lower()}"] = (
158158
header_value

sentry_sdk/integrations/chalice.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def wrapped_view_function(**function_args: "Any") -> "Any":
9292

9393
header_attrs: "Dict[str, Any]" = {}
9494
for header, value in _filter_headers(
95-
headers, use_annotated_value=False
95+
headers
9696
).items():
9797
header_attrs[f"http.request.header.{header.lower()}"] = value
9898

sentry_sdk/integrations/gcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def sentry_func(
8989
if hasattr(gcp_event, "headers"):
9090
headers = gcp_event.headers
9191
for header, header_value in _filter_headers(
92-
headers, use_annotated_value=False
92+
headers
9393
).items():
9494
header_attributes[f"http.request.header.{header.lower()}"] = (
9595
# header_value will always be a string because we set `use_annotated_value` to false above

sentry_sdk/integrations/quart.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ async def _request_websocket_started(app: "Quart", **kwargs: "Any") -> None:
195195
header_attributes: "dict[str, Any]" = {}
196196

197197
for header, header_value in _filter_headers(
198-
dict(request_websocket.headers), use_annotated_value=False
198+
dict(request_websocket.headers)
199199
).items():
200200
header_attributes[f"http.request.header.{header.lower()}"] = (
201201
header_value

sentry_sdk/integrations/sanic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ def _get_request_attributes(request: "Request") -> "Dict[str, Any]":
374374
if request.method:
375375
attributes[SPANDATA.HTTP_REQUEST_METHOD] = request.method.upper()
376376

377-
headers = _filter_headers(dict(request.headers), use_annotated_value=False)
377+
headers = _filter_headers(dict(request.headers))
378378
for header, value in headers.items():
379379
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = value
380380

sentry_sdk/integrations/tornado.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def _get_request_attributes(request: "Any") -> "Dict[str, Any]":
193193
if request.method:
194194
attributes[SPANDATA.HTTP_REQUEST_METHOD] = request.method.upper()
195195

196-
headers = _filter_headers(dict(request.headers), use_annotated_value=False)
196+
headers = _filter_headers(dict(request.headers))
197197
for header, value in headers.items():
198198
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = value
199199

0 commit comments

Comments
 (0)