From 92d1e604ba9b2be2cfa6addbba37913283c07ea2 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Fri, 14 Aug 2026 17:32:06 -0400 Subject: [PATCH] test(bottle): Add tests for data_collection gating of request body Refs PY-2419 Refs #6283 --- tests/integrations/bottle/test_bottle.py | 139 +++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/tests/integrations/bottle/test_bottle.py b/tests/integrations/bottle/test_bottle.py index 1c34a253d5..d670dde32b 100644 --- a/tests/integrations/bottle/test_bottle.py +++ b/tests/integrations/bottle/test_bottle.py @@ -807,3 +807,142 @@ def handle(): assert events[0]["exception"]["values"][0]["mechanism"]["handled"] is True else: assert len(events) == 0 + + +@pytest.mark.parametrize( + "data_collection, expect_body", + [ + pytest.param({}, True, id="data_collection_http_bodies_default"), + pytest.param( + {"http_bodies": ["incoming_request"]}, + True, + id="data_collection_http_bodies_incoming_request", + ), + pytest.param( + {"http_bodies": ["outgoing_request"]}, + False, + id="data_collection_http_bodies_outgoing_request_only", + ), + pytest.param( + {"http_bodies": []}, False, id="data_collection_http_bodies_empty" + ), + ], +) +def test_request_body_data_collection( + sentry_init, capture_events, app, get_client, data_collection, expect_body +): + sentry_init( + integrations=[BottleIntegration()], + _experiments={"data_collection": data_collection}, + ) + + data = {"foo": "bar"} + + @app.route("/", method="POST") + def index(): + capture_message("hi") + return "ok" + + events = capture_events() + + client = get_client() + response = client.post("/", content_type="application/json", data=json.dumps(data)) + assert response[1] == "200 OK" + + (event,) = events + if expect_body: + assert event["request"]["data"] == data + else: + assert "data" not in event["request"] + + +def test_request_body_dropped_with_form_and_files_data_collection( + sentry_init, capture_events, app, get_client +): + sentry_init( + integrations=[BottleIntegration()], + max_request_body_size="always", + _experiments={"data_collection": {"http_bodies": []}}, + ) + + data = { + "foo": "bar", + "file": (BytesIO(b"hello"), "hello.txt"), + } + + @app.route("/", method="POST") + def index(): + import bottle + + assert list(bottle.request.forms) == ["foo"] + assert list(bottle.request.files) == ["file"] + capture_message("hi") + return "ok" + + events = capture_events() + + client = get_client() + response = client.post("/", data=data) + assert response[1] == "200 OK" + + (event,) = events + assert "data" not in event["request"] + assert "data" not in event.get("_meta", {}).get("request", {}) + + +def test_transaction_request_body_data_collection( + sentry_init, capture_events, app, get_client +): + sentry_init( + integrations=[BottleIntegration()], + traces_sample_rate=1.0, + _experiments={"data_collection": {"http_bodies": []}}, + ) + + data = {"username": "sentry-user", "age": "26"} + + @app.route("/", method="POST") + def index(): + capture_message("hi") + return "ok" + + events = capture_events() + + client = get_client() + response = client.post("/", content_type="application/json", data=json.dumps(data)) + assert response[1] == "200 OK" + + event, transaction_event = events + assert "data" not in event["request"] + assert "data" not in transaction_event["request"] + + +def test_oversized_request_body_not_annotated_data_collection( + sentry_init, capture_events, app, get_client +): + """ + The gating happens before the size check, so an oversized body is dropped + outright instead of being reported as removed because of the size limit. + """ + sentry_init( + integrations=[BottleIntegration()], + max_request_body_size="small", + _experiments={"data_collection": {"http_bodies": []}}, + ) + + data = "a" * 2000 + + @app.route("/", method="POST") + def index(): + capture_message("hi") + return "ok" + + events = capture_events() + + client = get_client() + response = client.post("/", data=data) + assert response[1] == "200 OK" + + (event,) = events + assert "data" not in event["request"] + assert "data" not in event.get("_meta", {}).get("request", {})