feat(span-first): Support before_send_span
#6239
3 issues
Medium
`del res["attributes"]` could raise KeyError if span lacks attributes key - `sentry_sdk/_span_batcher.py:169-170`
In _to_transport_format, when item.get("attributes") is falsy, the code attempts del res["attributes"]. Since res is built by copying keys from item (excluding _segment_span), if item lacks an attributes key entirely, res will also lack it, causing a KeyError. While current code paths always include attributes via _to_json(), the SpanJSON TypedDict marks attributes as NotRequired, and the code structure suggests defensive handling of missing attributes. The implementation is buggy and could fail if future changes allow spans without attributes to reach the batcher, or if external code constructs SpanJSON dicts manually.
before_send_span silently wipes all span attributes when returned dict omits 'attributes' key - `sentry_sdk/client.py:980-984`
When a user's before_send_span callback returns a dict that contains a 'name' key but no (or a falsy) 'attributes' key, the code unconditionally resets telemetry._attributes = {} and then iterates over (serialized.get('attributes') or {}).items(), which is empty. This silently drops every attribute on the span, including SDK-internal ones such as 'sentry.origin', 'sentry.op', 'sentry.segment.id/name', thread info, and 'process.command_args'. Users who simply mutate name (e.g., return {'name': 'x'}) will unintentionally strip all telemetry attributes, breaking downstream processing without any warning.
Low
get_before_send_span lacks defensive check for missing _experiments key - `sentry_sdk/utils.py:2120`
The new get_before_send_span function (line 2121) directly accesses options["_experiments"] without using .get(), which could raise KeyError if the _experiments key is missing. While normal initialization through _Client and get_options() ensures _experiments is always present (defaulting to {}), the function is less defensive than it could be. This differs from get_before_send_metric and get_before_send_log which check a top-level option first (providing partial short-circuit protection). In practice, this is not exploitable through normal SDK usage since all initialization paths include _experiments, but represents a potential maintenance risk if the code is refactored or called in unexpected contexts.
4 skills analyzed
| Skill | Findings | Duration | Cost |
|---|---|---|---|
| security-review | 0 | 17.5s | $0.18 |
| code-review | 1 | 1m 40s | $1.76 |
| find-bugs | 2 | 2m 16s | $2.49 |
| skill-scanner | 0 | 37.1s | $0.11 |
⏱ 4m 50s · 1.9M in / 16.7k out · $4.54 (+verification: $2.35, +dedup: $0.00, +merge: $0.00)