From cbb446dcf0780cd300e0ef147d9ef221236dfaa7 Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Sat, 2 May 2026 12:59:26 +0100 Subject: [PATCH] Use net/url.Parse in extractHost to prevent userinfo-based host confusion --- defaults.go | 22 ++++++---------------- defaults_test.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/defaults.go b/defaults.go index fd452db..adb1c3c 100644 --- a/defaults.go +++ b/defaults.go @@ -1,6 +1,7 @@ package purl import ( + "net/url" "strings" ) @@ -39,22 +40,11 @@ func IsNonDefaultRegistry(purlType, registryURL string) bool { return !IsDefaultRegistry(purlType, registryURL) } -// extractHost extracts the host from a URL string without using net/url.Parse. +// extractHost extracts the hostname from a URL string using net/url.Parse. func extractHost(rawURL string) string { - s := rawURL - // Strip scheme - if i := strings.Index(s, "://"); i >= 0 { - s = s[i+3:] + u, err := url.Parse(rawURL) + if err != nil { + return "" } - // Strip userinfo - if i := strings.Index(s, "@"); i >= 0 { - s = s[i+1:] - } - // Strip path, query, fragment - for _, sep := range []byte{'/', '?', '#'} { - if i := strings.IndexByte(s, sep); i >= 0 { - s = s[:i] - } - } - return s + return u.Hostname() } diff --git a/defaults_test.go b/defaults_test.go index fd2247c..3852a30 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -86,6 +86,24 @@ func TestIsNonDefaultRegistry(t *testing.T) { } } +func TestExtractHostRejectsUserinfo(t *testing.T) { + tests := []struct { + input string + want string + }{ + {"https://evil.com@registry.npmjs.org/path", "registry.npmjs.org"}, + {"https://registry.npmjs.org", "registry.npmjs.org"}, + {"https://user:pass@evil.com/path", "evil.com"}, + {"not-a-url", ""}, + } + for _, tt := range tests { + got := extractHost(tt.input) + if got != tt.want { + t.Errorf("extractHost(%q) = %q, want %q", tt.input, got, tt.want) + } + } +} + func TestIsPrivateRegistry(t *testing.T) { tests := []struct { purl string