Skip to content

Commit d717172

Browse files
committed
add test coverage within the wsgi test file
1 parent 8b6e3ad commit d717172

1 file changed

Lines changed: 250 additions & 0 deletions

File tree

tests/integrations/wsgi/test_wsgi.py

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,256 @@ def test_get_request_url_x_forwarded_proto(environ, use_x_forwarded_for, expecte
858858
assert get_request_url(environ, use_x_forwarded_for) == expected_url
859859

860860

861+
@pytest.mark.parametrize("send_default_pii", [True, False])
862+
def test_request_headers_data_collection_default_redacts_sensitive(
863+
sentry_init, crashing_app, capture_events, send_default_pii
864+
):
865+
"""
866+
When ``data_collection`` is configured (even as ``None``, i.e. spec
867+
defaults), the WSGI event processor routes request headers through the
868+
data-collection filtering path. Sensitive headers are redacted regardless
869+
of ``send_default_pii`` -- the value of that legacy option must not change
870+
the outcome.
871+
"""
872+
sentry_init(
873+
send_default_pii=send_default_pii,
874+
_experiments={"data_collection": None},
875+
)
876+
app = SentryWsgiMiddleware(crashing_app)
877+
client = Client(app)
878+
events = capture_events()
879+
880+
with pytest.raises(ZeroDivisionError):
881+
client.get(
882+
"/",
883+
headers={
884+
"Authorization": "Bearer secret-token",
885+
"X-Custom-Header": "passthrough",
886+
},
887+
)
888+
889+
(event,) = events
890+
headers = event["request"]["headers"]
891+
892+
assert headers["Authorization"] == "[Filtered]"
893+
assert headers["X-Custom-Header"] == "passthrough"
894+
895+
896+
def test_request_headers_legacy_no_pii_redacts_sensitive(
897+
sentry_init, crashing_app, capture_events
898+
):
899+
"""
900+
With no ``data_collection`` configured, ``_filter_headers`` falls back to
901+
the legacy ``send_default_pii`` behaviour. When PII is disabled, headers in
902+
``SENSITIVE_HEADERS`` are replaced with an ``AnnotatedValue`` (the default
903+
``use_annotated_value=True`` on the event-processor call site), which
904+
serializes to an emptied value plus a ``_meta`` annotation. Non-sensitive
905+
headers pass through untouched.
906+
907+
``X-Forwarded-For`` is used because it is in ``SENSITIVE_HEADERS`` but is
908+
not scrubbed by the default ``EventScrubber``, so the substitution we are
909+
asserting on can only come from ``_filter_headers``.
910+
"""
911+
sentry_init(send_default_pii=False)
912+
app = SentryWsgiMiddleware(crashing_app)
913+
client = Client(app)
914+
events = capture_events()
915+
916+
with pytest.raises(ZeroDivisionError):
917+
client.get(
918+
"/",
919+
headers={
920+
"X-Forwarded-For": "1.2.3.4",
921+
"X-Custom-Header": "passthrough",
922+
},
923+
)
924+
925+
(event,) = events
926+
927+
assert event["request"]["headers"]["X-Forwarded-For"] == ""
928+
assert event["request"]["headers"]["X-Custom-Header"] == "passthrough"
929+
930+
# The emptied value is accompanied by a `_meta` annotation marking it as
931+
# removed, confirming the substitution came from the AnnotatedValue path.
932+
assert event["_meta"]["request"]["headers"]["X-Forwarded-For"] == {
933+
"": {"rem": [["!config", "x"]]}
934+
}
935+
936+
937+
def test_request_headers_data_collection_off_collects_no_headers(
938+
sentry_init, crashing_app, capture_events
939+
):
940+
"""
941+
With ``http_headers.request`` mode set to ``off``, no request headers are
942+
collected at all -- the filtering returns an empty mapping.
943+
"""
944+
sentry_init(
945+
_experiments={
946+
"data_collection": {"http_headers": {"request": {"mode": "off"}}}
947+
},
948+
)
949+
app = SentryWsgiMiddleware(crashing_app)
950+
client = Client(app)
951+
events = capture_events()
952+
953+
with pytest.raises(ZeroDivisionError):
954+
client.get(
955+
"/",
956+
headers={
957+
"X-Forwarded-For": "1.2.3.4",
958+
"X-Custom-Header": "passthrough",
959+
},
960+
)
961+
962+
(event,) = events
963+
964+
assert event["request"]["headers"] == {}
965+
966+
967+
def test_request_headers_data_collection_allowlist_redacts_all_but_allowed_terms(
968+
sentry_init, crashing_app, capture_events
969+
):
970+
"""
971+
An ``allowlist`` allows through only headers matching a configured term
972+
(partial, case-insensitive); every other header key is kept but its value
973+
is redacted.
974+
"""
975+
sentry_init(
976+
_experiments={
977+
"data_collection": {
978+
"http_headers": {"request": {"mode": "allowlist", "terms": ["custom"]}}
979+
}
980+
},
981+
)
982+
app = SentryWsgiMiddleware(crashing_app)
983+
client = Client(app)
984+
events = capture_events()
985+
986+
with pytest.raises(ZeroDivisionError):
987+
client.get(
988+
"/",
989+
headers={
990+
"X-Forwarded-For": "1.2.3.4",
991+
"X-Custom-Header": "passthrough",
992+
},
993+
)
994+
995+
(event,) = events
996+
headers = event["request"]["headers"]
997+
998+
assert headers["X-Custom-Header"] == "passthrough"
999+
assert headers["X-Forwarded-For"] == "[Filtered]"
1000+
assert headers["Host"] == "[Filtered]"
1001+
1002+
1003+
def test_request_headers_data_collection_denylist_redacts_only_matched_terms(
1004+
sentry_init, crashing_app, capture_events
1005+
):
1006+
"""
1007+
A ``denylist`` passes headers through by default, redacting only those
1008+
matching a configured term (partial, case-insensitive).
1009+
"""
1010+
sentry_init(
1011+
_experiments={
1012+
"data_collection": {
1013+
"http_headers": {"request": {"mode": "denylist", "terms": ["custom"]}}
1014+
}
1015+
},
1016+
)
1017+
app = SentryWsgiMiddleware(crashing_app)
1018+
client = Client(app)
1019+
events = capture_events()
1020+
1021+
with pytest.raises(ZeroDivisionError):
1022+
client.get(
1023+
"/",
1024+
headers={
1025+
"X-Forwarded-For": "1.2.3.4",
1026+
"X-Custom-Header": "passthrough",
1027+
},
1028+
)
1029+
1030+
(event,) = events
1031+
headers = event["request"]["headers"]
1032+
1033+
assert headers["X-Custom-Header"] == "[Filtered]"
1034+
assert headers["X-Forwarded-For"] == "1.2.3.4"
1035+
assert headers["Host"] == "localhost"
1036+
1037+
1038+
def test_request_headers_data_collection_cookie_always_redacted(
1039+
sentry_init, crashing_app, capture_events
1040+
):
1041+
"""
1042+
The ``cookie``/``set-cookie`` headers are always redacted in the
1043+
data-collection path, even when explicitly allowlisted. A sibling header
1044+
(``custom``) that is also allowlisted passes through, isolating the
1045+
cookie override.
1046+
1047+
The middleware is driven with an explicit environ because werkzeug's test
1048+
``Client`` manages its own cookie jar and strips the ``Cookie`` header.
1049+
"""
1050+
sentry_init(
1051+
_experiments={
1052+
"data_collection": {
1053+
"http_headers": {
1054+
"request": {"mode": "allowlist", "terms": ["cookie", "custom"]}
1055+
}
1056+
}
1057+
},
1058+
)
1059+
app = SentryWsgiMiddleware(crashing_app)
1060+
events = capture_events()
1061+
1062+
environ = {
1063+
"REQUEST_METHOD": "GET",
1064+
"PATH_INFO": "/",
1065+
"SERVER_NAME": "localhost",
1066+
"SERVER_PORT": "80",
1067+
"wsgi.url_scheme": "http",
1068+
"HTTP_COOKIE": "sessionid=secret",
1069+
"HTTP_X_CUSTOM_HEADER": "passthrough",
1070+
}
1071+
1072+
with pytest.raises(ZeroDivisionError):
1073+
list(app(environ, lambda status, headers: None))
1074+
1075+
(event,) = events
1076+
headers = event["request"]["headers"]
1077+
1078+
assert headers["Cookie"] == "[Filtered]"
1079+
assert headers["X-Custom-Header"] == "passthrough"
1080+
1081+
1082+
def test_request_headers_legacy_pii_passes_headers_through(
1083+
sentry_init, crashing_app, capture_events
1084+
):
1085+
"""
1086+
With no ``data_collection`` configured and ``send_default_pii`` enabled,
1087+
the legacy path returns all headers unchanged -- including those in
1088+
``SENSITIVE_HEADERS``.
1089+
"""
1090+
sentry_init(send_default_pii=True)
1091+
app = SentryWsgiMiddleware(crashing_app)
1092+
client = Client(app)
1093+
events = capture_events()
1094+
1095+
with pytest.raises(ZeroDivisionError):
1096+
client.get(
1097+
"/",
1098+
headers={
1099+
"X-Forwarded-For": "1.2.3.4",
1100+
"X-Custom-Header": "passthrough",
1101+
},
1102+
)
1103+
1104+
(event,) = events
1105+
headers = event["request"]["headers"]
1106+
1107+
assert headers["X-Forwarded-For"] == "1.2.3.4"
1108+
assert headers["X-Custom-Header"] == "passthrough"
1109+
1110+
8611111
@pytest.mark.parametrize("send_default_pii", [True, False])
8621112
def test_user_ip_address_on_all_spans(sentry_init, capture_items, send_default_pii):
8631113
def dogpark(environ, start_response):

0 commit comments

Comments
 (0)