mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +00:00
feat: disable CPE-based matching for GHSA ecosystems by default (#1412)
* feat: disable CPE-based matching for GHSA ecosystems by default Disables CPE-based matching for ecosystems which are covered by GitHub Security Advisories. Also adds a separate rust matcher and related configuration to allow configuring CPE-based matching off for it while still leaving it on for the stock matcher. Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * chore: use --by-cve with quality gate comparison Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * chore: add rust auditable binary match integration test Signed-off-by: Weston Steimel <weston.steimel@anchore.com> --------- Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
This commit is contained in:
parent
bcbc7e4bdc
commit
25762b7e3b
11 changed files with 131 additions and 7 deletions
1
.github/workflows/validations.yaml
vendored
1
.github/workflows/validations.yaml
vendored
|
@ -53,6 +53,7 @@ jobs:
|
|||
run: make quality
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GRYPE_BY_CVE: "true"
|
||||
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
|
|
|
@ -8,6 +8,7 @@ type matchConfig struct {
|
|||
Javascript matcherConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"` // settings for the javascript matcher
|
||||
Python matcherConfig `yaml:"python" json:"python" mapstructure:"python"` // settings for the python matcher
|
||||
Ruby matcherConfig `yaml:"ruby" json:"ruby" mapstructure:"ruby"` // settings for the ruby matcher
|
||||
Rust matcherConfig `yaml:"rust" json:"rust" mapstructure:"rust"` // settings for the rust matcher
|
||||
Stock matcherConfig `yaml:"stock" json:"stock" mapstructure:"stock"` // settings for the default/stock matcher
|
||||
}
|
||||
|
||||
|
@ -17,13 +18,15 @@ type matcherConfig struct {
|
|||
|
||||
func defaultMatchConfig() matchConfig {
|
||||
useCpe := matcherConfig{UseCPEs: true}
|
||||
dontUseCpe := matcherConfig{UseCPEs: false}
|
||||
return matchConfig{
|
||||
Java: useCpe,
|
||||
Dotnet: useCpe,
|
||||
Golang: useCpe,
|
||||
Javascript: useCpe,
|
||||
Python: useCpe,
|
||||
Ruby: useCpe,
|
||||
Java: dontUseCpe,
|
||||
Dotnet: dontUseCpe,
|
||||
Golang: dontUseCpe,
|
||||
Javascript: dontUseCpe,
|
||||
Python: dontUseCpe,
|
||||
Ruby: dontUseCpe,
|
||||
Rust: dontUseCpe,
|
||||
Stock: useCpe,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ func TestFromStringSlice(t *testing.T) {
|
|||
"nvd:cpe",
|
||||
"github:language:ruby",
|
||||
"abc.xyz:language:ruby",
|
||||
"github:language:rust",
|
||||
"something:language:rust",
|
||||
"1234.4567:language:unknown",
|
||||
"---:cpe",
|
||||
"another-provider:distro:alpine:3.15",
|
||||
|
@ -44,6 +46,10 @@ func TestFromStringSlice(t *testing.T) {
|
|||
language.NewNamespace("github", syftPkg.Ruby, ""),
|
||||
language.NewNamespace("abc.xyz", syftPkg.Ruby, ""),
|
||||
},
|
||||
syftPkg.Rust: {
|
||||
language.NewNamespace("github", syftPkg.Rust, ""),
|
||||
language.NewNamespace("something", syftPkg.Rust, ""),
|
||||
},
|
||||
syftPkg.Language("unknown"): {
|
||||
language.NewNamespace("1234.4567", syftPkg.Language("unknown"), ""),
|
||||
},
|
||||
|
|
|
@ -25,6 +25,10 @@ func TestFromString(t *testing.T) {
|
|||
namespaceString: "github:language:java",
|
||||
result: NewNamespace("github", syftPkg.Java, ""),
|
||||
},
|
||||
{
|
||||
namespaceString: "github:language:rust",
|
||||
result: NewNamespace("github", syftPkg.Rust, ""),
|
||||
},
|
||||
{
|
||||
namespaceString: "abc.xyz:language:something",
|
||||
result: NewNamespace("abc.xyz", syftPkg.Language("something"), ""),
|
||||
|
|
|
@ -15,6 +15,7 @@ const (
|
|||
PortageMatcher MatcherType = "portage-matcher"
|
||||
GoModuleMatcher MatcherType = "go-module-matcher"
|
||||
OpenVexMatcher MatcherType = "openvex-matcher"
|
||||
RustMatcher MatcherType = "rust-matcher"
|
||||
)
|
||||
|
||||
var AllMatcherTypes = []MatcherType{
|
||||
|
@ -30,6 +31,7 @@ var AllMatcherTypes = []MatcherType{
|
|||
PortageMatcher,
|
||||
GoModuleMatcher,
|
||||
OpenVexMatcher,
|
||||
RustMatcher,
|
||||
}
|
||||
|
||||
type MatcherType string
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/anchore/grype/grype/matcher/python"
|
||||
"github.com/anchore/grype/grype/matcher/rpm"
|
||||
"github.com/anchore/grype/grype/matcher/ruby"
|
||||
"github.com/anchore/grype/grype/matcher/rust"
|
||||
"github.com/anchore/grype/grype/matcher/stock"
|
||||
)
|
||||
|
||||
|
@ -23,6 +24,7 @@ type Config struct {
|
|||
Dotnet dotnet.MatcherConfig
|
||||
Javascript javascript.MatcherConfig
|
||||
Golang golang.MatcherConfig
|
||||
Rust rust.MatcherConfig
|
||||
Stock stock.MatcherConfig
|
||||
}
|
||||
|
||||
|
@ -39,6 +41,7 @@ func NewDefaultMatchers(mc Config) []Matcher {
|
|||
golang.NewGolangMatcher(mc.Golang),
|
||||
&msrc.Matcher{},
|
||||
&portage.Matcher{},
|
||||
rust.NewRustMatcher(mc.Rust),
|
||||
stock.NewStockMatcher(mc.Stock),
|
||||
}
|
||||
}
|
||||
|
|
40
grype/matcher/rust/matcher.go
Normal file
40
grype/matcher/rust/matcher.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package rust
|
||||
|
||||
import (
|
||||
"github.com/anchore/grype/grype/distro"
|
||||
"github.com/anchore/grype/grype/match"
|
||||
"github.com/anchore/grype/grype/pkg"
|
||||
"github.com/anchore/grype/grype/search"
|
||||
"github.com/anchore/grype/grype/vulnerability"
|
||||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
type Matcher struct {
|
||||
cfg MatcherConfig
|
||||
}
|
||||
|
||||
type MatcherConfig struct {
|
||||
UseCPEs bool
|
||||
}
|
||||
|
||||
func NewRustMatcher(cfg MatcherConfig) *Matcher {
|
||||
return &Matcher{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Matcher) PackageTypes() []syftPkg.Type {
|
||||
return []syftPkg.Type{syftPkg.RustPkg}
|
||||
}
|
||||
|
||||
func (m *Matcher) Type() match.MatcherType {
|
||||
return match.RustMatcher
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) {
|
||||
criteria := search.CommonCriteria
|
||||
if m.cfg.UseCPEs {
|
||||
criteria = append(criteria, search.ByCPE)
|
||||
}
|
||||
return search.ByCriteria(store, d, p, m.Type(), criteria...)
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
check-for-app-update: false
|
||||
|
||||
|
|
|
@ -163,6 +163,22 @@ func newMockDbStore() *mockStore {
|
|||
},
|
||||
},
|
||||
},
|
||||
"github:language:rust": {
|
||||
"hello-auditable": []grypeDB.Vulnerability{
|
||||
{
|
||||
ID: "CVE-rust-sample-1",
|
||||
VersionConstraint: "< 0.2.0",
|
||||
VersionFormat: "unknown",
|
||||
},
|
||||
},
|
||||
"auditable": []grypeDB.Vulnerability{
|
||||
{
|
||||
ID: "CVE-rust-sample-2",
|
||||
VersionConstraint: "< 0.2.0",
|
||||
VersionFormat: "unknown",
|
||||
},
|
||||
},
|
||||
},
|
||||
"debian:distro:debian:8": {
|
||||
"apt-dev": []grypeDB.Vulnerability{
|
||||
{
|
||||
|
|
|
@ -545,6 +545,45 @@ func addHaskellMatches(t *testing.T, theSource source.Source, catalog *syftPkg.C
|
|||
})
|
||||
}
|
||||
|
||||
func addRustMatches(t *testing.T, theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore, theResult *match.Matches) {
|
||||
packages := catalog.PackagesByPath("/hello-auditable")
|
||||
if len(packages) < 1 {
|
||||
t.Logf("Rust Packages: %+v", packages)
|
||||
t.Fatalf("problem with upstream syft cataloger (cargo-auditable-binary-cataloger)")
|
||||
}
|
||||
|
||||
for _, p := range packages {
|
||||
thePkg := pkg.New(p)
|
||||
theVuln := theStore.backend["github:language:rust"][strings.ToLower(thePkg.Name)][0]
|
||||
vulnObj, err := vulnerability.NewVulnerability(theVuln)
|
||||
require.NoError(t, err)
|
||||
|
||||
theResult.Add(match.Match{
|
||||
Vulnerability: *vulnObj,
|
||||
Package: thePkg,
|
||||
Details: []match.Detail{
|
||||
{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0,
|
||||
SearchedBy: map[string]any{
|
||||
"language": "rust",
|
||||
"namespace": "github:language:rust",
|
||||
"package": map[string]string{
|
||||
"name": thePkg.Name,
|
||||
"version": thePkg.Version,
|
||||
},
|
||||
},
|
||||
Found: map[string]any{
|
||||
"versionConstraint": vulnObj.Constraint.String(),
|
||||
"vulnerabilityID": vulnObj.ID,
|
||||
},
|
||||
Matcher: match.RustMatcher,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchByImage(t *testing.T) {
|
||||
observedMatchers := stringutil.NewStringSet()
|
||||
definedMatchers := stringutil.NewStringSet()
|
||||
|
@ -603,6 +642,14 @@ func TestMatchByImage(t *testing.T) {
|
|||
return expectedMatches
|
||||
},
|
||||
},
|
||||
{
|
||||
fixtureImage: "image-rust-auditable-match-coverage",
|
||||
expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches {
|
||||
expectedMatches := match.NewMatches()
|
||||
addRustMatches(t, theSource, catalog, theStore, &expectedMatches)
|
||||
return expectedMatches
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -647,7 +694,6 @@ func TestMatchByImage(t *testing.T) {
|
|||
}
|
||||
|
||||
actualResults := grype.FindVulnerabilitiesForPackage(str, theDistro, matchers, pkg.FromCollection(collection, pkg.SynthesisConfig{}))
|
||||
|
||||
for _, m := range actualResults.Sorted() {
|
||||
for _, d := range m.Details {
|
||||
observedMatchers.Add(string(d.Matcher))
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# An image containing the example hello-auditable binary from https://github.com/Shnatsel/rust-audit/tree/master/hello-auditable
|
||||
FROM docker.io/tofay/hello-rust-auditable:latest
|
Loading…
Reference in a new issue