Compare commits

1 Commits

Author SHA1 Message Date
5ff7cdbdb5 Send the chi route pattern to Sentry, not the concrete path (closes #179)
All checks were successful
check / check (push) Successful in 2m44s
The Sentry SDK builds Request.URL as scheme://host/path from the
concrete path, which on the receiver route is /webhook/<uuid> in full.
That UUID is a write capability, not an identifier: anyone holding it
can post events this service accepts and its targets then deliver. A
third-party tracker has its own retention, access control and deletion
policy, so the rule the local access log follows does not carry across
that boundary.

The BeforeSend hook now rebuilds the URL from the chi route pattern,
on every route rather than by route list, since a route-conditional
rule leaks on any route someone forgets to add. Scheme and host are
kept and everything else in the URL is discarded rather than edited:
the scheme is the CSRF TLS decision the header allowlist relies on,
and the host is operator configuration already carried by the
allowlisted Host header.

The pattern is reachable only on the error dispatch, where sentryhttp
puts the request on the context the client copies onto the hint. A
finished span captures with a nil hint, so BeforeSendTransaction sees
no context; there and wherever else the pattern is missing the path
falls back to the literal /(redacted), never to the concrete path, and
a URL that will not parse into a scheme is withheld whole. A
transaction event's SDK-built "METHOD /path" name carries the same
capability and is rewritten on the same terms.
2026-08-18 00:16:23 +00:00
2 changed files with 7 additions and 10 deletions

View File

@@ -1049,11 +1049,9 @@ the chi route pattern instead: `http://host/webhook/{uuid}`.
The scheme and the host are kept, and everything else in the URL is The scheme and the host are kept, and everything else in the URL is
discarded rather than edited, so a future SDK version that starts discarded rather than edited, so a future SDK version that starts
appending a query string cannot widen this. The scheme has to survive appending a query string cannot widen this. The scheme has to survive
for the reason given below. The host is whatever the request's `Host` for the reason given below. The host is operator configuration rather
header carried — this service validates no hostname, so on a directly than anything a client chooses, it names which deployment the event
exposed deployment a client sets it — and that same header is on the came from, and it is already carried by the allowlisted `Host` header.
allowlist above, so scrubbing the host out of the URL would withhold
nothing that is not sent anyway.
The body, the query string and the URL are all handled on every route The body, the query string and the URL are all handled on every route
rather than filtered by route. For the URL that is also what keeps the rather than filtered by route. For the URL that is also what keeps the

View File

@@ -150,11 +150,10 @@ func sentryRoutePattern(hint *sentry.EventHint) string {
// (interfaces.go:180), byte for byte the predicate // (interfaces.go:180), byte for byte the predicate
// internal/middleware/csrf.go uses, so it is the CSRF TLS decision and // internal/middleware/csrf.go uses, so it is the CSRF TLS decision and
// the reason dropping X-Forwarded-Proto from the header allowlist // the reason dropping X-Forwarded-Proto from the header allowlist
// costs nothing. The host is parsed.Host of the SDK's // costs nothing. The host is kept because it names the deployment the
// scheme://r.Host/path, so it is whatever the client's Host header // event came from and is already carried by the allowlisted Host
// carried: this service validates no hostname. It is kept because that // header; it is operator configuration, not a client-supplied or
// same header is on the allowlist, so scrubbing it here would withhold // capability-bearing value.
// nothing that is not sent anyway.
// //
// Everything else in the URL is discarded rather than edited, so a // Everything else in the URL is discarded rather than edited, so a
// future SDK that starts appending a query string cannot widen this. // future SDK that starts appending a query string cannot widen this.