Merge pull request #279 from anchore/enhance-java-cpe-by-group-id

Include CPEs with elements from POM GroupId fields
This commit is contained in:
Alex Goodman 2020-12-02 07:50:31 -05:00 committed by GitHub
commit 6f9ded60ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 142 additions and 66 deletions

1
go.mod
View File

@ -23,6 +23,7 @@ require (
github.com/olekukonko/tablewriter v0.0.4 github.com/olekukonko/tablewriter v0.0.4
github.com/package-url/packageurl-go v0.1.0 github.com/package-url/packageurl-go v0.1.0
github.com/pelletier/go-toml v1.8.0 github.com/pelletier/go-toml v1.8.0
github.com/scylladb/go-set v1.0.2
github.com/sergi/go-diff v1.1.0 github.com/sergi/go-diff v1.1.0
github.com/sirupsen/logrus v1.6.0 github.com/sirupsen/logrus v1.6.0
github.com/spf13/afero v1.2.2 github.com/spf13/afero v1.2.2

3
go.sum
View File

@ -263,6 +263,7 @@ github.com/facebookincubator/nvdtools v0.1.4/go.mod h1:0/FIVnSEl9YHXLq3tKBPpKaI0
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -726,6 +727,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/scylladb/go-set v1.0.2 h1:SkvlMCKhP0wyyct6j+0IHJkBkSZL+TDzZ4E7f7BCcRE=
github.com/scylladb/go-set v1.0.2/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE= github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE=
github.com/securego/gosec v0.0.0-20200401082031-e946c8c39989 h1:rq2/kILQnPtq5oL4+IAjgVOjh5e2yj2aaCYi7squEvI= github.com/securego/gosec v0.0.0-20200401082031-e946c8c39989 h1:rq2/kILQnPtq5oL4+IAjgVOjh5e2yj2aaCYi7squEvI=

View File

@ -2,6 +2,7 @@ package cataloger
import ( import (
"fmt" "fmt"
"strings"
"github.com/anchore/syft/internal" "github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@ -31,6 +32,7 @@ func generatePackageCPEs(p pkg.Package) []pkg.CPE {
// add a new entry... // add a new entry...
candidateCpe := wfn.NewAttributesWithAny() candidateCpe := wfn.NewAttributesWithAny()
candidateCpe.Part = "a"
candidateCpe.Product = product candidateCpe.Product = product
candidateCpe.Vendor = vendor candidateCpe.Vendor = vendor
candidateCpe.Version = p.Version candidateCpe.Version = p.Version
@ -45,8 +47,6 @@ func generatePackageCPEs(p pkg.Package) []pkg.CPE {
} }
func candidateTargetSoftwareAttrs(p pkg.Package) []string { func candidateTargetSoftwareAttrs(p pkg.Package) []string {
// TODO: expand with package metadata (from type assert)
// TODO: would be great to allow these to be overridden by user data/config // TODO: would be great to allow these to be overridden by user data/config
var targetSw []string var targetSw []string
switch p.Language { switch p.Language {
@ -68,14 +68,43 @@ func candidateTargetSoftwareAttrs(p pkg.Package) []string {
} }
func candidateVendors(p pkg.Package) []string { func candidateVendors(p pkg.Package) []string {
// TODO: expand with package metadata (from type assert) vendors := candidateProducts(p)
vendors := []string{p.Name} switch p.Language {
if p.Language == pkg.Python { case pkg.Python:
vendors = append(vendors, fmt.Sprintf("python-%s", p.Name)) vendors = append(vendors, fmt.Sprintf("python-%s", p.Name))
case pkg.Java:
if p.MetadataType == pkg.JavaMetadataType {
if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok && metadata.PomProperties != nil {
// derive the vendor from the groupID (e.g. org.sonatype.nexus --> sonatype)
if strings.HasPrefix(metadata.PomProperties.GroupID, "org.") || strings.HasPrefix(metadata.PomProperties.GroupID, "com.") {
fields := strings.Split(metadata.PomProperties.GroupID, ".")
if len(fields) >= 3 {
vendors = append(vendors, fields[1])
}
}
}
}
} }
return vendors return vendors
} }
func candidateProducts(p pkg.Package) []string { func candidateProducts(p pkg.Package) []string {
return []string{p.Name} var products = []string{p.Name}
switch p.Language {
case pkg.Java:
if p.MetadataType == pkg.JavaMetadataType {
if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok && metadata.PomProperties != nil {
// derive the product from the groupID (e.g. org.sonatype.nexus --> nexus)
if strings.HasPrefix(metadata.PomProperties.GroupID, "org.") || strings.HasPrefix(metadata.PomProperties.GroupID, "com.") {
fields := strings.Split(metadata.PomProperties.GroupID, ".")
if len(fields) >= 3 {
products = append(products, fields[2])
}
}
}
}
default:
return products
}
return products
} }

View File

@ -1,23 +1,21 @@
package cataloger package cataloger
import ( import (
"sort"
"testing" "testing"
"github.com/scylladb/go-set/strset"
"github.com/scylladb/go-set"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
) )
func must(c pkg.CPE, e error) pkg.CPE {
if e != nil {
panic(e)
}
return c
}
func TestGenerate(t *testing.T) { func TestGenerate(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
p pkg.Package p pkg.Package
expected []pkg.CPE expected []string
}{ }{
{ {
name: "python language", name: "python language",
@ -28,13 +26,13 @@ func TestGenerate(t *testing.T) {
Language: pkg.Python, Language: pkg.Python,
Type: pkg.DebPkg, Type: pkg.DebPkg,
}, },
expected: []pkg.CPE{ expected: []string{
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:python:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:python:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:python:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:python:*:*",
must(pkg.NewCPE("cpe:2.3:*:python-name:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:python-name:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:python-name:name:3.2:*:*:*:*:python:*:*")), "cpe:2.3:a:python-name:name:3.2:*:*:*:*:python:*:*",
}, },
}, },
{ {
@ -46,13 +44,13 @@ func TestGenerate(t *testing.T) {
Language: pkg.JavaScript, Language: pkg.JavaScript,
Type: pkg.DebPkg, Type: pkg.DebPkg,
}, },
expected: []pkg.CPE{ expected: []string{
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:node.js:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:node.js:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:nodejs:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:nodejs:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:node.js:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:node.js:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:nodejs:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:nodejs:*:*",
}, },
}, },
{ {
@ -64,13 +62,13 @@ func TestGenerate(t *testing.T) {
Language: pkg.Ruby, Language: pkg.Ruby,
Type: pkg.DebPkg, Type: pkg.DebPkg,
}, },
expected: []pkg.CPE{ expected: []string{
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:ruby:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:ruby:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:rails:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:rails:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:ruby:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:ruby:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:rails:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:rails:*:*",
}, },
}, },
{ {
@ -82,13 +80,55 @@ func TestGenerate(t *testing.T) {
Language: pkg.Java, Language: pkg.Java,
Type: pkg.DebPkg, Type: pkg.DebPkg,
}, },
expected: []pkg.CPE{ expected: []string{
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:java:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:java:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:maven:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:maven:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:java:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:maven:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:maven:*:*",
},
},
{
name: "java language with groupID",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{
GroupID: "org.sonatype.nexus",
},
},
},
expected: []string{
"cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:*:name:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:*:name:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:name:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:name:name:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:sonatype:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:sonatype:name:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:sonatype:name:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:*:nexus:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:*:nexus:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:*:nexus:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:sonatype:nexus:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:sonatype:nexus:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:sonatype:nexus:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:nexus:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:nexus:name:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:nexus:name:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:name:nexus:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:nexus:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:name:nexus:3.2:*:*:*:*:maven:*:*",
"cpe:2.3:a:nexus:nexus:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:nexus:nexus:3.2:*:*:*:*:java:*:*",
"cpe:2.3:a:nexus:nexus:3.2:*:*:*:*:maven:*:*",
}, },
}, },
{ {
@ -100,17 +140,17 @@ func TestGenerate(t *testing.T) {
Language: pkg.Java, Language: pkg.Java,
Type: pkg.JenkinsPluginPkg, Type: pkg.JenkinsPluginPkg,
}, },
expected: []pkg.CPE{ expected: []string{
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:java:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:java:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:maven:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:maven:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:jenkins:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:jenkins:*:*",
must(pkg.NewCPE("cpe:2.3:*:*:name:3.2:*:*:*:*:cloudbees_jenkins:*:*")), "cpe:2.3:a:*:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:java:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:maven:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:maven:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:jenkins:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:jenkins:*:*",
must(pkg.NewCPE("cpe:2.3:*:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*")), "cpe:2.3:a:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
}, },
}, },
} }
@ -119,19 +159,24 @@ func TestGenerate(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
actual := generatePackageCPEs(test.p) actual := generatePackageCPEs(test.p)
if len(actual) != len(test.expected) { expectedCpeSet := set.NewStringSet(test.expected...)
for _, e := range actual { actualCpeSet := set.NewStringSet()
t.Errorf(" unexpected entry: %+v", e.BindToFmtString()) for _, a := range actual {
} actualCpeSet.Add(a.BindToFmtString())
t.Fatalf("unexpected number of entries: %d", len(actual))
} }
for idx, a := range actual { extra := strset.Difference(expectedCpeSet, actualCpeSet).List()
e := test.expected[idx] sort.Strings(extra)
if a.BindToFmtString() != e.BindToFmtString() { for _, d := range extra {
t.Errorf("mismatched entries @ %d:\n\texpected:%+v\n\t actual:%+v\n", idx, e.BindToFmtString(), a.BindToFmtString()) t.Errorf("extra CPE: %+v", d)
}
} }
missing := strset.Difference(actualCpeSet, expectedCpeSet).List()
sort.Strings(missing)
for _, d := range missing {
t.Errorf("missing CPE: %+v", d)
}
}) })
} }
} }

View File

@ -1,5 +1,3 @@
// +build integration
package integration package integration
import ( import (
@ -21,7 +19,7 @@ func TestRegression212ApkBufferSize(t *testing.T) {
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName) tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
defer cleanup() defer cleanup()
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, source.SquashedScope) _, catalog, _, err := syft.Catalog("docker-archive:"+tarPath, source.SquashedScope)
if err != nil { if err != nil {
t.Fatalf("failed to catalog image: %+v", err) t.Fatalf("failed to catalog image: %+v", err)
} }