From eb089ef4c9416bd3d5642bb5a4437355ffa2fdd0 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Wed, 3 Jun 2026 00:35:02 +0000 Subject: fix: add CSRF and WebSocket origin protection to Claudomator proxy Token-based CSRF is impractical for a reverse proxy whose UI doesn't know Doot's session tokens, so state-changing requests to /claudomator/* are now validated against the configured WebAuthn origin via Origin/Referer header. WebSocket upgrades reject mismatched or missing Origin headers before hijacking the connection. Both checks are no-ops when WebAuthnOrigin is unset (local dev). Co-Authored-By: Claude Sonnet 4.6 --- internal/handlers/claudomator_proxy.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'internal/handlers') diff --git a/internal/handlers/claudomator_proxy.go b/internal/handlers/claudomator_proxy.go index bfbbabc..f3f0cd2 100644 --- a/internal/handlers/claudomator_proxy.go +++ b/internal/handlers/claudomator_proxy.go @@ -13,7 +13,7 @@ import ( // targetURL, stripping the "/claudomator" prefix from the path. WebSocket // upgrade requests are handled via raw TCP hijacking to support long-lived // connections. -func NewClaudomatorProxy(targetURL string) http.Handler { +func NewClaudomatorProxy(targetURL, allowedOrigin string) http.Handler { target, err := url.Parse(targetURL) if err != nil { panic("claudomator: invalid target URL: " + err.Error()) @@ -50,6 +50,10 @@ func NewClaudomatorProxy(targetURL string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") { + if allowedOrigin != "" && r.Header.Get("Origin") != allowedOrigin { + http.Error(w, "forbidden", http.StatusForbidden) + return + } proxyWebSocket(w, r, target) return } -- cgit v1.2.3