diff --git a/sentry_sdk/integrations/gcp.py b/sentry_sdk/integrations/gcp.py index cfd3344ab8..c0debc8094 100644 --- a/sentry_sdk/integrations/gcp.py +++ b/sentry_sdk/integrations/gcp.py @@ -246,9 +246,10 @@ def event_processor(event: "Event", hint: "Hint") -> "Optional[Event]": if hasattr(gcp_event, "method"): request["method"] = gcp_event.method + client_options = sentry_sdk.get_client().options + if hasattr(gcp_event, "query_string"): query_string = gcp_event.query_string.decode("utf-8", errors="replace") - client_options = sentry_sdk.get_client().options if has_data_collection_enabled(client_options): if query_string: filtered_qs = _apply_data_collection_filtering_to_query_string( @@ -263,11 +264,16 @@ def event_processor(event: "Event", hint: "Hint") -> "Optional[Event]": if hasattr(gcp_event, "headers"): request["headers"] = _filter_headers(gcp_event.headers) - if should_send_default_pii(): - if hasattr(gcp_event, "data"): + if hasattr(gcp_event, "data"): + if has_data_collection_enabled(client_options): + if ( + "incoming_request" + in client_options["data_collection"]["http_bodies"] + ): + request["data"] = gcp_event.data + elif should_send_default_pii(): request["data"] = gcp_event.data - else: - if hasattr(gcp_event, "data"): + else: # Unfortunately couldn't find a way to get structured body from GCP # event. Meaning every body is unstructured to us. request["data"] = AnnotatedValue.removed_because_raw_data() diff --git a/tests/integrations/gcp/test_gcp.py b/tests/integrations/gcp/test_gcp.py index b02518a413..a2b310b318 100644 --- a/tests/integrations/gcp/test_gcp.py +++ b/tests/integrations/gcp/test_gcp.py @@ -953,3 +953,110 @@ def cloud_function(functionhandler, event): assert "url.query" not in attrs else: assert attrs["url.query"] == expected_span + + +# Each case is (send_default_pii, data_collection, expected_data, expected_meta). +# ``send_default_pii`` / ``data_collection`` of None means the option is omitted; +# ``expected_data`` of None means the body is not attached to the event at all. +@pytest.mark.parametrize( + "send_default_pii, data_collection, expected_data, expected_meta", + [ + pytest.param( + True, + None, + '{"toy": "tennisball"}', + None, + id="send_default_pii_true_attaches_body", + ), + pytest.param( + False, + None, + "", + {"": {"rem": [["!raw", "x"]]}}, + id="send_default_pii_false_annotates_body_as_raw", + ), + pytest.param( + None, + {}, + '{"toy": "tennisball"}', + None, + id="http_bodies_default_attaches_body", + ), + pytest.param( + None, + {"http_bodies": ["incoming_request"]}, + '{"toy": "tennisball"}', + None, + id="http_bodies_incoming_request_attaches_body", + ), + pytest.param( + None, + {"http_bodies": []}, + None, + None, + id="http_bodies_empty_omits_body", + ), + pytest.param( + False, + {"http_bodies": ["incoming_request"]}, + '{"toy": "tennisball"}', + None, + id="http_bodies_incoming_request_attaches_body_despite_send_default_pii_false", + ), + pytest.param( + True, + {"http_bodies": []}, + None, + None, + id="http_bodies_empty_omits_body_despite_send_default_pii_true", + ), + ], +) +def test_request_body_data_collection_event_processor( + run_cloud_function, + send_default_pii, + data_collection, + expected_data, + expected_meta, +): + init_kwargs = _build_init_kwargs(send_default_pii, data_collection) + envelope_items, _, _ = run_cloud_function( + dedent( + """ + functionhandler = None + + from collections import namedtuple + GCPEvent = namedtuple("GCPEvent", ["headers", "method", "data"]) + event = GCPEvent( + headers={}, + method="POST", + data=b'{"toy": "tennisball"}', + ) + + def cloud_function(functionhandler, event): + raise Exception("something went wrong") + """ + ) + + FUNCTIONS_PRELUDE + + dedent( + """ + init_sdk(%s) + gcp_functions.worker_v1.FunctionHandler.invoke_user_function(functionhandler, event) + """ + % init_kwargs + ) + ) + + sentry_event = envelope_items[0] + request = sentry_event["request"] + + if expected_data is None: + assert "data" not in request + else: + assert request["data"] == expected_data + + request_meta = sentry_event.get("_meta", {}).get("request", {}) + if expected_meta is None: + assert "data" not in request_meta + else: + assert request_meta["data"] == expected_meta