diff --git a/internal/commands/snyk/enrich.go b/internal/commands/snyk/enrich.go index 5330ca8..7bc3218 100644 --- a/internal/commands/snyk/enrich.go +++ b/internal/commands/snyk/enrich.go @@ -27,7 +27,7 @@ func NewEnrichCommand(logger zerolog.Logger) *cobra.Command { logger.Fatal().Err(err).Msg("Failed to read SBOM input") } - snyk.EnrichSBOM(doc) + snyk.EnrichSBOM(doc, logger) if err := doc.Encode(os.Stdout); err != nil { logger.Fatal().Err(err).Msg("Failed to encode new SBOM") diff --git a/lib/snyk/enrich.go b/lib/snyk/enrich.go index c91f647..c44ee65 100644 --- a/lib/snyk/enrich.go +++ b/lib/snyk/enrich.go @@ -18,17 +18,18 @@ package snyk import ( cdx "github.com/CycloneDX/cyclonedx-go" + "github.com/rs/zerolog" "github.com/spdx/tools-golang/spdx" "github.com/snyk/parlay/lib/sbom" ) -func EnrichSBOM(doc *sbom.SBOMDocument) *sbom.SBOMDocument { +func EnrichSBOM(doc *sbom.SBOMDocument, logger zerolog.Logger) *sbom.SBOMDocument { switch bom := doc.BOM.(type) { case *cdx.BOM: - enrichCycloneDX(bom) + enrichCycloneDX(bom, logger) case *spdx.Document: - enrichSPDX(bom) + enrichSPDX(bom, logger) } return doc diff --git a/lib/snyk/enrich_cyclonedx.go b/lib/snyk/enrich_cyclonedx.go index 2be3b8a..a5c6f3f 100644 --- a/lib/snyk/enrich_cyclonedx.go +++ b/lib/snyk/enrich_cyclonedx.go @@ -25,11 +25,12 @@ import ( cdx "github.com/CycloneDX/cyclonedx-go" "github.com/package-url/packageurl-go" "github.com/remeh/sizedwaitgroup" + "github.com/rs/zerolog" "github.com/snyk/parlay/snyk/issues" ) -func enrichCycloneDX(bom *cdx.BOM) *cdx.BOM { +func enrichCycloneDX(bom *cdx.BOM, logger zerolog.Logger) *cdx.BOM { if bom.Components == nil { return bom } @@ -37,28 +38,48 @@ func enrichCycloneDX(bom *cdx.BOM) *cdx.BOM { wg := sizedwaitgroup.New(20) var mutex = &sync.Mutex{} vulnerabilities := make(map[cdx.Component][]issues.CommonIssueModelVTwo) + for i, component := range *bom.Components { wg.Add() go func(component cdx.Component, i int) { - // TODO: return when there is no usable Purl on the component. - purl, _ := packageurl.FromString(component.PackageURL) //nolint:errcheck + defer wg.Done() + + purl, err := packageurl.FromString(component.PackageURL) + if err != nil { + logger.Debug(). + Err(err). + Str("BOM-Ref", string(component.BOMRef)). + Msg("Could not identify package.") + return + } + resp, err := GetPackageVulnerabilities(purl) + if err != nil { + logger.Err(err). + Str("purl", purl.ToString()). + Msg("Failed to fetch vulnerabilities for package.") + return + } - if err == nil { - packageData := resp.Body - var packageDoc issues.IssuesWithPurlsResponse - if err := json.Unmarshal(packageData, &packageDoc); err == nil { - if packageDoc.Data != nil { - mutex.Lock() - vulnerabilities[component] = *packageDoc.Data - mutex.Unlock() - } - } + packageData := resp.Body + var packageDoc issues.IssuesWithPurlsResponse + if err := json.Unmarshal(packageData, &packageDoc); err != nil { + logger.Err(err). + Str("status", resp.Status()). + Msg("Failed to decode Snyk vulnerability response.") + return + } + + if packageDoc.Data != nil { + mutex.Lock() + vulnerabilities[component] = *packageDoc.Data + mutex.Unlock() } - wg.Done() }(component, i) } + wg.Wait() + var vulns []cdx.Vulnerability for k, v := range vulnerabilities { for _, issue := range v { diff --git a/lib/snyk/enrich_spdx.go b/lib/snyk/enrich_spdx.go index fabcfeb..799191a 100644 --- a/lib/snyk/enrich_spdx.go +++ b/lib/snyk/enrich_spdx.go @@ -23,6 +23,7 @@ import ( "sync" "github.com/remeh/sizedwaitgroup" + "github.com/rs/zerolog" "github.com/spdx/tools-golang/spdx" spdx_2_3 "github.com/spdx/tools-golang/spdx/v2/v2_3" @@ -34,35 +35,50 @@ const ( snykVulnerabilityDB_URI = "https://security.snyk.io" ) -func enrichSPDX(bom *spdx.Document) *spdx.Document { +func enrichSPDX(bom *spdx.Document, logger zerolog.Logger) *spdx.Document { mutex := &sync.Mutex{} wg := sizedwaitgroup.New(20) vulnerabilities := make(map[*spdx_2_3.Package][]issues.CommonIssueModelVTwo) for i, pkg := range bom.Packages { wg.Add() + go func(pkg *spdx_2_3.Package, i int) { + defer wg.Done() + purl, err := utils.GetPurlFromSPDXPackage(pkg) - if err != nil { + if err != nil || purl == nil { + logger.Debug(). + Str("SPDXID", string(pkg.PackageSPDXIdentifier)). + Msg("Could not identify package.") return } resp, err := GetPackageVulnerabilities(*purl) + if err != nil { + logger.Err(err). + Str("purl", purl.String()). + Msg("Failed to fetch vulnerabilities for package.") + return + } - if err == nil { - packageData := resp.Body - var packageDoc issues.IssuesWithPurlsResponse - if err := json.Unmarshal(packageData, &packageDoc); err == nil { - if packageDoc.Data != nil { - mutex.Lock() - vulnerabilities[pkg] = *packageDoc.Data - mutex.Unlock() - } - } + packageData := resp.Body + var packageDoc issues.IssuesWithPurlsResponse + if err := json.Unmarshal(packageData, &packageDoc); err != nil { + logger.Err(err). + Str("status", resp.Status()). + Msg("Failed to decode Snyk vulnerability response.") + return + } + + if packageDoc.Data != nil { + mutex.Lock() + vulnerabilities[pkg] = *packageDoc.Data + mutex.Unlock() } - wg.Done() }(pkg, i) } + wg.Wait() for pkg, vulns := range vulnerabilities { @@ -80,6 +96,7 @@ func enrichSPDX(bom *spdx.Document) *spdx.Document { url.PathEscape(*issue.Id), ), } + if issue.Attributes.Title != nil { ref.ExternalRefComment = *issue.Attributes.Title } diff --git a/lib/snyk/enrich_test.go b/lib/snyk/enrich_test.go index 27d4b65..11636b1 100644 --- a/lib/snyk/enrich_test.go +++ b/lib/snyk/enrich_test.go @@ -5,6 +5,7 @@ import ( cdx "github.com/CycloneDX/cyclonedx-go" "github.com/jarcoal/httpmock" + "github.com/rs/zerolog" spdx "github.com/spdx/tools-golang/spdx/v2/common" spdx_2_3 "github.com/spdx/tools-golang/spdx/v2/v2_3" "github.com/stretchr/testify/assert" @@ -27,8 +28,9 @@ func TestEnrichSBOM_CycloneDXWithVulnerabilities(t *testing.T) { }, } doc := &sbom.SBOMDocument{BOM: bom} + logger := zerolog.Nop() - EnrichSBOM(doc) + EnrichSBOM(doc, logger) assert.NotNil(t, bom.Vulnerabilities) assert.Len(t, *bom.Vulnerabilities, 1) @@ -52,8 +54,9 @@ func TestEnrichSBOM_CycloneDXWithoutVulnerabilities(t *testing.T) { }, } doc := &sbom.SBOMDocument{BOM: bom} + logger := zerolog.Nop() - EnrichSBOM(doc) + EnrichSBOM(doc, logger) assert.Nil(t, bom.Vulnerabilities, "should not extend vulnerabilities if there are none") } @@ -79,8 +82,9 @@ func TestEnrichSBOM_SPDXWithVulnerabilities(t *testing.T) { }, } doc := &sbom.SBOMDocument{BOM: bom} + logger := zerolog.Nop() - EnrichSBOM(doc) + EnrichSBOM(doc, logger) vulnRef := bom.Packages[0].PackageExternalReferences[1] assert.Equal(t, "SECURITY", vulnRef.Category)