`EventScrubber` doesn't scrub a request body that's a top level JSON array
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
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
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from getsentry/sentry-python
-
Python
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
getsentry/sentry-python#6992 · 1 comment ·
-
Improvement Python
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
getsentry/sentry-python#6970 · 1 comment ·
-
Bug Python
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
getsentry/sentry-python#6504 · 1 comment ·
-
Improvement Python Spans
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
getsentry/sentry-python#5833 · 1 comment ·
-
Improvement Python Spans
Difficulty 1/5 Under an hour Newbie friendliness 84/100
getsentry/sentry-python#5832 · 1 comment ·
All issues in getsentry/sentry-python
Similar issues
-
documentation help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
simonw/sqlite-utils#872 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100