Skip to content

fix(middleware): admin JSON endpoints no longer serve unauthenticated callers - #153

Merged
vnykmshr merged 1 commit into
mainfrom
fix/admin-json-endpoints-hard-auth
Aug 7, 2026
Merged

fix(middleware): admin JSON endpoints no longer serve unauthenticated callers#153
vnykmshr merged 1 commit into
mainfrom
fix/admin-json-endpoints-hard-auth

Conversation

@vnykmshr

@vnykmshr vnykmshr commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

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/stats additionally returned article, draft, tag and category counts.

SoftSessionAuth chose between the login overlay and a hard 401 by testing whether the request Accept header contained application/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_PASSWORD are 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/html Accept, so a non-committal caller is refused instead of handed whatever the handler produces; the wantsJSON check is retained ahead of it so a client preferring JSON while listing HTML still gets 401, leaving the previously-refusing cases untouched. Separately, /admin/stats and /admin/metrics move to hard SessionAuth, 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 empty row 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.

… 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.
@vnykmshr
vnykmshr merged commit a410df4 into main Aug 7, 2026
8 checks passed
@vnykmshr
vnykmshr deleted the fix/admin-json-endpoints-hard-auth branch August 7, 2026 11:33
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