fix: reject cross-site launcher setup requests

This commit is contained in:
danmobot
2026-06-23 02:23:06 +01:00
parent 287853ab58
commit f5b2ce7482
+61
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/sipeed/picoclaw/web/backend/middleware"
@@ -89,6 +90,60 @@ func (h *launcherAuthHandlers) isStoreInitialized(ctx context.Context) (bool, er
return h.store.IsInitialized(ctx)
}
func launcherSetupCrossSite(r *http.Request) bool {
fetchSite := strings.ToLower(strings.TrimSpace(r.Header.Get("Sec-Fetch-Site")))
if fetchSite == "cross-site" {
return true
}
if origin := strings.TrimSpace(r.Header.Get("Origin")); origin != "" {
return !sameLauncherRequestOrigin(r, origin)
}
if referer := strings.TrimSpace(r.Header.Get("Referer")); referer != "" {
return !sameLauncherRequestOrigin(r, referer)
}
return false
}
func sameLauncherRequestOrigin(r *http.Request, raw string) bool {
if strings.ContainsAny(raw, " \t\r\n") {
return false
}
u, err := url.Parse(raw)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}
wantScheme := launcherRequestScheme(r)
wantHost := r.Host
if wantHost == "" {
wantHost = r.URL.Host
}
return strings.EqualFold(u.Scheme, wantScheme) && strings.EqualFold(u.Host, wantHost)
}
func launcherRequestScheme(r *http.Request) string {
if proto := strings.TrimSpace(r.Header.Get("X-Forwarded-Proto")); proto != "" {
if i := strings.IndexByte(proto, ','); i >= 0 {
proto = proto[:i]
}
proto = strings.ToLower(strings.TrimSpace(proto))
if proto == "http" || proto == "https" {
return proto
}
}
if r.TLS != nil {
return "https"
}
if r.URL != nil && r.URL.Scheme != "" {
return r.URL.Scheme
}
return "http"
}
func (h *launcherAuthHandlers) handleLogin(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var body launcherAuthLoginBody
@@ -198,6 +253,12 @@ func (h *launcherAuthHandlers) handleStatus(w http.ResponseWriter, r *http.Reque
func (h *launcherAuthHandlers) handleSetup(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if launcherSetupCrossSite(r) {
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte(`{"error":"cross-site setup request rejected"}`))
return
}
if h.store == nil {
w.WriteHeader(http.StatusServiceUnavailable)
if h.storeErr != nil {