// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 package web import ( "encoding/json/v2" "log/slog" "net/http" ) // CrossOrigin refuses a state-changing request that a browser sent from // another origin, using the standard library's Fetch Metadata check: // Sec-Fetch-Site when the browser sends it, and the Origin header against // the Host header otherwise. // // It is the outer gate, and the admin's per-session CSRF token is the // inner one, because the two cover different cases: this refuses a // cross-site request before any handler runs, and the token also refuses // a same-site request (another port on the same host) and a browser that // sends neither header, which this check deliberately allows as a // non-browser client. func CrossOrigin() func(http.Handler) http.Handler { protection := http.NewCrossOriginProtection() protection.SetDenyHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { slog.Warn("web: refused a cross-origin request", "method", r.Method, "path", r.URL.Path, "origin", r.Header.Get("Origin")) w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") w.WriteHeader(http.StatusForbidden) _ = json.MarshalWrite(w, map[string]any{"error": "cross_origin"}, json.Deterministic(true)) })) return protection.Handler }