Skip to content
Open
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
9 changes: 9 additions & 0 deletions sentry_sdk/scrubber.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ def scrub_request(self, event: "Event") -> None:
self.scrub_dict(data) # no-op unless data is a dict
self.scrub_list(data) # no-op unless data is a list

# Some integrations (such as AWS Lambda) supply query parameters
# as a mapping instead of a raw query string. Apply the same
# configured scrubbing as for request bodies and headers.
# Leave raw strings unchanged for backwards compatibility.
if "query_string" in event["request"]:
query_string = event["request"]["query_string"]
self.scrub_dict(query_string)
self.scrub_list(query_string)

def scrub_extra(self, event: "Event") -> None:
with capture_internal_exceptions():
if "extra" in event:
Expand Down
82 changes: 82 additions & 0 deletions tests/test_scrubber.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,85 @@ def test_recursive_scrubber_does_not_override_original(sentry_init, capture_even
(frame,) = frames
assert data["csrf"] == "secret"
assert frame["vars"]["data"]["csrf"] == "[Filtered]"



def test_query_string_mapping_scrubbing():
# AWS Lambda provides queryStringParameters as a mapping; legacy
# request scrubbing must not leave secrets in its query_string.
event = {
"request": {
"query_string": {
"token": "do-not-send-token",
"AUTH": "do-not-send-auth",
"password": ["do-not-send-password"],
"safe": "keep-me",
}
}
}

EventScrubber().scrub_event(event)

query_string = event["request"]["query_string"]
for key in ("token", "AUTH", "password"):
assert query_string[key].value == "[Filtered]"
assert query_string[key].metadata == {"rem": [["!config", "s"]]}
assert query_string["safe"] == "keep-me"


def test_query_string_mapping_respects_custom_denylist():
event = {
"request": {
"query_string": {
"color": "sensitive-value",
"safe": "keep-me",
}
}
}
EventScrubber(denylist=["color"]).scrub_event(event)
assert event["request"]["query_string"]["color"].value == "[Filtered]"
assert event["request"]["query_string"]["safe"] == "keep-me"


def test_query_string_string_remains_unchanged():
# The mapping-only improvement deliberately does not rewrite text
# query strings, which are handled by other SDK configuration paths.
event = {"request": {"query_string": "token=a&safe=b"}}
EventScrubber().scrub_event(event)
assert event["request"]["query_string"] == "token=a&safe=b"


def test_query_string_list_of_mappings_scrubbed():
event = {
"request": {
"query_string": [{"token": "secret"}, {"safe": "keep-me"}],
}
}
EventScrubber().scrub_event(event)
assert event["request"]["query_string"][0]["token"].value == "[Filtered]"
assert event["request"]["query_string"][1]["safe"] == "keep-me"



def test_query_string_mapping_filtered_before_send(sentry_init, capture_events):
sentry_init()
events = capture_events()

capture_event(
{
"message": "test query parameters",
"request": {
"url": "https://example.test/resource",
"query_string": {"token": "do-not-send", "safe": "keep-me"},
},
}
)

(event,) = events
assert event["request"]["query_string"] == {
"token": "[Filtered]",
"safe": "keep-me",
}
assert event["_meta"]["request"]["query_string"]["token"] == {
"": {"rem": [["!config", "s"]]}
}