From dafd57ca256ee93b38ee4e5ee52e99010c688fc7 Mon Sep 17 00:00:00 2001 From: Deluan Date: Wed, 19 Aug 2026 23:43:51 -0400 Subject: [PATCH] fix(plugins): parse IPv6 hosts correctly in the websocket allowlist isHostAllowed cut the host at the last colon, which mangles an IPv6 literal: "[::1]:8080" became "[::1]" and "[::1]" became "[:". A plugin manifest could therefore never allow an IPv6 host. It now uses net.SplitHostPort, falling back to unwrapping the brackets when there is no port. --- plugins/host_websocket.go | 8 ++++++-- plugins/host_websocket_test.go | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/host_websocket.go b/plugins/host_websocket.go index 90403f4c0..8bedf5dc7 100644 --- a/plugins/host_websocket.go +++ b/plugins/host_websocket.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "maps" + "net" "net/http" "net/url" "strings" @@ -245,8 +246,11 @@ func (s *webSocketServiceImpl) getConnection(connectionID string) (*wsConnection func (s *webSocketServiceImpl) isHostAllowed(host string) bool { // Strip port from host if present hostWithoutPort := host - if idx := strings.LastIndex(host, ":"); idx != -1 { - hostWithoutPort = host[:idx] + if h, _, err := net.SplitHostPort(host); err == nil { + hostWithoutPort = h + } else if h, ok := strings.CutPrefix(host, "["); ok { + // Bracketed IPv6 literal with no port + hostWithoutPort = strings.TrimSuffix(h, "]") } for _, pattern := range s.requiredHosts { diff --git a/plugins/host_websocket_test.go b/plugins/host_websocket_test.go index e41cfbb82..15028acd2 100644 --- a/plugins/host_websocket_test.go +++ b/plugins/host_websocket_test.go @@ -614,3 +614,18 @@ func findWebSocketService(m *Manager, pluginName string) *webSocketServiceImpl { } return nil } + +var _ = Describe("isHostAllowed", func() { + DescribeTable("strips the port before matching", + func(allowed []string, host string, expected bool) { + svc := &webSocketServiceImpl{requiredHosts: allowed} + Expect(svc.isHostAllowed(host)).To(Equal(expected)) + }, + Entry("hostname with port", []string{"example.com"}, "example.com:8080", true), + Entry("hostname without port", []string{"example.com"}, "example.com", true), + Entry("IPv4 with port", []string{"127.0.0.1"}, "127.0.0.1:4533", true), + Entry("IPv6 with port", []string{"::1"}, "[::1]:8080", true), + Entry("IPv6 without port", []string{"::1"}, "[::1]", true), + Entry("IPv6 not in the list", []string{"::2"}, "[::1]:8080", false), + ) +})