diff --git a/sentry_sdk/integrations/gcp.py b/sentry_sdk/integrations/gcp.py index db1a24af92..6e6883e7df 100644 --- a/sentry_sdk/integrations/gcp.py +++ b/sentry_sdk/integrations/gcp.py @@ -11,14 +11,13 @@ from sentry_sdk.integrations import Integration from sentry_sdk.integrations._wsgi_common import _filter_headers from sentry_sdk.integrations.cloud_resource_context import CLOUD_PROVIDER -from sentry_sdk.scope import Scope, should_send_default_pii +from sentry_sdk.scope import Scope from sentry_sdk.traces import SegmentNameSource from sentry_sdk.utils import ( AnnotatedValue, TimeoutThread, capture_internal_exceptions, event_from_exception, - has_data_collection_enabled, logger, reraise, ) @@ -98,17 +97,12 @@ def sentry_func( if hasattr(gcp_event, "query_string"): query_string = gcp_event.query_string.decode("utf-8", errors="replace") if query_string: - if has_data_collection_enabled(client.options): - filtered_qs = _apply_data_collection_filtering_to_query_string( - query_string=query_string, - behaviour=client.options["data_collection"][ - "url_query_params" - ], - ) - if filtered_qs: - additional_attributes["url.query"] = filtered_qs - elif should_send_default_pii(): - additional_attributes["url.query"] = query_string + filtered_qs = _apply_data_collection_filtering_to_query_string( + query_string=query_string, + behaviour=client.options["data_collection"]["url_query_params"], + ) + if filtered_qs: + additional_attributes["url.query"] = filtered_qs sampling_context = { "gcp_env": { @@ -229,28 +223,19 @@ def event_processor(event: "Event", hint: "Hint") -> "Optional[Event]": if hasattr(gcp_event, "query_string"): query_string = gcp_event.query_string.decode("utf-8", errors="replace") - if has_data_collection_enabled(client_options): - if query_string: - filtered_qs = _apply_data_collection_filtering_to_query_string( - query_string=query_string, - behaviour=client_options["data_collection"]["url_query_params"], - ) - if filtered_qs: - request["query_string"] = filtered_qs - else: - request["query_string"] = query_string + if query_string: + filtered_qs = _apply_data_collection_filtering_to_query_string( + query_string=query_string, + behaviour=client_options["data_collection"]["url_query_params"], + ) + if filtered_qs: + request["query_string"] = filtered_qs if hasattr(gcp_event, "headers"): request["headers"] = _filter_headers(gcp_event.headers) 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(): + if "incoming_request" in client_options["data_collection"]["http_bodies"]: request["data"] = gcp_event.data else: # Unfortunately couldn't find a way to get structured body from GCP diff --git a/tests/integrations/gcp/test_gcp.py b/tests/integrations/gcp/test_gcp.py index c95122faf1..6910254895 100644 --- a/tests/integrations/gcp/test_gcp.py +++ b/tests/integrations/gcp/test_gcp.py @@ -613,7 +613,7 @@ def cloud_function(functionhandler, event): + FUNCTIONS_PRELUDE + dedent( """ - init_sdk(traces_sample_rate=1.0, send_default_pii=True) + init_sdk(traces_sample_rate=1.0, data_collection={}) gcp_functions.worker_v1.FunctionHandler.invoke_user_function(functionhandler, event) """ ) @@ -633,131 +633,53 @@ def cloud_function(functionhandler, event): assert attrs["faas.entry_point"] == "cloud_function" -def test_no_query_string_without_pii(run_cloud_function): - _, _, span_items = run_cloud_function( - dedent( - """ - functionhandler = None - - from collections import namedtuple - GCPEvent = namedtuple("GCPEvent", ["headers", "method", "query_string"]) - event = GCPEvent( - headers={}, - method="GET", - query_string=b"secret=hunter2", - ) - - def cloud_function(functionhandler, event): - return "ok" - """ - ) - + FUNCTIONS_PRELUDE - + dedent( - """ - init_sdk(traces_sample_rate=1.0, send_default_pii=False) - gcp_functions.worker_v1.FunctionHandler.invoke_user_function(functionhandler, event) - """ - ) - ) - - assert len(span_items) == 1 - segment_span = span_items[0] - attrs = segment_span["attributes"] - - assert "url.query" not in attrs - assert attrs["http.request.method"] == "GET" - assert attrs["faas.name"] == "Google Cloud function" - assert attrs["gcp.project.id"] == "serverless_project" - assert attrs["faas.identity"] == "func_ID" - assert attrs["faas.entry_point"] == "cloud_function" - - -# Each case is (send_default_pii, data_collection, expected_event, expected_span). -# ``send_default_pii`` / ``data_collection`` of None means the option is omitted. -# The two paths only diverge for the legacy (no ``data_collection``) rows: the -# event processor always records the raw query string, while the span-streaming -# path gates the ``url.query`` attribute on ``send_default_pii``. +# Each case is (data_collection, expected_span). ``data_collection`` of None +# means the option is omitted. _QUERY_STRING_DATA_COLLECTION_CASES = [ pytest.param( - True, - None, - "toy=tennisball&color=red&auth=secret", - "toy=tennisball&color=red&auth=secret", - id="send_default_pii_true", - ), - pytest.param( - False, - None, - "toy=tennisball&color=red&auth=secret", - None, - id="send_default_pii_false", - ), - pytest.param( - None, - None, - "toy=tennisball&color=red&auth=secret", - None, - id="defaults", - ), - # data_collection configured: query string is routed through filtering. - # Spec defaults -> denylist: only the sensitive ``auth`` is redacted. - pytest.param( - None, {}, "toy=tennisball&color=red&auth=%5BFiltered%5D", - "toy=tennisball&color=red&auth=%5BFiltered%5D", id="data_collection_denylist_default", ), pytest.param( - None, {"url_query_params": {"mode": "denylist", "terms": ["toy"]}}, "toy=%5BFiltered%5D&color=red&auth=%5BFiltered%5D", - "toy=%5BFiltered%5D&color=red&auth=%5BFiltered%5D", id="data_collection_denylist_custom_terms", ), # allowlist with only ``toy`` allowed: ``color`` is redacted even though it # is not sensitive, proving the redaction comes from the allowlist. pytest.param( - None, {"url_query_params": {"mode": "allowlist", "terms": ["toy"]}}, "toy=tennisball&color=%5BFiltered%5D&auth=%5BFiltered%5D", - "toy=tennisball&color=%5BFiltered%5D&auth=%5BFiltered%5D", id="data_collection_allowlist", ), pytest.param( - None, {"url_query_params": {"mode": "off"}}, None, - None, id="data_collection_off", ), ] -def _build_init_kwargs(send_default_pii, data_collection): +def _build_init_kwargs(data_collection): """Render the keyword-argument string passed to ``init_sdk`` in the subprocess.""" - kwargs = [] - if send_default_pii is not None: - kwargs.append("send_default_pii=%r" % send_default_pii) - if data_collection is not None: - kwargs.append("data_collection=%r" % (data_collection,)) + if data_collection is None: + return "" - return ", ".join(kwargs) + return "data_collection=%r" % (data_collection,) @pytest.mark.parametrize( - "send_default_pii, data_collection, expected_event, expected_span", + "data_collection, expected_span", _QUERY_STRING_DATA_COLLECTION_CASES, ) def test_query_string_data_collection( run_cloud_function, - send_default_pii, data_collection, - expected_event, expected_span, ): - init_kwargs = _build_init_kwargs(send_default_pii, data_collection) + init_kwargs = _build_init_kwargs(data_collection) _, _, span_items = run_cloud_function( dedent( """ @@ -793,71 +715,37 @@ def cloud_function(functionhandler, event): 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. +# Each case is (data_collection, expected_data, expected_meta). @pytest.mark.parametrize( - "send_default_pii, data_collection, expected_data, expected_meta", + "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", + "", + {"": {"rem": [["!raw", "x"]]}}, + id="http_bodies_empty_annotates_body_as_raw", ), ], ) 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) + init_kwargs = _build_init_kwargs(data_collection) envelope_items, _, _ = run_cloud_function( dedent( """