From a8fd01d4d8841f389cda13e579a05c64ca129d57 Mon Sep 17 00:00:00 2001 From: sagit <36596628+Sagit-chu@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:05:04 +0800 Subject: [PATCH] fix(node): allow IPv6-only addresses (#537) --- .../internal/http/handler/security_utils.go | 7 +++ .../http/handler/security_utils_test.go | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 go-backend/internal/http/handler/security_utils_test.go diff --git a/go-backend/internal/http/handler/security_utils.go b/go-backend/internal/http/handler/security_utils.go index 3516729..71d0b7c 100644 --- a/go-backend/internal/http/handler/security_utils.go +++ b/go-backend/internal/http/handler/security_utils.go @@ -3,6 +3,7 @@ package handler import ( "fmt" "net" + "net/netip" "strings" ) @@ -75,6 +76,12 @@ func IsValidNodeAddress(addr string) error { if strings.ContainsAny(addr, "/?") { return fmt.Errorf("address must not contain path or query parameters") } + // A bare IPv6 literal contains multiple colons, so net.SplitHostPort treats + // it as a malformed host:port pair. Accept IP literals before attempting + // host:port parsing; netip also handles scoped IPv6 addresses. + if _, err := netip.ParseAddr(addr); err == nil { + return nil + } _, _, err := net.SplitHostPort(addr) if err != nil { diff --git a/go-backend/internal/http/handler/security_utils_test.go b/go-backend/internal/http/handler/security_utils_test.go new file mode 100644 index 0000000..543505a --- /dev/null +++ b/go-backend/internal/http/handler/security_utils_test.go @@ -0,0 +1,46 @@ +package handler + +import "testing" + +func TestIssue515IsValidNodeAddressAcceptsBareIPv6(t *testing.T) { + for _, addr := range []string{ + "2001:db8::1", + "::1", + "fe80::1%eth0", + } { + t.Run(addr, func(t *testing.T) { + if err := IsValidNodeAddress(addr); err != nil { + t.Fatalf("expected bare IPv6 address %q to be accepted: %v", addr, err) + } + }) + } +} + +func TestIsValidNodeAddressKeepsExistingAddressForms(t *testing.T) { + for _, addr := range []string{ + "203.0.113.10", + "node.example.com", + "node.example.com:6365", + "[2001:db8::1]:6365", + } { + t.Run(addr, func(t *testing.T) { + if err := IsValidNodeAddress(addr); err != nil { + t.Fatalf("expected node address %q to be accepted: %v", addr, err) + } + }) + } +} + +func TestIsValidNodeAddressRejectsURLComponents(t *testing.T) { + for _, addr := range []string{ + "https://node.example.com", + "node.example.com/path", + "node.example.com?transport=tcp", + } { + t.Run(addr, func(t *testing.T) { + if err := IsValidNodeAddress(addr); err == nil { + t.Fatalf("expected node address %q to be rejected", addr) + } + }) + } +}