Skip to content

fix(admin): harden localhost auth bypass and lock down admin CORS#33

Merged
zach-snell merged 3 commits into
mainfrom
fix/admin-auth-hardening
Jun 15, 2026
Merged

fix(admin): harden localhost auth bypass and lock down admin CORS#33
zach-snell merged 3 commits into
mainfrom
fix/admin-auth-hardening

Conversation

@zach-snell

Copy link
Copy Markdown
Contributor

Closes two Admin API issues reported by an external researcher against v0.6.5.

Issue 1 — cross-origin localhost Admin API takeover

The localhost auth bypass trusted any request from the loopback socket, but a browser fetch() from any site also originates from loopback, and the admin CORS default echoed *. Fix:

  • The bypass now refuses cross-site / foreign-origin browser requests (via the unforgeable Sec-Fetch-* headers, with an Origin fallback) and validates the Host header against the loopback allowlist (anti-DNS-rebinding).
  • The same gating is applied to the engine-registration endpoints (shared keylessLocalhostTrusted helper).
  • The admin CORS default now allows loopback origins only — never *, never null.
  • CLI/MCP keep keyless localhost access for read-only requests.

Issue 2 — unauthenticated /openapi.json and /insomnia.json

The dashboard-asset auth exemption matched any *.json / *.map path, exposing the OpenAPI/Insomnia exports (full mock config) without the API key. The exemption no longer matches those extensions, consistent with the .yaml twins.

Tests

Adds pkg/admin/security_bypass_cors_test.go covering the denied (cross-site, legacy-browser, DNS-rebinding, header-less write) and allowed (CLI, same-origin, cross-port dashboard) paths, plus predicate unit tests. go build, go vet, and the pkg/admin + pkg/tunnel suites pass.

Close two Admin API issues reported by an external researcher against v0.6.5.

Cross-origin localhost takeover: the localhost auth bypass trusted any loopback request, but a browser fetch() from any site also originates from loopback, and the admin CORS default echoed "*". The bypass now refuses cross-site / foreign-origin browser requests (Sec-Fetch-* with an Origin fallback) and validates the Host header against the loopback allowlist (anti-DNS-rebinding); the same gating is applied to the engine-registration endpoints. The admin CORS default allows loopback origins only.

Unauthenticated exports: the dashboard-asset auth exemption matched any *.json/*.map path, exposing /openapi.json and /insomnia.json. The exemption no longer matches by those extensions.

CLI/MCP keep keyless localhost access for read-only requests. Adds regression tests for the denied and allowed paths.
@codecov-commenter

codecov-commenter commented Jun 15, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 80.00000% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/tunnel/quic/client.go 0.00% 10 Missing ⚠️
pkg/admin/tokens.go 92.15% 2 Missing and 2 partials ⚠️
pkg/admin/engine_handlers.go 75.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

- client.go: drop trailing punctuation/newline from the relay dial error (staticcheck ST1005)

- bump Go build/CI to 1.26.4 (stdlib advisories: net/textproto, crypto/x509, net/mail, html/template, net, net/http)

- bump golang.org/x/net to v0.55.0 (idna + http2 advisories)
@zach-snell zach-snell closed this Jun 15, 2026
@zach-snell zach-snell reopened this Jun 15, 2026
# Conflicts:
#	.github/workflows/benchmark.yaml
#	.github/workflows/ci.yaml
#	Dockerfile
@zach-snell
zach-snell merged commit 470e4cd into main Jun 15, 2026
12 checks passed
@zach-snell
zach-snell deleted the fix/admin-auth-hardening branch June 15, 2026 04:16
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.

2 participants