package packageurl import ( "github.com/git-pkgs/purl" ) // distroTypes are PURL types whose namespace names the distribution // ("alpine", "debian"), not part of the package name the handlers store. var distroTypes = map[string]bool{"alpm": true, "apk": true, "bsd": true, "deb": true, "rpm": true} // DistroType reports whether a PURL type's namespace names the distribution, // so that handlers ignore it. func DistroType(purlType string) bool { return distroTypes[purlType] } // HandlerEcosystem returns the ecosystem name the proxy's handlers store // packages of a PURL type under. purl names gem and composer after their // registries, rubygems and packagist; the handlers use the type. func HandlerEcosystem(purlType string) string { switch purlType { case "gem", "composer": return purlType default: return purl.PURLTypeToEcosystem(purlType) } } // HandlerPackage returns the ecosystem and package name the proxy's handlers // store p under, so a package named in a PURL, such as one from an SBOM, // matches the package a client fetched through the proxy: // // - apk, alpm, bsd, deb and rpm: the namespace names the distribution and // is dropped // - conda: the channel, from the namespace or the channel qualifier, is // part of the name ("conda-forge/numpy"); without either the name stays // bare // - pypi and nuget: the name in purl's comparison form, PEP 503 for PyPI // and lowercased for NuGet, as the handlers store them // - swift: namespace/name, the registry scope or the source repository // with the package, which the Swift mirror resolves; purl's full name // is the registry identity scope.name // // Other types use purl's full name, which keeps the namespace: "@babel/core", // or "org.slf4j:slf4j-api" for Maven, as its handler stores it. func HandlerPackage(p *purl.PURL) (ecosystem, name string) { ecosystem = HandlerEcosystem(p.Type) switch { case distroTypes[p.Type]: return ecosystem, p.Name case p.Type == "conda": return ecosystem, condaName(p.Namespace, p.Name, p.Qualifier("channel")) case p.Type == "pypi" || p.Type == "nuget": return ecosystem, p.ComparisonForm().FullName() case p.Type == "swift" && p.Namespace != "": return ecosystem, p.Namespace + "/" + p.Name } return ecosystem, p.FullName() } func condaName(namespace, name, channel string) string { switch { case namespace != "": return namespace + "/" + name case channel != "": return channel + "/" + name default: return name } }