`EventScrubber` doesn't scrub a request body that's a top level JSON array

Open Beginner friendly
#7,543 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
flask, python
Domain
security

Research direction

Reproduce the issue with the supplied repro.py, then inspect EventScrubber.scrub_request, scrub_dict, and scrub_list to trace how request data is handled. Use the existing scrubber tests or add coverage for a top-level array; done means nested password or api_key values are filtered in array bodies just as they are in object bodies.

Written by the indexing model from the issue text.

Description

Waiting for: Product Owner
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.

Dominant language
Python
Stars
2.2k
Forks
672
Avg merge
1d 13m
Merged PRs (30d)
212

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from getsentry/sentry-python

All issues in getsentry/sentry-python

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.