Skip to content

fix: fill the user context before the router can throw - #1068

Open
guillaume-sainthillier wants to merge 1 commit into
getsentry:masterfrom
guillaume-sainthillier:fix/request-listener-before-router
Open

guillaume-sainthillier wants to merge 1 commit into
getsentry:masterfrom
guillaume-sainthillier:fix/request-listener-before-router

Conversation

@guillaume-sainthillier

Copy link
Copy Markdown

Description

RequestListener::handleKernelRequestEvent sets the Sentry user IP from Request::getClientIp(), which resolves the real client through the configured trusted proxies. It is registered on kernel.request at priority 5, but RouterListener throws NotFoundHttpException at priority 32.

So on any unknown URL the listener never runs, the scope carries no user, and RequestIntegration falls back to $_SERVER['REMOTE_ADDR'] — which behind a reverse proxy is the proxy itself, not the client.

The result is inconsistent within the same application: a 404 raised by the router is attributed to the proxy, while a 404 raised by a controller on the same site carries the real client IP. I hit this on a site fronted by a CDN, where it collapsed several million events onto a single "user" and made the issue list unusable for triage.

This moves the listener to priority 64:

  • above RouterListener (32) and the security firewall (8), which are the early listeners that throw;
  • below ValidateRequestListener (256), so conflicting forwarded headers are still rejected before getClientIp() is ever called on them.

The listener only reads the request, so it has no dependency on routing or security having run — it used to sit at 5 simply because it once also read the security token, which it no longer does.

This mirrors #1043, which moved RuntimeContextListener above the router for the same class of reason.

Tests

  • ClientIpEnd2EndTest covers both paths end to end behind a trusted proxy: a routing 404 (/missing-page) and a controller exception (/exception). Both must report the forwarded client IP. The first one fails on master and passes with the change; the second passes either way and guards the path that already worked.
  • SentryExtensionTest::testRequestListenerFillsUserContextBeforeTheRouterCanThrow pins the priority window (> 32, < 256) rather than the literal value, so the intent survives a future renumbering.

vendor/bin/phpunit — 619 tests, 1782 assertions, green. php-cs-fixer clean. phpstan reports the same 3 pre-existing errors as master, none new. (psalm 4.30 crashes on PHP 8.5 in my environment, unrelated to this change.)

Issues

Reminders

  • PR title uses conventional commit style
  • Read CONTRIBUTING.md — CHANGELOG is updated at release time, so this PR does not touch it

RequestListener::handleKernelRequestEvent sets the Sentry user IP from
Request::getClientIp(), which resolves the real client through trusted
proxies. It ran at kernel.request priority 5, but RouterListener throws
NotFoundHttpException at priority 32, so on any unknown URL the listener
never ran. RequestIntegration then fell back to $_SERVER['REMOTE_ADDR'],
which behind a reverse proxy is the proxy itself.

The result was inconsistent within one application: a 404 raised by the
router was attributed to the proxy, while a 404 raised by a controller on
the same site carried the real client IP. On a site fronted by a CDN that
collapsed millions of events onto a single "user".

Move the listener to priority 64: above RouterListener (32) and the
security firewall (8), but below ValidateRequestListener (256), so that
conflicting forwarded headers are still rejected before getClientIp() is
called on them.
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