Skip to content

Commit

Permalink
Fix: Include version information in binary cataloger CPEs (#1310)
Browse files Browse the repository at this point in the history
  • Loading branch information
kzantow committed Nov 3, 2022
1 parent 1046464 commit 3e99c4d
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 20 deletions.
53 changes: 33 additions & 20 deletions syft/pkg/cataloger/generic/classifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"regexp"

"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
Expand All @@ -22,10 +23,10 @@ type Classifier struct {
// source location. If any of the patterns match, the file will be considered a candidate for parsing.
// If no patterns are provided, the reader is automatically considered a candidate.
FilepathPatterns []*regexp.Regexp
// EvidencePattern is a list of regular expressions that will be used to match against the file contents of a
// EvidencePatterns is a list of regular expressions that will be used to match against the file contents of a
// given file in the source location. If any of the patterns match, the file will be considered a candidate for parsing.
EvidencePatterns []*regexp.Regexp
// CPE is the CPE we want to match against
// CPEs are the specific CPEs we want to include for this binary with updated version information
CPEs []pkg.CPE
}

Expand All @@ -45,29 +46,41 @@ func (c Classifier) Examine(reader source.LocationReadCloser) (p *pkg.Package, r
}

var classifiedPackage *pkg.Package
for _, patternTemplate := range c.EvidencePatterns {
if !patternTemplate.Match(contents) {
for _, evidencePattern := range c.EvidencePatterns {
if !evidencePattern.Match(contents) {
continue
}

matchMetadata := internal.MatchNamedCaptureGroups(patternTemplate, string(contents))
if classifiedPackage == nil {
classifiedPackage = &pkg.Package{
Name: path.Base(reader.VirtualPath),
Version: matchMetadata["version"],
Language: pkg.Binary,
Locations: source.NewLocationSet(reader.Location),
Type: pkg.BinaryPkg,
CPEs: c.CPEs,
MetadataType: pkg.BinaryMetadataType,
Metadata: pkg.BinaryMetadata{
Classifier: c.Package,
RealPath: reader.RealPath,
VirtualPath: reader.VirtualPath,
},
matchMetadata := internal.MatchNamedCaptureGroups(evidencePattern, string(contents))
version, ok := matchMetadata["version"]
if !ok {
log.Debugf("no version found in binary from pattern %v", evidencePattern)
continue
}

var cpes []pkg.CPE
for _, cpe := range c.CPEs {
cpe.Version = version
if err == nil {
cpes = append(cpes, cpe)
}
break
}

classifiedPackage = &pkg.Package{
Name: path.Base(reader.VirtualPath),
Version: version,
Language: pkg.Binary,
Locations: source.NewLocationSet(reader.Location),
Type: pkg.BinaryPkg,
CPEs: cpes,
MetadataType: pkg.BinaryMetadataType,
Metadata: pkg.BinaryMetadata{
Classifier: c.Package,
RealPath: reader.RealPath,
VirtualPath: reader.VirtualPath,
},
}
break
}
return classifiedPackage, nil, nil
}
Expand Down
96 changes: 96 additions & 0 deletions syft/pkg/cataloger/generic/classifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package generic

import (
"regexp"
"testing"

"github.com/stretchr/testify/require"

"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)

func Test_ClassifierCPEs(t *testing.T) {
tests := []struct {
name string
fixture string
classifier Classifier
cpes []string
}{
{
name: "no CPEs",
fixture: "test-fixtures/version.txt",
classifier: Classifier{
Package: "some-app",
FilepathPatterns: []*regexp.Regexp{
regexp.MustCompile(".*/version.txt"),
},
EvidencePatterns: []*regexp.Regexp{
regexp.MustCompile(`(?m)my-verison:(?P<version>[0-9.]+)`),
},
CPEs: []pkg.CPE{},
},
cpes: nil,
},
{
name: "one CPE",
fixture: "test-fixtures/version.txt",
classifier: Classifier{
Package: "some-app",
FilepathPatterns: []*regexp.Regexp{
regexp.MustCompile(".*/version.txt"),
},
EvidencePatterns: []*regexp.Regexp{
regexp.MustCompile(`(?m)my-verison:(?P<version>[0-9.]+)`),
},
CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"),
},
},
cpes: []string{
"cpe:2.3:a:some:app:1.8:*:*:*:*:*:*:*",
},
},
{
name: "multiple CPEs",
fixture: "test-fixtures/version.txt",
classifier: Classifier{
Package: "some-app",
FilepathPatterns: []*regexp.Regexp{
regexp.MustCompile(".*/version.txt"),
},
EvidencePatterns: []*regexp.Regexp{
regexp.MustCompile(`(?m)my-verison:(?P<version>[0-9.]+)`),
},
CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"),
pkg.MustCPE("cpe:2.3:a:some:apps:*:*:*:*:*:*:*:*"),
},
},
cpes: []string{
"cpe:2.3:a:some:app:1.8:*:*:*:*:*:*:*",
"cpe:2.3:a:some:apps:1.8:*:*:*:*:*:*:*",
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resolver := source.NewMockResolverForPaths(test.fixture)
locations, err := resolver.FilesByPath(test.fixture)
require.NoError(t, err)
require.Len(t, locations, 1)
location := locations[0]
readCloser, err := resolver.FileContentsByLocation(location)
require.NoError(t, err)
p, _, err := test.classifier.Examine(source.NewLocationReadCloser(location, readCloser))
require.NoError(t, err)

var cpes []string
for _, c := range p.CPEs {
cpes = append(cpes, pkg.CPEString(c))
}
require.Equal(t, test.cpes, cpes)
})
}
}
1 change: 1 addition & 0 deletions syft/pkg/cataloger/generic/test-fixtures/version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
my-verison:1.8

0 comments on commit 3e99c4d

Please sign in to comment.