fix(middleware): admin JSON endpoints no longer serve unauthenticated callers - #153
Merged
Merged
Conversation
… callers Soft-fail keyed on Accept containing application/json, so `*/*` or an absent header matched neither branch and reached the handler — plain curl against /admin/metrics returned 200 with runtime and article statistics. Soft-fail now requires explicit text/html, and the JSON-only endpoints take hard SessionAuth.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found during the 3.31.0 preflight.
curl http://host/admin/metrics— no session, no credentials — returned 200 with Go version, goroutine count, heap/GC statistics and uptime;/admin/statsadditionally returned article, draft, tag and category counts.SoftSessionAuthchose between the login overlay and a hard 401 by testing whether the request Accept header containedapplication/json. A caller sending*/*, or no Accept header at all — curl's default, and most scripts — matched neither branch, so it took the soft-fail path intended for browsers and reached the handler. For a handler that always answers JSON, that path can only mean returning the data. The comment above the branch already warned that "silent fall-through would leak handler data"; the check was simply keyed to the request's preference rather than to whether the caller could render the overlay at all.Confirmed pre-existing, not a regression: a binary built from the v3.30.1 tag leaks identically once
ADMIN_USERNAME/ADMIN_PASSWORDare set. My first comparison showed 404 and was wrong — the admin group only mounts when those are configured, so the worktree was running without the routes at all.Fixed at two levels. Soft-fail now requires an explicit
text/htmlAccept, so a non-committal caller is refused instead of handed whatever the handler produces; thewantsJSONcheck is retained ahead of it so a client preferring JSON while listing HTML still gets 401, leaving the previously-refusing cases untouched. Separately,/admin/statsand/admin/metricsmove to hardSessionAuth, which has no fall-through path — soft auth exists to render an overlay in place, and a JSON-only handler has none.Scope: no article content, credentials, or session data was reachable, and no write path was exposed. HTML admin routes were never affected — they render the overlay correctly, verified unchanged after the fix.
Three existing tests asserted the vulnerable behaviour by sending no Accept header; they now send what a browser sends, and the
emptyrow in the Accept-variants table flips from fall-through to 401. The new regression test drives a real router with a real JSON handler and asserts the handler never runs — the leak was never in the middleware's own response, it was in what ran after the middleware declined to stop it.