mirror of
https://github.com/anchore/syft.git
synced 2026-11-02 07:26:34 +01:00
feat: Record where CPEs come from (#2552)
Syft can get CPEs from several source, including generating them based on package data, finding them in the NVD CPE dictionary, or finding them declared in a manifest or existing SBOM. Record where Syft got CPEs so that consumers of SBOMs can reason about how trustworthy they are. Signed-off-by: Will Murphy <will.murphy@anchore.com>
This commit is contained in:
@@ -1282,7 +1282,7 @@ func TestCatalogerConfig_MarshalJSON(t *testing.T) {
|
||||
Qualifiers: nil,
|
||||
Subpath: "subpath",
|
||||
},
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*")},
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource)},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -56,7 +56,7 @@ func (cfg Classifier) MarshalJSON() ([]byte, error) {
|
||||
|
||||
var marshalledCPEs []string
|
||||
for _, c := range cfg.CPEs {
|
||||
marshalledCPEs = append(marshalledCPEs, c.BindToFmtString())
|
||||
marshalledCPEs = append(marshalledCPEs, c.Attributes.BindToFmtString())
|
||||
}
|
||||
|
||||
m := marshalled{
|
||||
@@ -225,10 +225,11 @@ func getContents(resolver file.Resolver, location file.Location) ([]byte, error)
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
// singleCPE returns a []pkg.CPE based on the cpe string or panics if the CPE is invalid
|
||||
// singleCPE returns a []cpe.CPE with Source: Generated based on the cpe string or panics if the
|
||||
// cpe string cannot be parsed into valid CPE Attributes
|
||||
func singleCPE(cpeString string) []cpe.CPE {
|
||||
return []cpe.CPE{
|
||||
cpe.Must(cpeString),
|
||||
cpe.Must(cpeString, cpe.GeneratedSource),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,14 +30,14 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
cpes: nil,
|
||||
},
|
||||
{
|
||||
name: "one CPE",
|
||||
name: "one Attributes",
|
||||
fixture: "test-fixtures/version.txt",
|
||||
classifier: Classifier{
|
||||
Package: "some-app",
|
||||
FileGlob: "**/version.txt",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
cpes: []string{
|
||||
@@ -52,8 +52,8 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
FileGlob: "**/version.txt",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:some:apps:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:some:apps:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
cpes: []string{
|
||||
@@ -79,7 +79,7 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
|
||||
var cpes []string
|
||||
for _, c := range p.CPEs {
|
||||
cpes = append(cpes, c.String())
|
||||
cpes = append(cpes, c.Attributes.String())
|
||||
}
|
||||
require.Equal(t, test.cpes, cpes)
|
||||
})
|
||||
@@ -109,7 +109,7 @@ func TestClassifier_MarshalJSON(t *testing.T) {
|
||||
Qualifiers: nil,
|
||||
Subpath: "subpath",
|
||||
},
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*")},
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource)},
|
||||
},
|
||||
want: `{"class":"class","fileGlob":"glob","package":"pkg","purl":"pkg:type/namespace/name@version#subpath","cpes":["cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"]}`,
|
||||
},
|
||||
|
||||
@@ -23,8 +23,8 @@ func DefaultClassifiers() []Classifier {
|
||||
Package: "python",
|
||||
PURL: mustPURL("pkg:generic/python@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -34,8 +34,8 @@ func DefaultClassifiers() []Classifier {
|
||||
Package: "python",
|
||||
PURL: mustPURL("pkg:generic/python@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -93,7 +93,7 @@ func DefaultClassifiers() []Classifier {
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/java@version"),
|
||||
// TODO the updates might need to be part of the CPE, like: 1.8.0:update152
|
||||
// TODO the updates might need to be part of the CPE Attributes, like: 1.8.0:update152
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
@@ -255,8 +255,8 @@ func DefaultClassifiers() []Classifier {
|
||||
Package: "percona-server",
|
||||
PURL: mustPURL("pkg:generic/percona-server@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -267,9 +267,9 @@ func DefaultClassifiers() []Classifier {
|
||||
Package: "percona-xtradb-cluster",
|
||||
PURL: mustPURL("pkg:generic/percona-xtradb-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -363,8 +363,8 @@ func DefaultClassifiers() []Classifier {
|
||||
Package: "nginx",
|
||||
PURL: mustPURL("pkg:generic/nginx@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*"),
|
||||
cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,8 +21,8 @@ func newPackage(classifier Classifier, location file.Location, matchMetadata map
|
||||
|
||||
var cpes []cpe.CPE
|
||||
for _, c := range classifier.CPEs {
|
||||
c.Version = version
|
||||
c.Update = update
|
||||
c.Attributes.Version = version
|
||||
c.Attributes.Update = update
|
||||
cpes = append(cpes, c)
|
||||
}
|
||||
|
||||
|
||||
@@ -127,5 +127,5 @@ func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) {
|
||||
cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate)
|
||||
}
|
||||
|
||||
return cpe.New(cpeString)
|
||||
return cpe.New(cpeString, cpe.GeneratedSource)
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func Test_Binary_Cataloger_Stdlib_Cpe(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := generateStdlibCpe(tc.candidate)
|
||||
assert.NoError(t, err, "expected no err; got %v", err)
|
||||
assert.Equal(t, got.String(), tc.want)
|
||||
assert.Equal(t, got.Attributes.String(), tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ type upstreamCandidate struct {
|
||||
}
|
||||
|
||||
func upstreamCandidates(m pkg.ApkDBEntry) (candidates []upstreamCandidate) {
|
||||
// Do not consider OriginPackage variations when generating CPE candidates for the child package
|
||||
// Do not consider OriginPackage variations when generating CPE Attributes candidates for the child package
|
||||
// because doing so will result in false positives when matching to vulnerabilities in Grype since
|
||||
// it won't know to lookup apk fix entries using the OriginPackage name.
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func filterCpeList(cpeList CpeList) CpeList {
|
||||
return processedCpeList
|
||||
}
|
||||
|
||||
// normalizeCPE removes the version and update parts of a CPE.
|
||||
// normalizeCPE removes the version and update parts of CPE Attributes.
|
||||
func normalizeCPE(cpe *wfn.Attributes) *wfn.Attributes {
|
||||
cpeCopy := *cpe
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
const jenkinsName = "jenkins"
|
||||
|
||||
// filterFn instances should return true if the given CPE should be removed from a collection for the given package
|
||||
type filterFn func(cpe cpe.CPE, p pkg.Package) bool
|
||||
// filterFn instances should return true if the given CPE attributes should be removed from a collection for the given package
|
||||
type filterFn func(cpe cpe.Attributes, p pkg.Package) bool
|
||||
|
||||
var cpeFilters = []filterFn{
|
||||
disallowJiraClientServerMismatch,
|
||||
@@ -19,7 +19,7 @@ var cpeFilters = []filterFn{
|
||||
disallowNonParseableCPEs,
|
||||
}
|
||||
|
||||
func filter(cpes []cpe.CPE, p pkg.Package, filters ...filterFn) (result []cpe.CPE) {
|
||||
func filter(cpes []cpe.Attributes, p pkg.Package, filters ...filterFn) (result []cpe.Attributes) {
|
||||
cpeLoop:
|
||||
for _, c := range cpes {
|
||||
for _, fn := range filters {
|
||||
@@ -33,9 +33,9 @@ cpeLoop:
|
||||
return result
|
||||
}
|
||||
|
||||
func disallowNonParseableCPEs(c cpe.CPE, _ pkg.Package) bool {
|
||||
func disallowNonParseableCPEs(c cpe.Attributes, _ pkg.Package) bool {
|
||||
v := c.String()
|
||||
_, err := cpe.New(v)
|
||||
_, err := cpe.NewAttributes(v)
|
||||
|
||||
cannotParse := err != nil
|
||||
|
||||
@@ -43,15 +43,15 @@ func disallowNonParseableCPEs(c cpe.CPE, _ pkg.Package) bool {
|
||||
}
|
||||
|
||||
// jenkins plugins should not match against jenkins
|
||||
func disallowJenkinsServerCPEForPluginPackage(c cpe.CPE, p pkg.Package) bool {
|
||||
func disallowJenkinsServerCPEForPluginPackage(c cpe.Attributes, p pkg.Package) bool {
|
||||
if p.Type == pkg.JenkinsPluginPkg && c.Product == jenkinsName {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// filter to account that packages that are not for jenkins but have a CPE generated that will match against jenkins
|
||||
func disallowJenkinsCPEsNotAssociatedWithJenkins(c cpe.CPE, p pkg.Package) bool {
|
||||
// filter to account that packages that are not for jenkins but have a Attributes generated that will match against jenkins
|
||||
func disallowJenkinsCPEsNotAssociatedWithJenkins(c cpe.Attributes, p pkg.Package) bool {
|
||||
// jenkins server should only match against a product with the name jenkins
|
||||
if c.Product == jenkinsName && !strings.Contains(strings.ToLower(p.Name), jenkinsName) {
|
||||
if c.Vendor == cpe.Any || c.Vendor == jenkinsName || c.Vendor == "cloudbees" {
|
||||
@@ -61,8 +61,8 @@ func disallowJenkinsCPEsNotAssociatedWithJenkins(c cpe.CPE, p pkg.Package) bool
|
||||
return false
|
||||
}
|
||||
|
||||
// filter to account for packages which are jira client packages but have a CPE that will match against jira
|
||||
func disallowJiraClientServerMismatch(c cpe.CPE, p pkg.Package) bool {
|
||||
// filter to account for packages which are jira client packages but have a Attributes that will match against jira
|
||||
func disallowJiraClientServerMismatch(c cpe.Attributes, p pkg.Package) bool {
|
||||
// jira / atlassian should not apply to clients
|
||||
if c.Product == "jira" && strings.Contains(strings.ToLower(p.Name), "client") {
|
||||
if c.Vendor == cpe.Any || c.Vendor == "jira" || c.Vendor == "atlassian" {
|
||||
|
||||
@@ -18,7 +18,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "go case (filter out)",
|
||||
cpe: cpe.Must("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
},
|
||||
@@ -26,7 +26,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ignore jenkins plugins with unique name",
|
||||
cpe: cpe.Must("cpe:2.3:a:name:ci-jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:name:ci-jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
},
|
||||
@@ -34,7 +34,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ignore java packages",
|
||||
cpe: cpe.Must("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Type: pkg.JavaPkg,
|
||||
},
|
||||
@@ -43,7 +43,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, disallowJenkinsServerCPEForPluginPackage(test.cpe, test.pkg))
|
||||
assert.Equal(t, test.expected, disallowJenkinsServerCPEForPluginPackage(test.cpe.Attributes, test.pkg))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "filter out mismatched name (cloudbees vendor)",
|
||||
cpe: cpe.Must("cpe:2.3:a:cloudbees:jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:cloudbees:jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "not-j*nkins",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -66,7 +66,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "filter out mismatched name (jenkins vendor)",
|
||||
cpe: cpe.Must("cpe:2.3:a:jenkins:jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:jenkins:jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "not-j*nkins",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -75,7 +75,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "filter out mismatched name (any vendor)",
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "not-j*nkins",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -84,7 +84,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ignore packages with the name jenkins",
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "jenkins-thing",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -93,7 +93,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ignore product names that are not exactly 'jenkins'",
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jenkins-something-else:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jenkins-something-else:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "not-j*nkins",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -103,7 +103,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, disallowJenkinsCPEsNotAssociatedWithJenkins(test.cpe, test.pkg))
|
||||
assert.Equal(t, test.expected, disallowJenkinsCPEsNotAssociatedWithJenkins(test.cpe.Attributes, test.pkg))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "filter out mismatched name (atlassian vendor)",
|
||||
cpe: cpe.Must("cpe:2.3:a:atlassian:jira:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:atlassian:jira:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "something-client",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -126,7 +126,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "filter out mismatched name (jira vendor)",
|
||||
cpe: cpe.Must("cpe:2.3:a:jira:jira:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:jira:jira:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "something-client",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -135,7 +135,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "filter out mismatched name (any vendor)",
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "something-client",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -144,7 +144,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ignore package names that do not have 'client'",
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "jira-thing",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -153,7 +153,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ignore product names that are not exactly 'jira'",
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jira-something-else:3.2:*:*:*:*:*:*:*"),
|
||||
cpe: cpe.Must("cpe:2.3:a:*:jira-something-else:3.2:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
pkg: pkg.Package{
|
||||
Name: "not-j*ra",
|
||||
Type: pkg.JavaPkg,
|
||||
@@ -163,7 +163,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, disallowJiraClientServerMismatch(test.cpe, test.pkg))
|
||||
assert.Equal(t, test.expected, disallowJiraClientServerMismatch(test.cpe.Attributes, test.pkg))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
// the CPE database, so they will be preferred over other candidates:
|
||||
var knownVendors = strset.New("apache")
|
||||
|
||||
func newCPE(product, vendor, version, targetSW string) *cpe.CPE {
|
||||
func newCPE(product, vendor, version, targetSW string) *cpe.Attributes {
|
||||
c := cpe.NewWithAny()
|
||||
c.Part = "a"
|
||||
c.Product = product
|
||||
@@ -61,7 +61,7 @@ func GetIndexedDictionary() (_ *dictionary.Indexed, err error) {
|
||||
func FromDictionaryFind(p pkg.Package) (cpe.CPE, bool) {
|
||||
dict, err := GetIndexedDictionary()
|
||||
if err != nil {
|
||||
log.Debugf("dictionary CPE lookup not available: %+v", err)
|
||||
log.Debugf("CPE dictionary lookup not available: %+v", err)
|
||||
return cpe.CPE{}, false
|
||||
}
|
||||
|
||||
@@ -96,12 +96,12 @@ func FromDictionaryFind(p pkg.Package) (cpe.CPE, bool) {
|
||||
return cpe.CPE{}, false
|
||||
}
|
||||
|
||||
parsedCPE, err := cpe.New(cpeString)
|
||||
parsedCPE, err := cpe.New(cpeString, cpe.NVDDictionaryLookupSource)
|
||||
if err != nil {
|
||||
return cpe.CPE{}, false
|
||||
}
|
||||
|
||||
parsedCPE.Version = p.Version
|
||||
parsedCPE.Attributes.Version = p.Version
|
||||
|
||||
return parsedCPE, true
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func FromPackageAttributes(p pkg.Package) []cpe.CPE {
|
||||
}
|
||||
|
||||
keys := strset.New()
|
||||
cpes := make([]cpe.CPE, 0)
|
||||
cpes := make([]cpe.Attributes, 0)
|
||||
for _, product := range products {
|
||||
for _, vendor := range vendors {
|
||||
// prevent duplicate entries...
|
||||
@@ -137,8 +137,12 @@ func FromPackageAttributes(p pkg.Package) []cpe.CPE {
|
||||
cpes = filter(cpes, p, cpeFilters...)
|
||||
|
||||
sort.Sort(cpe.BySpecificity(cpes))
|
||||
var result []cpe.CPE
|
||||
for _, c := range cpes {
|
||||
result = append(result, cpe.CPE{Attributes: c, Source: cpe.GeneratedSource})
|
||||
}
|
||||
|
||||
return cpes
|
||||
return result
|
||||
}
|
||||
|
||||
func candidateVendors(p pkg.Package) []string {
|
||||
|
||||
@@ -723,11 +723,14 @@ func TestGeneratePackageCPEs(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := FromPackageAttributes(test.p)
|
||||
expectedCpeSet := set.NewStringSet()
|
||||
for _, cpeStr := range test.expected {
|
||||
expectedCpeSet.Add("syft-generated:" + cpeStr)
|
||||
}
|
||||
|
||||
expectedCpeSet := set.NewStringSet(test.expected...)
|
||||
actualCpeSet := set.NewStringSet()
|
||||
for _, a := range actual {
|
||||
actualCpeSet.Add(a.String())
|
||||
actualCpeSet.Add(fmt.Sprintf("%s:%s", a.Source.String(), a.Attributes.String()))
|
||||
}
|
||||
|
||||
extra := strset.Difference(actualCpeSet, expectedCpeSet).List()
|
||||
@@ -1007,7 +1010,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, gotExists := FromDictionaryFind(tt.pkg)
|
||||
|
||||
assert.Equal(t, tt.want, got.BindToFmtString())
|
||||
assert.Equal(t, tt.want, got.Attributes.BindToFmtString())
|
||||
assert.Equal(t, tt.wantExists, gotExists)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ func (c *nativeImageCataloger) Name() string {
|
||||
func getPackage(component nativeImageComponent) pkg.Package {
|
||||
var cpes []cpe.CPE
|
||||
for _, property := range component.Properties {
|
||||
c, err := cpe.New(property.Value)
|
||||
c, err := cpe.New(property.Value, cpe.DeclaredSource)
|
||||
if err != nil {
|
||||
log.Debugf("unable to parse CPE: %v", err)
|
||||
log.Debugf("unable to parse Attributes: %v", err)
|
||||
continue
|
||||
}
|
||||
cpes = append(cpes, c)
|
||||
|
||||
@@ -80,22 +80,31 @@ func TestParseNativeImageSbom(t *testing.T) {
|
||||
},
|
||||
CPEs: []cpe.CPE{
|
||||
{
|
||||
Part: "a",
|
||||
Vendor: "codec",
|
||||
Product: "codec",
|
||||
Version: "4.1.73.Final",
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "codec",
|
||||
Product: "codec",
|
||||
Version: "4.1.73.Final",
|
||||
},
|
||||
Source: "declared",
|
||||
},
|
||||
{
|
||||
Part: "a",
|
||||
Vendor: "codec",
|
||||
Product: "netty-codec-http2",
|
||||
Version: "4.1.73.Final",
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "codec",
|
||||
Product: "netty-codec-http2",
|
||||
Version: "4.1.73.Final",
|
||||
},
|
||||
Source: "declared",
|
||||
},
|
||||
{
|
||||
Part: "a",
|
||||
Vendor: "codec",
|
||||
Product: "netty_codec_http2",
|
||||
Version: "4.1.73.Final",
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "codec",
|
||||
Product: "netty_codec_http2",
|
||||
Version: "4.1.73.Final",
|
||||
},
|
||||
Source: "declared",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,21 +14,11 @@ import (
|
||||
|
||||
func mustCPEs(s ...string) (c []cpe.CPE) {
|
||||
for _, i := range s {
|
||||
c = append(c, mustCPE(i))
|
||||
c = append(c, cpe.Must(i, ""))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func mustCPE(c string) cpe.CPE {
|
||||
return must(cpe.New(c))
|
||||
}
|
||||
func must(c cpe.CPE, e error) cpe.CPE {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func Test_parseSBOM(t *testing.T) {
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
|
||||
@@ -368,7 +368,7 @@ func TestCatalog_MergeRecords(t *testing.T) {
|
||||
name: "multiple Locations with shared path",
|
||||
pkgs: []Package{
|
||||
{
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:package:1:1:*:*:*:*:*:*:*")},
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:package:1:1:*:*:*:*:*:*:*", cpe.GeneratedSource)},
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewVirtualLocationFromCoordinates(
|
||||
file.Coordinates{
|
||||
@@ -381,7 +381,7 @@ func TestCatalog_MergeRecords(t *testing.T) {
|
||||
Type: RpmPkg,
|
||||
},
|
||||
{
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:b:package:1:1:*:*:*:*:*:*:*")},
|
||||
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:b:package:1:1:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource)},
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewVirtualLocationFromCoordinates(
|
||||
file.Coordinates{
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestIDUniqueness(t *testing.T) {
|
||||
Language: "math",
|
||||
Type: PythonPkg,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`),
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
PURL: "pkg:pypi/pi@3.14",
|
||||
Metadata: PythonPackage{
|
||||
@@ -256,7 +256,7 @@ func TestPackage_Merge(t *testing.T) {
|
||||
Language: "math",
|
||||
Type: PythonPkg,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`),
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
PURL: "pkg:pypi/pi@3.14",
|
||||
Metadata: PythonPackage{
|
||||
@@ -278,7 +278,7 @@ func TestPackage_Merge(t *testing.T) {
|
||||
Language: "math",
|
||||
Type: PythonPkg,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must(`cpe:2.3:a:DIFFERENT:pi:3.14:*:*:*:*:math:*:*`), // NOTE: difference
|
||||
cpe.Must(`cpe:2.3:a:DIFFERENT:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource), // NOTE: difference
|
||||
},
|
||||
PURL: "pkg:pypi/pi@3.14",
|
||||
Metadata: PythonPackage{
|
||||
@@ -301,8 +301,8 @@ func TestPackage_Merge(t *testing.T) {
|
||||
Language: "math",
|
||||
Type: PythonPkg,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`),
|
||||
cpe.Must(`cpe:2.3:a:DIFFERENT:pi:3.14:*:*:*:*:math:*:*`), // NOTE: merge!
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must(`cpe:2.3:a:DIFFERENT:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource), // NOTE: merge!
|
||||
},
|
||||
PURL: "pkg:pypi/pi@3.14",
|
||||
Metadata: PythonPackage{
|
||||
@@ -327,7 +327,7 @@ func TestPackage_Merge(t *testing.T) {
|
||||
Language: "math",
|
||||
Type: PythonPkg,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`),
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
PURL: "pkg:pypi/pi@3.14",
|
||||
Metadata: PythonPackage{
|
||||
@@ -349,7 +349,7 @@ func TestPackage_Merge(t *testing.T) {
|
||||
Language: "math",
|
||||
Type: PythonPkg,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`),
|
||||
cpe.Must(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`, cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
PURL: "pkg:pypi/pi@3.14",
|
||||
Metadata: PythonPackage{
|
||||
|
||||
Reference in New Issue
Block a user