diff --git a/go.mod b/go.mod index 7d17bfde..a86e9902 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/git-pkgs/gcs v0.1.0 github.com/git-pkgs/integrity v0.2.0 github.com/git-pkgs/magic v0.5.0 - github.com/git-pkgs/purl v0.1.23 + github.com/git-pkgs/purl v0.1.24 github.com/git-pkgs/registries v0.11.0 github.com/git-pkgs/sbom v0.1.7 github.com/git-pkgs/spdx v0.3.3 diff --git a/go.sum b/go.sum index ee376526..a78c08e9 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ github.com/git-pkgs/magic v0.5.0 h1:Pm+4fkHR+K6kg9pwJM4hnwWUuBlkYdLmF1yIAI8lU9Q= github.com/git-pkgs/magic v0.5.0/go.mod h1:SXOqcsNmbmpZjJZHEEWnwxprbsFvpwWgTAZrgXp4Jm4= github.com/git-pkgs/pom v0.1.8 h1:mXCuWw6XkrKKZb6IurRZyIznYLPhV5AYg5ZO38Uj/l8= github.com/git-pkgs/pom v0.1.8/go.mod h1:sQBeYRzjCUn77fcABPn6I/a1aBHFxm55jTmkNxYtGtw= -github.com/git-pkgs/purl v0.1.23 h1:RG7F73S4ZnbAnB/posWf2WS4OhdG0xUbyX8UIkEUhPw= -github.com/git-pkgs/purl v0.1.23/go.mod h1:l8rCfvt5iXVrFIcoQ8+vCLOaHhrtEW2id0LENP+IzMw= +github.com/git-pkgs/purl v0.1.24 h1:JMEB1uNWI+baEpl6AeVx+Kfq1c+xkou+THxa5KJ3MrU= +github.com/git-pkgs/purl v0.1.24/go.mod h1:l8rCfvt5iXVrFIcoQ8+vCLOaHhrtEW2id0LENP+IzMw= github.com/git-pkgs/registries v0.11.0 h1:kkB80NjO9UncyyxGfKmdPXWqUMNE/CkNw5Z/6gqW7Rs= github.com/git-pkgs/registries v0.11.0/go.mod h1:bKxgdy/PwnzQXL2FnOktmSQHTKk6B85MA4VtvvYItPc= github.com/git-pkgs/sbom v0.1.7 h1:4eboy/Yed/SC9ZcC/xT50iywHPzWbmCU7azDb5T1RbA= diff --git a/internal/denylist/denylist.go b/internal/denylist/denylist.go index 77b076aa..15bb9e5e 100644 --- a/internal/denylist/denylist.go +++ b/internal/denylist/denylist.go @@ -3,7 +3,6 @@ package denylist import ( "fmt" - "regexp" "strings" "github.com/git-pkgs/purl" @@ -116,65 +115,28 @@ func (id identity) channelKey() string { return id.key + "?channel=" + id.channel } -var pypiSeparators = regexp.MustCompile(`[-_.]+`) - -// swiftRegistryIdentity returns the registry scope and name of a Swift -// package. A source coordinate such as pkg:swift/github.com/apple/swift-nio -// names no registry identity. -func swiftRegistryIdentity(pkg *purl.PURL) (scope, name string, ok bool) { - switch { - case pkg.Type == "swift" && pkg.Namespace != "" && !strings.ContainsAny(pkg.Namespace, "./"): - return pkg.Namespace, pkg.Name, true - case pkg.Type == "generic" && strings.EqualFold(pkg.Namespace, "swift-registry"): - scope, name, found := strings.Cut(pkg.Name, ".") - return scope, name, found && scope != "" && name != "" +// genericSwiftRegistry returns the scope and name of a Swift registry archive, +// which the proxy stores as pkg:generic/swift-registry/{scope}.{name}. +func genericSwiftRegistry(pkg *purl.PURL) (scope, name string, ok bool) { + if pkg.Type != "generic" || !strings.EqualFold(pkg.Namespace, "swift-registry") { + return "", "", false } - return "", "", false + scope, name, found := strings.Cut(pkg.Name, ".") + return scope, name, found && scope != "" && name != "" } +// identify compares PURLs in purl's comparison form. The generic +// swift-registry spelling is the proxy's own, so it is mapped here first. func identify(pkg *purl.PURL) identity { - namespace, name, version := pkg.Namespace, pkg.Name, pkg.Version + if scope, name, ok := genericSwiftRegistry(pkg); ok { + pkg = purl.New("swift", scope, name, pkg.Version, nil) + } + c := pkg.ComparisonForm() var channel string - switch pkg.Type { - case "pypi": - // PEP 503: case-insensitive, runs of -, _ and . are equivalent. - name = pypiSeparators.ReplaceAllString(strings.ToLower(name), "-") - case "nuget": - // NuGet ids and versions are case-insensitive. - name, version = strings.ToLower(name), strings.ToLower(version) - case "conda": - // The conda handler names packages "channel/name"; entries may give the - // channel as a qualifier or namespace. An entry without one matches - // every channel. - channel = pkg.Qualifier("channel") - if namespace != "" { - channel, namespace = namespace, "" - } - if i := strings.LastIndex(name, "/"); i >= 0 { - channel, name = name[:i], name[i+1:] - } - case "composer": - // Packagist keeps a tag's leading v ("v6.1.7"); Composer treats it - // as the same version. - if len(version) > 1 && (version[0] == 'v' || version[0] == 'V') && version[1] >= '0' && version[1] <= '9' { - version = version[1:] - } - case "oci": - // Handlers name the repository as one name, library%2Falpine; - // entries write it as a path, library/alpine. - if namespace != "" { - name, namespace = namespace+"/"+name, "" - } - case "swift", "generic": - // Swift registry archives are stored as - // pkg:generic/swift-registry/{scope}.{name}; entries may write - // pkg:swift/{scope}/{name}. Registry identities ignore case. - if scope, pkgName, ok := swiftRegistryIdentity(pkg); ok { - namespace, name = strings.ToLower(scope), strings.ToLower(pkgName) - key := purl.New("swift", namespace, name, "", nil).String() - return identity{key: key, version: version} - } + if c.Type == "conda" { + // An entry without a channel matches every channel. + channel = c.Qualifier("channel") } - key := purl.New(pkg.Type, namespace, name, "", nil).String() - return identity{key: key, channel: channel, version: version} + key := purl.New(c.Type, c.Namespace, c.Name, "", nil).String() + return identity{key: key, channel: channel, version: c.Version} } diff --git a/internal/handler/nuget.go b/internal/handler/nuget.go index 3d9f7278..2989298a 100644 --- a/internal/handler/nuget.go +++ b/internal/handler/nuget.go @@ -18,7 +18,8 @@ const ( // Packages are stored under pkg:nuget/{id} with the id lowercased, since // NuGet ids are case-insensitive. purl keeps nuget names as written, so -// package rules are lowercased the same way before the default mapping. +// package rules go through its comparison form, which lowercases them the +// same way, before the default mapping. func init() { retention.Default.Register(retention.Spec{Key: "nuget", CanonicalPackage: nugetRetentionKey}) } @@ -29,7 +30,8 @@ func nugetRetentionKey(p *purl.PURL) (string, bool) { if p == nil { return "", false } - return retention.DefaultCanonical("nuget")(purl.New(p.Type, p.Namespace, strings.ToLower(p.Name), "", nil)) + c := p.ComparisonForm() + return retention.DefaultCanonical("nuget")(purl.New(c.Type, c.Namespace, c.Name, "", nil)) } // NuGetHandler handles NuGet V3 API protocol requests.