Skip to content

Commit

Permalink
fix: return ecosyste.ms license based on package version
Browse files Browse the repository at this point in the history
  • Loading branch information
paulrosca-snyk committed Nov 27, 2024
1 parent 5e753b3 commit a3816fb
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 27 deletions.
46 changes: 35 additions & 11 deletions lib/ecosystems/enrich_cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import (
"github.com/snyk/parlay/internal/utils"
)

type cdxEnricher = func(*cdx.Component, *packages.Package)
type cdxPackageEnricher = func(*cdx.Component, *packages.Package)
type cdxPackageVersionEnricher = func(*cdx.Component, *packages.Version)

var cdxEnrichers = []cdxEnricher{
var cdxPackageEnrichers = []cdxPackageEnricher{
enrichCDXDescription,
enrichCDXLicense,
enrichCDXHomepage,
enrichCDXRegistryURL,
enrichCDXRepositoryURL,
Expand All @@ -47,16 +47,20 @@ var cdxEnrichers = []cdxEnricher{
enrichCDXSupplier,
}

var cdxPackageVersionEnrichers = []cdxPackageVersionEnricher{
enrichCDXLicense,
}

func enrichCDXDescription(comp *cdx.Component, data *packages.Package) {
if data.Description != nil {
comp.Description = *data.Description
}
}

func enrichCDXLicense(comp *cdx.Component, data *packages.Package) {
if data.NormalizedLicenses != nil {
if len(data.NormalizedLicenses) > 0 {
expression := data.NormalizedLicenses[0]
func enrichCDXLicense(comp *cdx.Component, data *packages.Version) {
if data.Licenses != nil {
if *data.Licenses != "" {
expression := *data.Licenses
licenses := cdx.LicenseChoice{Expression: expression}
comp.Licenses = &cdx.Licenses{licenses}
}
Expand Down Expand Up @@ -207,24 +211,44 @@ func enrichCDX(bom *cdx.BOM, logger *zerolog.Logger) {
return
}

resp, err := GetPackageData(purl)
packageResp, err := GetPackageData(purl)
if err != nil {
l.Debug().
Err(err).
Msg("Skipping package: failed to get package data")
return
}

if resp.JSON200 == nil {
if packageResp.JSON200 == nil {
l.Debug().
Err(err).
Msg("Skipping package: no data on ecosyste.ms response")
return
}

for _, enrichFunc := range cdxEnrichers {
enrichFunc(comp, resp.JSON200)
for _, enrichFunc := range cdxPackageEnrichers {
enrichFunc(comp, packageResp.JSON200)
}

packageVersionResp, err := GetPackageVersionData(purl)
if err != nil {
l.Debug().
Err(err).
Msg("Skipping package version enrichment: failed to get package version data")
return
}

if packageVersionResp.JSON200 == nil {
l.Debug().
Err(err).
Msg("Skipping package version enrichment: no data on ecosyste.ms response")
return
}

for _, enrichFunc := range cdxPackageVersionEnrichers {
enrichFunc(comp, packageVersionResp.JSON200)
}

}(comps[i])
}

Expand Down
20 changes: 15 additions & 5 deletions lib/ecosystems/enrich_cyclonedx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,20 @@ func TestEnrichSBOM_CycloneDX(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

httpmock.RegisterResponder("GET", `=~^https://packages.ecosyste.ms/api/v1/registries/.*/packages/.*/versions`,
func(r *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, map[string]interface{}{
// This is the license we expect to see for the specific package version
"licenses": "MIT",
})
},
)
httpmock.RegisterResponder("GET", `=~^https://packages.ecosyste.ms/api/v1/registries`,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, map[string]interface{}{
"description": "description",
"normalized_licenses": []string{
// This license should be ignored as it corresponds to the latest version of the package
"BSD-3-Clause",
},
})
Expand Down Expand Up @@ -73,7 +82,7 @@ func TestEnrichSBOM_CycloneDX(t *testing.T) {
component := components[0]
licenses := *component.Licenses

comp := cdx.LicenseChoice(cdx.LicenseChoice{Expression: "BSD-3-Clause"})
comp := cdx.LicenseChoice(cdx.LicenseChoice{Expression: "MIT"})

assert.Equal(t, "description", components[0].Description)
assert.Equal(t, comp, licenses[0])
Expand Down Expand Up @@ -119,7 +128,7 @@ func TestEnrichSBOM_CycloneDX_NestedComps(t *testing.T) {

httpmock.GetTotalCallCount()
calls := httpmock.GetCallCountInfo()
assert.Equal(t, 2, calls[`GET =~^https://packages.ecosyste.ms/api/v1/registries`])
assert.Equal(t, 4, calls[`GET =~^https://packages.ecosyste.ms/api/v1/registries`])
}

func TestEnrichSBOMWithoutLicense(t *testing.T) {
Expand Down Expand Up @@ -156,7 +165,7 @@ func TestEnrichSBOMWithoutLicense(t *testing.T) {

httpmock.GetTotalCallCount()
calls := httpmock.GetCallCountInfo()
assert.Equal(t, len(components), calls[`GET =~^https://packages.ecosyste.ms/api/v1/registries`])
assert.Equal(t, 2*len(components), calls[`GET =~^https://packages.ecosyste.ms/api/v1/registries`])
}

func TestEnrichDescription(t *testing.T) {
Expand All @@ -181,8 +190,9 @@ func TestEnrichLicense(t *testing.T) {
Name: "cyclonedx-go",
Version: "v0.3.0",
}
pack := &packages.Package{
NormalizedLicenses: []string{"BSD-3-Clause"},
lic := "BSD-3-Clause"
pack := &packages.Version{
Licenses: &lic,
}

enrichCDXLicense(component, pack)
Expand Down
27 changes: 17 additions & 10 deletions lib/ecosystems/enrich_spdx.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ package ecosystems

import (
"errors"
"fmt"
"strings"

"github.com/package-url/packageurl-go"
"github.com/rs/zerolog"
Expand All @@ -41,20 +39,31 @@ func enrichSPDX(bom *spdx.Document, logger *zerolog.Logger) {
continue
}

resp, err := GetPackageData(*purl)
packageResp, err := GetPackageData(*purl)
if err != nil {
continue
}

pkgData := resp.JSON200
pkgData := packageResp.JSON200
if pkgData == nil {
continue
}

enrichSPDXDescription(pkg, pkgData)
enrichSPDXLicense(pkg, pkgData)
enrichSPDXHomepage(pkg, pkgData)
enrichSPDXSupplier(pkg, pkgData)

packageVersionResp, err := GetPackageVersionData(*purl)
if err != nil {
continue
}

pkgVersionData := packageVersionResp.JSON200
if pkgData == nil {
continue
}

enrichSPDXLicense(pkg, pkgVersionData)
}
}

Expand Down Expand Up @@ -86,11 +95,9 @@ func enrichSPDXSupplier(pkg *v2_3.Package, data *packages.Package) {
}
}

func enrichSPDXLicense(pkg *v2_3.Package, data *packages.Package) {
if len(data.NormalizedLicenses) == 1 {
pkg.PackageLicenseConcluded = data.NormalizedLicenses[0]
} else if len(data.NormalizedLicenses) > 1 {
pkg.PackageLicenseConcluded = fmt.Sprintf("(%s)", strings.Join(data.NormalizedLicenses, " OR "))
func enrichSPDXLicense(pkg *v2_3.Package, data *packages.Version) {
if data.Licenses != nil && *data.Licenses != "" {
pkg.PackageLicenseConcluded = *data.Licenses
}
}

Expand Down
11 changes: 10 additions & 1 deletion lib/ecosystems/enrich_spdx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,20 @@ func TestEnrichSBOM_SPDX(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

httpmock.RegisterResponder("GET", `=~^https://packages.ecosyste.ms/api/v1/registries/.*/packages/.*/versions`,
func(r *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, map[string]interface{}{
// This is the license we expect to see for the specific package version
"licenses": "MIT",
})
},
)
httpmock.RegisterResponder("GET", `=~^https://packages.ecosyste.ms/api/v1/registries`,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, map[string]interface{}{
"description": "description",
"normalized_licenses": []string{
// This license should be ignored as it corresponds to the latest version of the package
"BSD-3-Clause",
},
"homepage": "https://github.com/spdx/tools-golang",
Expand Down Expand Up @@ -73,7 +82,7 @@ func TestEnrichSBOM_SPDX(t *testing.T) {
pkgs := bom.Packages

assert.Equal(t, "description", pkgs[0].PackageDescription)
assert.Equal(t, "BSD-3-Clause", pkgs[0].PackageLicenseConcluded)
assert.Equal(t, "MIT", pkgs[0].PackageLicenseConcluded)
assert.Equal(t, "https://github.com/spdx/tools-golang", pkgs[0].PackageHomePage)
assert.Equal(t, "Organization", pkgs[0].PackageSupplier.SupplierType)
assert.Equal(t, "Acme Corp", pkgs[0].PackageSupplier.Supplier)
Expand Down
20 changes: 20 additions & 0 deletions lib/ecosystems/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ func GetPackageData(purl packageurl.PackageURL) (*packages.GetRegistryPackageRes
return resp, nil
}

func GetPackageVersionData(purl packageurl.PackageURL) (*packages.GetRegistryPackageVersionResponse, error) {
client, err := packages.NewClientWithResponses(server)
if err != nil {
return nil, err
}

// Ecosyste.ms has a purl based API, but unfortunately slower
// so we break the purl down to registry and name values locally
// params := packages.LookupPackageParams{Purl: &p}
// resp, err := client.LookupPackageWithResponse(context.Background(), &params)
name := purlToEcosystemsName(purl)
registry := purlToEcosystemsRegistry(purl)
resp, err := client.GetRegistryPackageVersionWithResponse(context.Background(), registry, name, purl.Version)

if err != nil {
return nil, err
}
return resp, nil
}

func purlToEcosystemsRegistry(purl packageurl.PackageURL) string {
return map[string]string{
packageurl.TypeApk: "alpine-edge",
Expand Down

0 comments on commit a3816fb

Please sign in to comment.