fix(14.2): WR-01 access log never records the URL path

An unmatched request logged its path. A token route reached with another
method (405) or with a trailing slash (404) is answered by ServeMux before
any route handler records the pattern, so the share or invitation token in
the path reached the log. The record now carries method, pattern, status
and duration only.
This commit is contained in:
2026-10-12 11:17:51 +02:00
parent 18a4ceabd2
commit fbcfaaf05f
4 changed files with 43 additions and 38 deletions

View File

@@ -38,16 +38,16 @@ func accessLogEnabled(app *backpack.App) bool {
// accessLog logs one Info record "http request" per request after it is
// served, with method, pattern (the matched route, "" when none matched),
// status and duration_ms (whole milliseconds). Only when no route matched
// (a ServeMux 404 or 405, or a CORS preflight answered before routing) is
// path added, set to the URL path without the query string. The query
// string, headers, the remote address and a matched route's raw path are
// never logged: share and invitation tokens travel in paths. A 5xx written
// after the client cancelled the request (its context ends with
// context.Canceled) is logged as 499: the error came from the cancellation,
// and no client received it. The logger is
// the app's published *slog.Logger, else slog.Default (which the generated
// main wraps in the redacting sunscreen handler).
// status and duration_ms (whole milliseconds). The URL path is never
// logged, not even for a request that matched no route: a ServeMux 405 for
// a token route reached with another method, or a 404 for its trailing-slash
// variant, would otherwise carry the token, because share and invitation
// tokens travel in paths. The query string, headers and the remote address
// are never logged either. A 5xx written after the client cancelled the
// request (its context ends with context.Canceled) is logged as 499: the
// error came from the cancellation, and no client received it. The logger
// is the app's published *slog.Logger, else slog.Default (which the
// generated main wraps in the redacting sunscreen handler).
func accessLog(app *backpack.App, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
@@ -67,9 +67,6 @@ func accessLog(app *backpack.App, next http.Handler) http.Handler {
slog.Int("status", status),
slog.Int64("duration_ms", time.Since(start).Milliseconds()),
}
if entry.pattern == "" {
attrs = append(attrs, slog.String("path", req.URL.Path))
}
accessLogger(app).LogAttrs(req.Context(), slog.LevelInfo, accessLogMessage, attrs...)
})
}