aboutsummaryrefslogtreecommitdiff
path: root/internal/net/web_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-05 18:16:47 -0400
committerhistoria <[not public]>2026-10-05 18:16:47 -0400
commit9d8c43cd68c960b9a6b8f781684f763e204a4498 (patch)
treec9e2a02094a8700db29db2768a0d058ce259a4f4 /internal/net/web_test.go
parent7a15a5d70004b28bf2a710fa0f536af2446e5b17 (diff)
downloadthehouseoficarus-9d8c43cd68c960b9a6b8f781684f763e204a4498.tar.gz
fix: shared YAML encoder and journal directory, text cleanup
Diffstat (limited to 'internal/net/web_test.go')
-rw-r--r--internal/net/web_test.go59
1 files changed, 59 insertions, 0 deletions
diff --git a/internal/net/web_test.go b/internal/net/web_test.go
new file mode 100644
index 0000000..d16b31a
--- /dev/null
+++ b/internal/net/web_test.go
@@ -0,0 +1,59 @@
+package net
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/gorilla/websocket"
+)
+
+// The browser transport previously had no tests. CheckOrigin is the security
+// boundary for the /ws upgrade; test it directly with a non-loopback peer so
+// the loopback bypass does not mask a hostile Origin.
+func TestWebSocketCheckOrigin(t *testing.T) {
+ cases := []struct {
+ name string
+ origin string
+ host string
+ remoteAddr string
+ want bool
+ }{
+ {"no origin", "", "mud.example:4000", "203.0.113.5:1234", true},
+ {"same host", "http://mud.example:4000", "mud.example:4000", "203.0.113.5:1234", true},
+ {"same host https", "https://mud.example:4000", "mud.example:4000", "203.0.113.5:1234", true},
+ {"same hostname no port", "http://mud.example", "mud.example:4000", "203.0.113.5:1234", true},
+ {"hostile origin", "http://evil.example", "mud.example:4000", "203.0.113.5:1234", false},
+ {"loopback bypass", "http://evil.example", "mud.example:4000", "127.0.0.1:1234", true},
+ }
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ req := &http.Request{Header: http.Header{}, Host: tc.host, RemoteAddr: tc.remoteAddr}
+ if tc.origin != "" {
+ req.Header.Set("Origin", tc.origin)
+ }
+ if got := upgrader.CheckOrigin(req); got != tc.want {
+ t.Fatalf("CheckOrigin(%q, host %q, peer %q) = %v, want %v", tc.origin, tc.host, tc.remoteAddr, got, tc.want)
+ }
+ })
+ }
+}
+
+// The /ws upgrade wires a live session and writer. Drive it end-to-end over a
+// real HTTP test server (loopback, so the origin check passes).
+func TestWebSocketUpgradeAccepted(t *testing.T) {
+ s := &Server{hub: NewHub()}
+ done := make(chan struct{})
+ s.handler = func(*Session, string) { <-done }
+ srv := httptest.NewServer(newHTTPMux(s))
+ defer srv.Close()
+ defer close(done)
+
+ wsURL := "ws" + strings.TrimPrefix(srv.URL, "http") + "/ws"
+ conn, resp, err := websocket.DefaultDialer.Dial(wsURL, nil)
+ if err != nil {
+ t.Fatalf("upgrade rejected: %v (resp %v)", err, resp)
+ }
+ conn.Close()
+}