Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/_asgi_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def _get_request_attributes(

filtered_headers = _filter_headers(headers)
for header, value in filtered_headers.items():
attributes[f"http.request.header.{header.lower()}"] = value
attributes[f"http.request.header.{header.lower()}"] = [value]

if has_data_collection_enabled(client_options):
filtered_query_string = None
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ async def sentry_app_handle(

header_attributes: "dict[str, Any]" = {}
for header, header_value in _filter_headers(headers).items():
header_attributes[f"http.request.header.{header.lower()}"] = (
header_attributes[f"http.request.header.{header.lower()}"] = [
header_value
)
]

url_attributes = {}
client_address_attributes = {}
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ def sentry_handler(

header_attributes: "dict[str, Any]" = {}
for header, header_value in _filter_headers(headers).items():
header_attributes[f"http.request.header.{header.lower()}"] = (
header_attributes[f"http.request.header.{header.lower()}"] = [
header_value
)
]

additional_attributes: "dict[str, Any]" = {}
if "httpMethod" in request_data:
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/chalice.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def wrapped_view_function(**function_args: "Any") -> "Any":

header_attrs: "Dict[str, Any]" = {}
for header, value in _filter_headers(headers).items():
header_attrs[f"http.request.header.{header.lower()}"] = value
header_attrs[f"http.request.header.{header.lower()}"] = [value]

additional_attrs: "Dict[str, Any]" = {}
if "method" in request_dict:
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ def sentry_func(
if hasattr(gcp_event, "headers"):
headers = gcp_event.headers
for header, header_value in _filter_headers(headers).items():
header_attributes[f"http.request.header.{header.lower()}"] = (
header_attributes[f"http.request.header.{header.lower()}"] = [
header_value
)
]

additional_attributes = {}
if hasattr(gcp_event, "method"):
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/quart.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ async def _request_websocket_started(app: "Quart", **kwargs: "Any") -> None:
for header, header_value in _filter_headers(
dict(request_websocket.headers)
).items():
header_attributes[f"http.request.header.{header.lower()}"] = header_value
header_attributes[f"http.request.header.{header.lower()}"] = [header_value]

segment.set_attributes(header_attributes)

Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/sanic.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def _get_request_attributes(request: "Request") -> "Dict[str, Any]":

headers = _filter_headers(dict(request.headers))
for header, value in headers.items():
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = value
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = [value]

urlparts = urlsplit(request.url)
client_options = sentry_sdk.get_client().options
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/tornado.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def _get_request_attributes(request: "Any") -> "Dict[str, Any]":

headers = _filter_headers(dict(request.headers))
for header, value in headers.items():
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = value
attributes[f"{SPANDATA.HTTP_REQUEST_HEADER}.{header.lower()}"] = [value]

attributes["url.path"] = request.path

Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ def _get_request_attributes(

headers = _filter_headers(dict(_get_headers(environ)))
for header, value in headers.items():
attributes[f"http.request.header.{header.lower()}"] = value
attributes[f"http.request.header.{header.lower()}"] = [value]

url_scheme = environ.get("wsgi.url_scheme")
if url_scheme:
Expand Down
14 changes: 6 additions & 8 deletions tests/integrations/aiohttp/test_aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1438,15 +1438,13 @@ async def hello(request):

# Data collection always substitutes sensitive headers with
# SENSITIVE_DATA_SUBSTITUTE ("[Filtered]"). The original token must not leak.
assert (
server_span["attributes"]["http.request.header.authorization"]
== SENSITIVE_DATA_SUBSTITUTE
)
assert server_span["attributes"]["http.request.header.authorization"] == [
SENSITIVE_DATA_SUBSTITUTE
]
# Non-sensitive headers pass through untouched.
assert (
server_span["attributes"]["http.request.header.x-custom-header"]
== "passthrough"
)
assert server_span["attributes"]["http.request.header.x-custom-header"] == [
"passthrough"
]


@pytest.mark.asyncio
Expand Down
26 changes: 13 additions & 13 deletions tests/integrations/asgi/test_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ async def test_capture_transaction(

assert span["attributes"]["network.protocol.name"] == "http"
assert span["attributes"]["http.request.method"] == "GET"
assert span["attributes"]["http.request.header.host"] == "localhost"
assert span["attributes"]["http.request.header.user-agent"] == "ASGI-Test-Client"
assert span["attributes"]["http.request.header.host"] == ["localhost"]
assert span["attributes"]["http.request.header.user-agent"] == ["ASGI-Test-Client"]

if should_send_pii:
assert (
Expand Down Expand Up @@ -707,8 +707,8 @@ def test_get_headers():
pytest.param(
{},
{
"http.request.header.authorization": "[Filtered]",
"http.request.header.x-custom-header": "passthrough",
"http.request.header.authorization": ["[Filtered]"],
"http.request.header.x-custom-header": ["passthrough"],
},
id="default_redacts_sensitive_headers",
),
Expand All @@ -720,18 +720,18 @@ def test_get_headers():
pytest.param(
{"http_headers": {"request": {"mode": "allowlist", "terms": ["custom"]}}},
{
"http.request.header.x-custom-header": "passthrough",
"http.request.header.x-forwarded-for": "[Filtered]",
"http.request.header.host": "[Filtered]",
"http.request.header.x-custom-header": ["passthrough"],
"http.request.header.x-forwarded-for": ["[Filtered]"],
"http.request.header.host": ["[Filtered]"],
},
id="allowlist_redacts_all_but_allowed_terms",
),
pytest.param(
{"http_headers": {"request": {"mode": "denylist", "terms": ["custom"]}}},
{
"http.request.header.x-custom-header": "[Filtered]",
"http.request.header.x-forwarded-for": "1.2.3.4",
"http.request.header.host": "localhost",
"http.request.header.x-custom-header": ["[Filtered]"],
"http.request.header.x-forwarded-for": ["1.2.3.4"],
"http.request.header.host": ["localhost"],
},
id="denylist_redacts_only_matched_terms",
),
Expand Down Expand Up @@ -798,8 +798,8 @@ async def test_request_headers_data_collection_cookie_always_redacted(
(span,) = [item.payload for item in items]
attributes = span["attributes"]

assert attributes["http.request.header.cookie"] == "[Filtered]"
assert attributes["http.request.header.x-custom-header"] == "passthrough"
assert attributes["http.request.header.cookie"] == ["[Filtered]"]
assert attributes["http.request.header.x-custom-header"] == ["passthrough"]


@pytest.mark.asyncio
Expand Down Expand Up @@ -828,7 +828,7 @@ async def test_get_request_attributes_url_with_filtered_host(
assert len(items) == 1
attributes = items[0].payload["attributes"]

assert attributes["http.request.header.host"] == "[Filtered]"
assert attributes["http.request.header.host"] == ["[Filtered]"]
assert attributes["url.full"] == "http://example.com/foo?somevalue=123"


Expand Down
45 changes: 22 additions & 23 deletions tests/integrations/aws_lambda/test_aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,17 +376,17 @@ def test_request_data_with_data_collection_allowlist(lambda_client, test_environ

assert _get_span_attr(attrs, "http.request.method") == "GET"
# Allowlisted, non-sensitive headers pass through.
assert _get_span_attr(attrs, "http.request.header.user-agent") == "custom"
assert _get_span_attr(attrs, "http.request.header.x-allow-me") == "yes"
assert _get_span_attr(attrs, "http.request.header.user-agent") == ["custom"]
assert _get_span_attr(attrs, "http.request.header.x-allow-me") == ["yes"]
# Not allowlisted -> filtered.
assert _get_span_attr(attrs, "http.request.header.host") == "[Filtered]"
assert (
_get_span_attr(attrs, "http.request.header.x-forwarded-proto") == "[Filtered]"
)
assert _get_span_attr(attrs, "http.request.header.host") == ["[Filtered]"]
assert _get_span_attr(attrs, "http.request.header.x-forwarded-proto") == [
"[Filtered]"
]
# Allowlisted but sensitive -> still filtered.
assert _get_span_attr(attrs, "http.request.header.authorization") == "[Filtered]"
assert _get_span_attr(attrs, "http.request.header.authorization") == ["[Filtered]"]
# Not allowlisted, and cookies are always filtered.
assert _get_span_attr(attrs, "http.request.header.cookie") == "[Filtered]"
assert _get_span_attr(attrs, "http.request.header.cookie") == ["[Filtered]"]


def test_request_data_with_data_collection_denylist(lambda_client, test_environment):
Expand Down Expand Up @@ -431,20 +431,19 @@ def test_request_data_with_data_collection_denylist(lambda_client, test_environm

assert _get_span_attr(attrs, "http.request.method") == "GET"
# Not denied by any term -> pass through.
assert (
_get_span_attr(attrs, "http.request.header.host")
== "iwsz2c7uwi.execute-api.us-east-1.amazonaws.com"
)
assert _get_span_attr(attrs, "http.request.header.x-custom") == "keep-me"
assert _get_span_attr(attrs, "http.request.header.host") == [
"iwsz2c7uwi.execute-api.us-east-1.amazonaws.com"
]
assert _get_span_attr(attrs, "http.request.header.x-custom") == ["keep-me"]
# Denied by custom terms.
assert _get_span_attr(attrs, "http.request.header.user-agent") == "[Filtered]"
assert (
_get_span_attr(attrs, "http.request.header.x-forwarded-proto") == "[Filtered]"
)
assert _get_span_attr(attrs, "http.request.header.user-agent") == ["[Filtered]"]
assert _get_span_attr(attrs, "http.request.header.x-forwarded-proto") == [
"[Filtered]"
]
# Denied by the built-in sensitive denylist.
assert _get_span_attr(attrs, "http.request.header.authorization") == "[Filtered]"
assert _get_span_attr(attrs, "http.request.header.authorization") == ["[Filtered]"]
# Cookies are always filtered.
assert _get_span_attr(attrs, "http.request.header.cookie") == "[Filtered]"
assert _get_span_attr(attrs, "http.request.header.cookie") == ["[Filtered]"]


def test_request_data_with_data_collection_off(lambda_client, test_environment):
Expand Down Expand Up @@ -841,10 +840,10 @@ def test_request_attributes(lambda_client, test_environment):
_get_span_attr(attrs, "url.query")
== "foo=bar&a-complicated-value=a%3Db%26c%3Dd"
)
assert (
_get_span_attr(attrs, "http.request.header.content-type") == "application/json"
)
assert _get_span_attr(attrs, "http.request.header.accept") == "text/html"
assert _get_span_attr(attrs, "http.request.header.content-type") == [
"application/json"
]
assert _get_span_attr(attrs, "http.request.header.accept") == ["text/html"]
assert _get_span_attr(attrs, "faas.name") == "BasicOkSpanStreamingDataCollection"
assert _get_span_attr(attrs, "cloud.provider") == "aws"
assert _get_span_attr(attrs, "cloud.platform") == "aws_lambda"
Expand Down
4 changes: 2 additions & 2 deletions tests/integrations/gcp/test_gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,8 +625,8 @@ def cloud_function(functionhandler, event):

assert attrs["http.request.method"] == "POST"
assert attrs["url.query"] == "foo=bar"
assert attrs["http.request.header.content-type"] == "application/json"
assert attrs["http.request.header.accept"] == "text/html"
assert attrs["http.request.header.content-type"] == ["application/json"]
assert attrs["http.request.header.accept"] == ["text/html"]
assert attrs["faas.name"] == "Google Cloud function"
assert attrs["gcp.project.id"] == "serverless_project"
assert attrs["faas.identity"] == "func_ID"
Expand Down
36 changes: 18 additions & 18 deletions tests/integrations/quart/test_quart.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,9 +792,9 @@ async def test_request_attributes(
pytest.param(
{},
{
"authorization": "[Filtered]",
"custom": "passthrough",
"cookie": "[Filtered]",
"authorization": ["[Filtered]"],
"custom": ["passthrough"],
"cookie": ["[Filtered]"],
},
id="data_collection_default_redacts_auth_header",
),
Expand All @@ -806,9 +806,9 @@ async def test_request_attributes(
pytest.param(
{"http_headers": {"request": {"mode": "allowlist"}}},
{
"authorization": "[Filtered]",
"custom": "[Filtered]",
"cookie": "[Filtered]",
"authorization": ["[Filtered]"],
"custom": ["[Filtered]"],
"cookie": ["[Filtered]"],
},
id="data_collection_allow_list_redacts_terms_that_do_not_appear",
),
Expand All @@ -819,36 +819,36 @@ async def test_request_attributes(
}
},
{
"authorization": "[Filtered]",
"custom": "[Filtered]",
"cookie": "[Filtered]",
"authorization": ["[Filtered]"],
"custom": ["[Filtered]"],
"cookie": ["[Filtered]"],
},
id="data_collection_allow_list_redacts_sensitive_terms_even_when_provided_by_user",
),
pytest.param(
{"http_headers": {"request": {"mode": "allowlist", "terms": ["custom"]}}},
{
"authorization": "[Filtered]",
"custom": "passthrough",
"cookie": "[Filtered]",
"authorization": ["[Filtered]"],
"custom": ["passthrough"],
"cookie": ["[Filtered]"],
},
id="data_collection_allow_list_does_not_redact_provided_term",
),
pytest.param(
{"http_headers": {"request": {"mode": "denylist", "terms": ["custom"]}}},
{
"authorization": "[Filtered]",
"custom": "[Filtered]",
"cookie": "[Filtered]",
"authorization": ["[Filtered]"],
"custom": ["[Filtered]"],
"cookie": ["[Filtered]"],
},
id="data_collection_deny_list_redacts_sensitive_terms_when_provided_by_user",
),
pytest.param(
{"http_headers": {"request": {"mode": "allowlist", "terms": ["cookie"]}}},
{
"authorization": "[Filtered]",
"custom": "[Filtered]",
"cookie": "[Filtered]",
"authorization": ["[Filtered]"],
"custom": ["[Filtered]"],
"cookie": ["[Filtered]"],
},
id="data_collection_cookie_is_always_redacted_even_when_allow_listed",
),
Expand Down
Loading