From 7c11cdc4d135952bb78c8e27c0cdbbcbe3d93449 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 3 Jun 2026 13:43:28 -0400 Subject: [PATCH] fix(server): handleGetStream - auth check before DB lookup TestRoutesRegisteredInMount failed because handleGetStream did the DB lookup (404 on missing track) BEFORE streamAuthOk (401 on unauth). For an unauth request to a non-existent track, the test saw 404 and concluded the route wasn't registered when actually it was - the handler just bailed at the lookup before auth. Reorder: extract trackID via chi.URLParam, run streamAuthOk on the raw path id first (the HMAC token is signed over the same id string so we don't need the resolved row yet), then do the DB lookup. Test now sees 401 on the unauth probe as it expected. Also closes a small info-leak: previously a 404/401 differential let unauth callers probe which track IDs exist. Now both unknown and known IDs return 401 for unauth requests. --- internal/api/media.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/internal/api/media.go b/internal/api/media.go index ef6a3b3c..cca4afec 100644 --- a/internal/api/media.go +++ b/internal/api/media.go @@ -14,6 +14,8 @@ import ( "strconv" "strings" + "github.com/go-chi/chi/v5" + "git.fabledsword.com/bvandeusen/minstrel/internal/apierror" "git.fabledsword.com/bvandeusen/minstrel/internal/auth" "git.fabledsword.com/bvandeusen/minstrel/internal/coverart" @@ -163,15 +165,21 @@ func (h *handlers) streamAuthOk(r *http.Request, trackID string) bool { // permissive middleware the route is wrapped with) OR a signed token // (UPnP slice). streamAuthOk gates both paths. func (h *handlers) handleGetStream(w http.ResponseWriter, r *http.Request) { + // Auth check before the DB lookup: a 404 ahead of the auth check + // would let unauth callers probe which track IDs exist via the + // 404/401 response differential. streamAuthOk is keyed on the + // path's id directly (the HMAC token is signed over the same id + // string, so we don't need the resolved row yet). + rawID := chi.URLParam(r, "id") + if !h.streamAuthOk(r, rawID) { + writeErr(w, apierror.ErrUnauthorized) + return + } track, apiErr := resolveByID(r, "id", dbq.New(h.pool).GetTrackByID, "track") if apiErr != nil { writeErr(w, apiErr) return } - if !h.streamAuthOk(r, uuidToString(track.ID)) { - writeErr(w, apierror.ErrUnauthorized) - return - } f, err := os.Open(track.FilePath) if err != nil { // File vanished (scanner indexed it, filesystem lost it). Treat as