diff --git a/registry.go b/registry.go index dd62d91..a47e02e 100644 --- a/registry.go +++ b/registry.go @@ -2,6 +2,7 @@ package purl import ( "errors" + "net/url" "regexp" "strings" "sync" @@ -86,9 +87,9 @@ func expandTemplate(rc *RegistryConfig, namespace, name, version string) (string // Expand template variables result := template - result = strings.ReplaceAll(result, "{namespace}", displayNamespace) - result = strings.ReplaceAll(result, "{name}", name) - result = strings.ReplaceAll(result, "{version}", version) + result = strings.ReplaceAll(result, "{namespace}", url.PathEscape(displayNamespace)) + result = strings.ReplaceAll(result, "{name}", url.PathEscape(name)) + result = strings.ReplaceAll(result, "{version}", url.PathEscape(version)) return result, nil } diff --git a/registry_test.go b/registry_test.go index 5679aa7..f6dd680 100644 --- a/registry_test.go +++ b/registry_test.go @@ -1,6 +1,7 @@ package purl import ( + "strings" "testing" ) @@ -191,6 +192,18 @@ func TestParseRegistryURLWithType(t *testing.T) { } } +func TestRegistryURLEscapesSpecialChars(t *testing.T) { + // A name with path traversal characters should be escaped in the URL + p := New("npm", "", "evil/../../secret", "", nil) + got, err := p.RegistryURL() + if err != nil { + t.Fatalf("RegistryURL() error: %v", err) + } + if strings.Contains(got, "../../") { + t.Errorf("expected path traversal to be escaped in URL, got %s", got) + } +} + func TestParseRegistryURL(t *testing.T) { tests := []struct { url string