Skip to content

feat(asgi): Gate client IP/user info behind data_collection config#6841

Open
ericapisani wants to merge 2 commits into
py-2583-query-params-awsfrom
py-2586-gate-user-data
Open

feat(asgi): Gate client IP/user info behind data_collection config#6841
ericapisani wants to merge 2 commits into
py-2583-query-params-awsfrom
py-2586-gate-user-data

Conversation

@ericapisani

@ericapisani ericapisani commented Jul 17, 2026

Copy link
Copy Markdown
Member

Apply data_collection.user_info filtering to REMOTE_ADDR/client.address,
falling back to send_default_pii when data_collection is not configured.

Also convert earlier query parameter data collection tests to end-to-end assertions via a real ASGI app.

Ref PY-2586
Ref #6746

Apply data_collection.user_info filtering to REMOTE_ADDR/client.address,
falling back to send_default_pii when data_collection is not configured.

Also convert earlier query parameter data collection tests to end-to-end assertions via a real ASGI app.

Ref PY-2586
Ref #6746
@linear-code

linear-code Bot commented Jul 17, 2026

Copy link
Copy Markdown

PY-2586

Comment on lines +152 to +153
if has_data_collection_enabled(client_options):
if client_options["data_collection"]["user_info"]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client_options may be unbound when scope type is not http/websocket

In both _get_request_data and _get_request_attributes, client_options = sentry_sdk.get_client().options is assigned only inside the if ty in ("http", "websocket") block, but is later referenced unconditionally when client/asgi_scope_client is truthy. If a scope with ty other than http/websocket carries a client entry, has_data_collection_enabled(client_options) raises NameError. In practice only http/websocket ASGI scopes carry a client key (lifespan scopes do not), so this path is largely unreachable with spec-compliant servers, but it is a latent regression: the prior code branched on should_send_default_pii() and did not depend on client_options.

Evidence
  • client_options is assigned at line 128 (and 181) inside if ty in ("http", "websocket").
  • Lines 151-156 (_get_request_data) and 234-239 (_get_request_attributes) reference has_data_collection_enabled(client_options) at the outer indent, guarded only by if client: / if asgi_scope_client:, not by scope type.
  • Per ASGI, only http/websocket scopes include a client key, so client being truthy while ty is neither is not expected from compliant servers, making the NameError path hard to reach in practice.
  • The regression is that the old branch used should_send_default_pii(), which needed no client_options, so a malformed/non-standard scope with a client value would now raise instead of behaving gracefully.
Also found at 1 additional location
  • sentry_sdk/integrations/_asgi_common.py:233-235

Identified by Warden code-review · DVQ-38F

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch bot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix attempt detected (commit 8c9f43d)

The fix moved client_options above the http/websocket block in _get_request_data (line 153 resolved), but the identical bug at the additional reported location (_get_request_attributes, lines 233-235) still has client_options assigned only inside the http/websocket block, so the NameError path persists there.

The original issue appears unresolved. Please review and try again.

Evaluated by Warden

Comment thread tests/integrations/asgi/test_asgi.py
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

94263 passed | ⏭️ 6338 skipped | Total: 100601 | Pass Rate: 93.7% | Execution Time: 331m 19s

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +2385
Passed Tests 📈 +2349
Failed Tests
Skipped Tests 📈 +36

All tests are passing successfully.

✅ Patch coverage is 100.00%. Project has 2513 uncovered lines.
❌ Project coverage is 89.62%. Comparing base (base) to head (head).

Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    89.68%    89.62%    -0.06%
==========================================
  Files          193       193         —
  Lines        24007     24218      +211
  Branches      8342      8504      +162
==========================================
+ Hits         21530     21705      +175
- Misses        2477      2513       +36
- Partials      1386      1396       +10

Generated by Codecov Action

@ericapisani
ericapisani marked this pull request as ready for review July 20, 2026 15:27
@ericapisani
ericapisani requested a review from a team as a code owner July 20, 2026 15:27

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8c9f43d. Configure here.

attributes["client.address"] = ip
elif should_send_default_pii():
ip = _get_ip(asgi_scope)
attributes["client.address"] = ip

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unbound client_options outside type check

Medium Severity

In _get_request_attributes, client_options is assigned only inside the http/websocket branch, but the new user_info gating reads it afterward. For a non-http/websocket scope with a truthy client, that path raises UnboundLocalError. The matching move of client_options outside the type check was applied in _get_request_data but missed here.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8c9f43d. Configure here.

trace_lifecycle="stream",
_experiments={
**init_kwargs.pop("_experiments", {}),
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shared param dict mutated by pop

Medium Severity

test_user_info_data_collection_with_streamed_spans calls init_kwargs.pop("_experiments", {}) on the shared USER_INFO_CASES dicts. That permanently removes _experiments for later uses of the same cases, including test_user_info_data_collection, so data-collection expectations can silently diverge depending on collection order.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8c9f43d. Configure here.

Comment on lines +235 to +236
if has_data_collection_enabled(client_options):
if client_options["data_collection"]["user_info"]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The variable client_options is used outside the conditional block where it is defined, which can cause an UnboundLocalError.
Severity: LOW

Suggested Fix

Move the initialization of client_options to a higher scope, before the if ty in ("http", "websocket"): conditional block, to ensure it is always defined before being accessed.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: sentry_sdk/integrations/_asgi_common.py#L235-L236

Potential issue: In the function `_get_request_attributes`, the variable
`client_options` is initialized within a conditional block that checks if the scope type
`ty` is `"http"` or `"websocket"`. However, `client_options` is later accessed outside
of this conditional block. If the function is called with a scope type other than "http"
or "websocket" (e.g., `"lifespan"`) and `asgi_scope.get("client")` is truthy, an
`UnboundLocalError` will be raised because `client_options` was never assigned.

Also affects:

  • sentry_sdk/integrations/_asgi_common.py:235~237

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant