How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.69.2 (also on master at 494ecb3)
Steps to Reproduce
When a request body parses to a top level JSON array, nothing in it gets scrubbed. The same secrets in an object body are filtered normally. No event_scrubber argument, no _experiments data collection config, default max_request_body_size. The integration and the transport in the snippet are only there to catch the event.
A top level array is the usual shape for a bulk endpoint, something like POST /api/v1/users/bulk with a list of records. If those records carry a password or an api_key, they leave the process in the clear. A team that spot checks a single object endpoint sees scrubbing work fine, so there's nothing to tip them off.
Python 3.13.15, Flask 3.1.3, sentry-sdk 2.69.2. Save this as repro.py and run it.
import json
import logging
import sentry_sdk
from flask import Flask
from sentry_sdk.integrations.flask import FlaskIntegration
events = []
class Capture(sentry_sdk.transport.Transport):
def capture_envelope(self, envelope):
for item in envelope.items:
if item.headers.get("type") == "event":
events.append(item.payload.json)
sentry_sdk.init(
dsn="https://public@example.com/1",
integrations=[FlaskIntegration()],
transport=Capture(),
)
app = Flask(__name__)
app.logger.disabled = True
logging.getLogger("werkzeug").disabled = True
@app.route("/bulk", methods=["POST"])
def bulk():
raise ValueError("boom")
@app.route("/single", methods=["POST"])
def single():
raise ValueError("boom")
client = app.test_client()
client.post("/bulk", json=[{"user": "a", "password": "hunter2"}])
client.post("/single", json={"user": "a", "password": "hunter2"})
sentry_sdk.get_client().close()
print("array body :", json.dumps(events[0]["request"]["data"]))
print("object body:", json.dumps(events[1]["request"]["data"]))
Expected Result
The array body should come out filtered the same way the object body does:
array body : [{"password": "[Filtered]", "user": "a"}]
object body: {"password": "[Filtered]", "user": "a"}
Actual Result
array body : [{"password": "hunter2", "user": "a"}]
object body: {"password": "[Filtered]", "user": "a"}
EventScrubber.scrub_request calls scrub_dict on event["request"]["data"], and scrub_dict returns immediately when what it's given isn't a dict, so a list body stops scrubbing before it starts. The class already ships scrub_list, whose docstring says it walks a list and any nested lists and calls scrub_dict on every dictionary it finds, but it's never called on the request body.
This is separate from #7542, which is about how denylist keys are matched. Here the keys never get looked at at all. I have a fix and tests ready if you want a PR.
How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.69.2 (also on master at 494ecb3)
Steps to Reproduce
When a request body parses to a top level JSON array, nothing in it gets scrubbed. The same secrets in an object body are filtered normally. No
event_scrubberargument, no_experimentsdata collection config, defaultmax_request_body_size. The integration and the transport in the snippet are only there to catch the event.A top level array is the usual shape for a bulk endpoint, something like
POST /api/v1/users/bulkwith a list of records. If those records carry a password or anapi_key, they leave the process in the clear. A team that spot checks a single object endpoint sees scrubbing work fine, so there's nothing to tip them off.Python 3.13.15, Flask 3.1.3,
sentry-sdk2.69.2. Save this asrepro.pyand run it.Expected Result
The array body should come out filtered the same way the object body does:
Actual Result
EventScrubber.scrub_requestcallsscrub_dictonevent["request"]["data"], andscrub_dictreturns immediately when what it's given isn't a dict, so a list body stops scrubbing before it starts. The class already shipsscrub_list, whose docstring says it walks a list and any nested lists and callsscrub_dicton every dictionary it finds, but it's never called on the request body.This is separate from #7542, which is about how denylist keys are matched. Here the keys never get looked at at all. I have a fix and tests ready if you want a PR.