Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: disable CPE-based matching for GHSA ecosystems by default #1412

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/validations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 9 additions & 6 deletions cmd/grype/cli/options/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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,
}
}
6 changes: 6 additions & 0 deletions grype/db/v5/namespace/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"), ""),
},
Expand Down
4 changes: 4 additions & 0 deletions grype/db/v5/namespace/language/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"), ""),
Expand Down
2 changes: 2 additions & 0 deletions grype/match/matcher_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -30,6 +31,7 @@ var AllMatcherTypes = []MatcherType{
PortageMatcher,
GoModuleMatcher,
OpenVexMatcher,
RustMatcher,
}

type MatcherType string
3 changes: 3 additions & 0 deletions grype/matcher/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -23,6 +24,7 @@ type Config struct {
Dotnet dotnet.MatcherConfig
Javascript javascript.MatcherConfig
Golang golang.MatcherConfig
Rust rust.MatcherConfig
Stock stock.MatcherConfig
}

Expand All @@ -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 changes: 40 additions & 0 deletions grype/matcher/rust/matcher.go
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions test/grype-test-config.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
check-for-app-update: false

16 changes: 16 additions & 0 deletions test/integration/db_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
{
Expand Down
48 changes: 47 additions & 1 deletion test/integration/match_by_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
@@ -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