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:
William Murphy
2024-02-02 16:17:52 +00:00
committed by GitHub
parent 4fe50f4169
commit b7a6d5e946
47 changed files with 2674 additions and 287 deletions
+1 -1
View File
@@ -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)},
},
},
},
+4 -3
View File
@@ -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),
}
}
+6 -6
View File
@@ -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),
},
},
{
+2 -2
View File
@@ -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)
}