diff --git a/go/purl/ecosystems_simple.go b/go/purl/ecosystems_simple.go index f9fb845851a..c2258a9f4b2 100644 --- a/go/purl/ecosystems_simple.go +++ b/go/purl/ecosystems_simple.go @@ -1,6 +1,8 @@ package purl import ( + "strings" + "github.com/ossf/osv-schema/bindings/go/osvconstants" "github.com/package-url/packageurl-go" ) @@ -16,6 +18,24 @@ func (g simpleGenerator) generate(_, packageName string) (packageurl.PackageURL, return *packageurl.NewPackageURL(g.purlType, g.namespace, packageName, "", g.qualifiers, ""), nil } +// slashGenerator handles PURL mappings where the package name contains a slash-separated namespace. +type slashGenerator struct { + purlType string + qualifiers packageurl.Qualifiers +} + +func (g slashGenerator) generate(_, packageName string) (packageurl.PackageURL, error) { + namespace := "" + name := packageName + if strings.Contains(packageName, "/") { + parts := strings.Split(packageName, "/") + name = parts[len(parts)-1] + namespace = strings.Join(parts[:len(parts)-1], "/") + } + + return *packageurl.NewPackageURL(g.purlType, namespace, name, "", g.qualifiers, ""), nil +} + // simpleParser handles standard PURL mappings without special logic. type simpleParser struct { ecosystem osvconstants.Ecosystem @@ -36,6 +56,11 @@ func registerSimple(ecosystem osvconstants.Ecosystem, purlType string, namespace registerParser(purlType, namespace, simpleParser{ecosystem: ecosystem}) } +func registerSlash(ecosystem osvconstants.Ecosystem, purlType string) { + registerGenerator(ecosystem, slashGenerator{purlType: purlType}) + registerParser(purlType, "", simpleParser{ecosystem: ecosystem, joinNamespace: true}) +} + //nolint:gochecknoinits // init is used here to register simple ecosystems with the global PURL registry. func init() { // Language Ecosystems (No namespace) @@ -44,18 +69,17 @@ func init() { registerSimple(osvconstants.EcosystemConanCenter, "conan", "", nil) registerSimple(osvconstants.EcosystemDockerHardenedImages, "dhi", "", nil) registerSimple(osvconstants.EcosystemHackage, "hackage", "", nil) - registerSimple(osvconstants.EcosystemHex, "hex", "", nil) + registerSlash(osvconstants.EcosystemHex, "hex") registerSimple(osvconstants.EcosystemJulia, "julia", "", nil) registerSimple(osvconstants.EcosystemNuGet, "nuget", "", nil) registerSimple(osvconstants.EcosystemOSSFuzz, "generic", "", nil) - registerSimple(osvconstants.EcosystemPackagist, "composer", "", nil) + registerSlash(osvconstants.EcosystemPackagist, "composer") registerSimple(osvconstants.EcosystemPub, "pub", "", nil) registerSimple(osvconstants.EcosystemPyPI, "pypi", "", nil) registerSimple(osvconstants.EcosystemRubyGems, "gem", "", nil) - registerSimple(osvconstants.EcosystemSwiftURL, "swift", "", nil) + registerSlash(osvconstants.EcosystemSwiftURL, "swift") registerSimple(osvconstants.EcosystemCratesIO, "cargo", "", nil) - registerGenerator(osvconstants.EcosystemNPM, simpleGenerator{purlType: "npm"}) - registerParser("npm", "", simpleParser{ecosystem: osvconstants.EcosystemNPM, joinNamespace: true}) + registerSlash(osvconstants.EcosystemNPM, "npm") registerSimple(osvconstants.EcosystemOpam, "opam", "", nil) // OS Ecosystems (With static namespace) diff --git a/go/purl/purl_test.go b/go/purl/purl_test.go index 7dece1fe472..f7f5d248a91 100644 --- a/go/purl/purl_test.go +++ b/go/purl/purl_test.go @@ -21,6 +21,10 @@ func TestGenerate(t *testing.T) { {"Go", "github.com/gorilla/mux", "pkg:golang/github.com/gorilla/mux", false}, {"Go", "stdlib", "pkg:golang/stdlib", false}, {"Maven", "org.apache.commons:commons-lang3", "pkg:maven/org.apache.commons/commons-lang3", false}, + {"Packagist", "drupal/colorbox", "pkg:composer/drupal/colorbox", false}, + {"npm", "@babel/core", "pkg:npm/%40babel/core", false}, + {"Hex", "acme/foo", "pkg:hex/acme/foo", false}, + {"SwiftURL", "github.com/apple/swift-markdown", "pkg:swift/github.com/apple/swift-markdown", false}, // Error cases {"UnknownEcosystem", "package", "", true}, } @@ -58,6 +62,10 @@ func TestParse(t *testing.T) { {"pkg:golang/stdlib@1.18", "Go", "stdlib", "1.18", false}, {"pkg:maven/org.apache.commons/commons-lang3@3.12.0", "Maven", "org.apache.commons:commons-lang3", "3.12.0", false}, {"pkg:gradle/org.apache.commons/commons-lang3@3.12.0", "Maven", "org.apache.commons:commons-lang3", "3.12.0", false}, // alias + {"pkg:composer/drupal/colorbox@1.2.3", "Packagist", "drupal/colorbox", "1.2.3", false}, + {"pkg:npm/%40babel/core@1.2.3", "npm", "@babel/core", "1.2.3", false}, + {"pkg:hex/acme/foo@1.2.3", "Hex", "acme/foo", "1.2.3", false}, + {"pkg:swift/github.com/apple/swift-markdown@1.2.3", "SwiftURL", "github.com/apple/swift-markdown", "1.2.3", false}, // Error cases {"invalid-purl", "", "", "", true}, {"pkg:unknown/package@1.0.0", "", "", "", true},