Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: snyk enrich external refs #62

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ require (
github.com/edoardottt/depsdev v0.0.3
github.com/google/uuid v1.3.0
github.com/jarcoal/httpmock v1.3.0
github.com/package-url/packageurl-go v0.1.2-0.20230717211154-3587d8c2829e
github.com/package-url/packageurl-go v0.1.2
mcombuechen marked this conversation as resolved.
Show resolved Hide resolved
github.com/remeh/sizedwaitgroup v1.0.0
github.com/rs/zerolog v1.29.1
github.com/spdx/tools-golang v0.5.2
github.com/spdx/tools-golang v0.5.4-0.20240304222056-8baafa1a79c4
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
)

require (
Expand Down
11 changes: 9 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
Expand Down Expand Up @@ -168,8 +169,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/package-url/packageurl-go v0.1.2-0.20230717211154-3587d8c2829e h1:h/TWC+mVfoco4qhPEsaxkdwymlTaDe/BGnzljU8SIPw=
github.com/package-url/packageurl-go v0.1.2-0.20230717211154-3587d8c2829e/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4=
github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand All @@ -188,6 +189,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.2 h1:dtMNjJreWPe37584ajk7m/rQtfJaLpRMk7pUGgvekOg=
github.com/spdx/tools-golang v0.5.2/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
github.com/spdx/tools-golang v0.5.4-0.20240304222056-8baafa1a79c4 h1:h1iNkxAggQH5lpDxHslTTB3Y61XN2G/rjA/n/TAIwFg=
github.com/spdx/tools-golang v0.5.4-0.20240304222056-8baafa1a79c4/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
Expand All @@ -204,6 +207,7 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKk
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand All @@ -214,6 +218,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
Expand Down Expand Up @@ -533,3 +539,4 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
2 changes: 1 addition & 1 deletion lib/scorecard/enrich_spdx.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func enrichSPDX(bom *spdx.Document) {
}

pkg.PackageExternalReferences = append(pkg.PackageExternalReferences, &spdx_2_3.PackageExternalReference{
Category: "OTHER",
Category: spdx.CategoryOther,
RefType: "openssfscorecard",
Locator: scURL,
})
Expand Down
4 changes: 2 additions & 2 deletions lib/scorecard/enrich_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func TestEnrichSBOM_SPDX(t *testing.T) {
{
PackageExternalReferences: []*spdx_2_3.PackageExternalReference{
{
Category: "OTHER",
Category: spdx.CategoryOther,
RefType: "purl",
Locator: "pkg:golang/snyk/parlay",
},
Expand All @@ -191,7 +191,7 @@ func TestEnrichSBOM_SPDX(t *testing.T) {
scRef := pkg.PackageExternalReferences[1]
assert.Equal(t, scorecardURL, scRef.Locator)
assert.Equal(t, "openssfscorecard", scRef.RefType)
assert.Equal(t, "OTHER", scRef.Category)
assert.Equal(t, spdx.CategoryOther, scRef.Category)
}

func setupEcosystemsAPIMock(t *testing.T) func() {
Expand Down
43 changes: 42 additions & 1 deletion lib/snyk/enrich_cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,45 @@ import (
"github.com/snyk/parlay/snyk/issues"
)

type cdxEnricher = func(*cdx.Component, *packageurl.PackageURL)

var cdxEnrichers = []cdxEnricher{
enrichCDXSnykAdvisorData,
enrichCDXSnykVulnerabilityDBData,
}

func enrichCDXSnykVulnerabilityDBData(component *cdx.Component, purl *packageurl.PackageURL) {
url := SnykVulnURL(purl)
if url != "" {
ext := cdx.ExternalReference{
URL: url,
Comment: "Snyk Vulnerability DB",
Type: "Other",
}
if component.ExternalReferences == nil {
component.ExternalReferences = &[]cdx.ExternalReference{ext}
} else {
*component.ExternalReferences = append(*component.ExternalReferences, ext)
}
}
}

func enrichCDXSnykAdvisorData(component *cdx.Component, purl *packageurl.PackageURL) {
url := SnykAdvisorURL(purl)
if url != "" {
ext := cdx.ExternalReference{
URL: url,
Comment: "Snyk Advisor",
Type: "Other",
}
if component.ExternalReferences == nil {
component.ExternalReferences = &[]cdx.ExternalReference{ext}
} else {
*component.ExternalReferences = append(*component.ExternalReferences, ext)
}
}
}

func enrichCycloneDX(bom *cdx.BOM, logger *zerolog.Logger) *cdx.BOM {
auth, err := AuthFromToken(APIToken())
if err != nil {
Expand Down Expand Up @@ -65,7 +104,9 @@ func enrichCycloneDX(bom *cdx.BOM, logger *zerolog.Logger) *cdx.BOM {
Msg("Could not identify package")
return
}

for _, enrichFunc := range cdxEnrichers {
enrichFunc(component, &purl)
}
resp, err := GetPackageVulnerabilities(&purl, auth, orgID)
if err != nil {
l.Err(err).
Expand Down
46 changes: 45 additions & 1 deletion lib/snyk/enrich_spdx.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"net/url"
"sync"

"github.com/package-url/packageurl-go"
"github.com/remeh/sizedwaitgroup"
"github.com/rs/zerolog"
"github.com/spdx/tools-golang/spdx"
Expand All @@ -35,6 +36,47 @@ const (
snykVulnerabilityDB_URI = "https://security.snyk.io"
)

type spdxEnricher = func(*spdx_2_3.Package, *packageurl.PackageURL)

var spdxEnrichers = []spdxEnricher{
enrichSPDXSnykAdvisorData,
enrichSPDXSnykVulnerabilityDBData,
}

func enrichSPDXSnykAdvisorData(component *spdx_2_3.Package, purl *packageurl.PackageURL) {
url := SnykAdvisorURL(purl)
if url != "" {
ext := &spdx_2_3.PackageExternalReference{
Locator: url,
RefType: "advisory",
Category: spdx.CategoryOther,
ExternalRefComment: "Snyk Advisor",
}
if component.PackageExternalReferences == nil {
component.PackageExternalReferences = []*spdx_2_3.PackageExternalReference{ext}
} else {
component.PackageExternalReferences = append(component.PackageExternalReferences, ext)
}
}
}

func enrichSPDXSnykVulnerabilityDBData(component *spdx_2_3.Package, purl *packageurl.PackageURL) {
url := SnykVulnURL(purl)
if url != "" {
ext := &spdx_2_3.PackageExternalReference{
Locator: url,
RefType: "url",
Category: spdx.CategoryOther,
ExternalRefComment: "Snyk Vulnerability DB",
}
if component.PackageExternalReferences == nil {
component.PackageExternalReferences = []*spdx_2_3.PackageExternalReference{ext}
} else {
component.PackageExternalReferences = append(component.PackageExternalReferences, ext)
}
}
}

func enrichSPDX(bom *spdx.Document, logger *zerolog.Logger) *spdx.Document {
auth, err := AuthFromToken(APIToken())
if err != nil {
Expand Down Expand Up @@ -71,7 +113,9 @@ func enrichSPDX(bom *spdx.Document, logger *zerolog.Logger) *spdx.Document {
l.Debug().Msg("Could not identify package")
return
}

for _, enrichFn := range spdxEnrichers {
enrichFn(pkg, purl)
}
resp, err := GetPackageVulnerabilities(purl, auth, orgID)
if err != nil {
l.Err(err).
Expand Down
121 changes: 116 additions & 5 deletions lib/snyk/enrich_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ func TestEnrichSBOM_CycloneDXWithVulnerabilities(t *testing.T) {
},
}
doc := &sbom.SBOMDocument{BOM: bom}
logger := zerolog.Nop()

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

assert.NotNil(t, bom.Vulnerabilities)
Expand All @@ -39,6 +39,74 @@ func TestEnrichSBOM_CycloneDXWithVulnerabilities(t *testing.T) {
assert.Equal(t, "SNYK-PYTHON-NUMPY-73513", vuln.ID)
}

func TestEnrichSBOM_CycloneDXExternalRefs(t *testing.T) {
teardown := setupTestEnv(t)
defer teardown()

bom := &cdx.BOM{
Components: &[]cdx.Component{
{
BOMRef: "pkg:pypi/numpy@1.16.0",
Name: "numpy",
Version: "1.16.0",
PackageURL: "pkg:pypi/numpy@1.16.0",
},
},
}
doc := &sbom.SBOMDocument{BOM: bom}

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

assert.NotNil(t, bom.Components)
refs := (*bom.Components)[0].ExternalReferences
assert.Len(t, *refs, 2)

ref1 := (*refs)[0]
assert.Equal(t, "https://snyk.io/advisor/python/numpy", ref1.URL)
assert.Equal(t, "Snyk Advisor", ref1.Comment)
assert.Equal(t, cdx.ExternalReferenceType("Other"), ref1.Type)

ref2 := (*refs)[1]
assert.Equal(t, "https://security.snyk.io/package/pip/numpy", ref2.URL)
assert.Equal(t, "Snyk Vulnerability DB", ref2.Comment)
assert.Equal(t, cdx.ExternalReferenceType("Other"), ref2.Type)
}

func TestEnrichSBOM_CycloneDXExternalRefs_WithNamespace(t *testing.T) {
teardown := setupTestEnv(t)
defer teardown()

bom := &cdx.BOM{
Components: &[]cdx.Component{
{
BOMRef: "@emotion/react@11.11.3",
Name: "react",
Version: "11.11.3",
PackageURL: "pkg:npm/%40emotion/react@11.11.3",
},
},
}
doc := &sbom.SBOMDocument{BOM: bom}

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

assert.NotNil(t, bom.Components)
refs := (*bom.Components)[0].ExternalReferences
assert.Len(t, *refs, 2)

ref1 := (*refs)[0]
assert.Equal(t, "https://snyk.io/advisor/npm-package/@emotion/react", ref1.URL)
assert.Equal(t, "Snyk Advisor", ref1.Comment)
assert.Equal(t, cdx.ExternalReferenceType("Other"), ref1.Type)

ref2 := (*refs)[1]
assert.Equal(t, "https://security.snyk.io/package/npm/@emotion%2Freact", ref2.URL)
assert.Equal(t, "Snyk Vulnerability DB", ref2.Comment)
assert.Equal(t, cdx.ExternalReferenceType("Other"), ref2.Type)
}

func TestEnrichSBOM_CycloneDXWithVulnerabilities_NestedComponents(t *testing.T) {
teardown := setupTestEnv(t)
defer teardown()
Expand All @@ -62,8 +130,8 @@ func TestEnrichSBOM_CycloneDXWithVulnerabilities_NestedComponents(t *testing.T)
},
}
doc := &sbom.SBOMDocument{BOM: bom}
logger := zerolog.Nop()

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

assert.NotNil(t, bom.Vulnerabilities)
Expand All @@ -85,8 +153,8 @@ func TestEnrichSBOM_CycloneDXWithoutVulnerabilities(t *testing.T) {
},
}
doc := &sbom.SBOMDocument{BOM: bom}
logger := zerolog.Nop()

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

assert.Nil(t, bom.Vulnerabilities, "should not extend vulnerabilities if there are none")
Expand All @@ -113,17 +181,60 @@ func TestEnrichSBOM_SPDXWithVulnerabilities(t *testing.T) {
},
}
doc := &sbom.SBOMDocument{BOM: bom}
logger := zerolog.Nop()

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

vulnRef := bom.Packages[0].PackageExternalReferences[1]
vulnRef := bom.Packages[0].PackageExternalReferences[3]
assert.Equal(t, "SECURITY", vulnRef.Category)
assert.Equal(t, "advisory", vulnRef.RefType)
assert.Equal(t, "https://security.snyk.io/vuln/SNYK-PYTHON-NUMPY-73513", vulnRef.Locator)
assert.Equal(t, "Arbitrary Code Execution", vulnRef.ExternalRefComment)
}

func TestEnrichSBOM_SPDXExternalRefs(t *testing.T) {
teardown := setupTestEnv(t)
defer teardown()

bom := &spdx_2_3.Document{
Packages: []*spdx_2_3.Package{
{
PackageSPDXIdentifier: "pkg:pypi/numpy@1.16.0",
PackageName: "numpy",
PackageVersion: "1.16.0",
PackageExternalReferences: []*spdx_2_3.PackageExternalReference{
{
Category: spdx.CategoryPackageManager,
RefType: "purl",
Locator: "pkg:pypi/numpy@1.16.0",
},
},
},
},
}

doc := &sbom.SBOMDocument{BOM: bom}

logger := zerolog.Nop()
EnrichSBOM(doc, &logger)

assert.NotNil(t, bom.Packages)
refs := (*bom.Packages[0]).PackageExternalReferences
assert.Len(t, refs, 4)

ref1 := refs[1]
assert.Equal(t, "https://snyk.io/advisor/python/numpy", ref1.Locator)
assert.Equal(t, "Snyk Advisor", ref1.ExternalRefComment)
assert.Equal(t, "advisory", ref1.RefType)
assert.Equal(t, spdx.CategoryOther, ref1.Category)

ref2 := refs[2]
assert.Equal(t, "https://security.snyk.io/package/pip/numpy", ref2.Locator)
assert.Equal(t, "Snyk Vulnerability DB", ref2.ExternalRefComment)
assert.Equal(t, "url", ref2.RefType)
assert.Equal(t, spdx.CategoryOther, ref2.Category)
}

func setupTestEnv(t *testing.T) func() {
t.Helper()

Expand Down
Loading
Loading