mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +00:00
Add java.Matcher configuration to includes maven upstream sha1 query (#714)
This commit is contained in:
parent
e77a6c8d63
commit
95f68b4c33
16 changed files with 425 additions and 65 deletions
20
README.md
20
README.md
|
@ -165,6 +165,20 @@ will be resolved _relative to the specified scan directory_. Keep in mind, your
|
|||
may attempt to expand wildcards, so put those parameters in single quotes, like:
|
||||
`'**/*.json'`.
|
||||
|
||||
### External Sources
|
||||
|
||||
Grype can be configured to incorporate external data sources for added fidelity in vulnerability matching. This
|
||||
feature is currently disabled by default. To enable this feature add the following to the grype config:
|
||||
```yaml
|
||||
external-sources:
|
||||
enable: true
|
||||
maven:
|
||||
search-upstream-by-sha1: true
|
||||
base-url: https://search.maven.org/solrsearch/select
|
||||
```
|
||||
|
||||
You can also configure the base-url if you're using another registry as your maven endpoint.
|
||||
|
||||
### Output formats
|
||||
|
||||
The output format for Grype is configurable as well:
|
||||
|
@ -504,6 +518,12 @@ add-cpes-if-none: false
|
|||
# Explicitly specify a linux distribution to use as <distro>:<version> like alpine:3.10
|
||||
distro:
|
||||
|
||||
external-sources:
|
||||
enable: false
|
||||
maven:
|
||||
search-upstream-by-sha1: true
|
||||
base-url: https://search.maven.org/solrsearch/select
|
||||
|
||||
db:
|
||||
# check for database updates on execution
|
||||
# same as GRYPE_DB_AUTO_UPDATE env var
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/anchore/grype/grype/event"
|
||||
"github.com/anchore/grype/grype/grypeerr"
|
||||
"github.com/anchore/grype/grype/match"
|
||||
"github.com/anchore/grype/grype/matcher"
|
||||
"github.com/anchore/grype/grype/pkg"
|
||||
"github.com/anchore/grype/grype/presenter"
|
||||
"github.com/anchore/grype/grype/vulnerability"
|
||||
|
@ -309,7 +310,11 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha
|
|||
|
||||
applyDistroHint(&context, appConfig)
|
||||
|
||||
allMatches := grype.FindVulnerabilitiesForPackage(provider, context.Distro, packages...)
|
||||
matchers := matcher.NewDefaultMatchers(matcher.Config{
|
||||
Java: appConfig.ExternalSources.ToJavaMatcherConfig(),
|
||||
})
|
||||
|
||||
allMatches := grype.FindVulnerabilitiesForPackage(provider, context.Distro, matchers, packages)
|
||||
remainingMatches, ignoredMatches := match.ApplyIgnoreRules(allMatches, appConfig.Ignore)
|
||||
|
||||
if count := len(ignoredMatches); count > 0 {
|
||||
|
|
4
go.mod
4
go.mod
|
@ -9,8 +9,8 @@ require (
|
|||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b
|
||||
github.com/anchore/stereoscope v0.0.0-20220330165332-7fc73ee7b0f0
|
||||
github.com/anchore/syft v0.43.0
|
||||
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270
|
||||
github.com/anchore/syft v0.44.0
|
||||
github.com/bmatcuk/doublestar/v2 v2.0.4
|
||||
github.com/docker/docker v20.10.12+incompatible
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
|
|
8
go.sum
8
go.sum
|
@ -153,10 +153,10 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE
|
|||
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b h1:YJWYt/6KQXR9JR46lLHrTTYi8rcye42tKcyjREA/hvA=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
|
||||
github.com/anchore/stereoscope v0.0.0-20220330165332-7fc73ee7b0f0 h1:mObz7bepZ6DtbIrsB2mxuOE9XEYmVtA/P/EDlKwfbjs=
|
||||
github.com/anchore/stereoscope v0.0.0-20220330165332-7fc73ee7b0f0/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc=
|
||||
github.com/anchore/syft v0.43.0 h1:9w52VCgSutRL08ojztoXwG6/KdOjS3w+5ePZqJNGx5o=
|
||||
github.com/anchore/syft v0.43.0/go.mod h1:Po6PO927XnrHzrs8qshOjAfb/wBmu4cXEeO7FmlWFgk=
|
||||
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270 h1:NmxPDR6vo3xjwCL6o+tpF1vUad/BVo+WaVSwueB9W9w=
|
||||
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc=
|
||||
github.com/anchore/syft v0.44.0 h1:HlaWN2yLbj4Of0fATK/72BmDPMwRrQBWZ/HUmVthnj8=
|
||||
github.com/anchore/syft v0.44.0/go.mod h1:m6/cWOz0aFbCL0pflHvvBgvw0qMPLVqDQa0L8+FBPVE=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
|
|
|
@ -29,11 +29,13 @@ func FindVulnerabilities(provider vulnerability.Provider, userImageStr string, s
|
|||
return match.Matches{}, pkg.Context{}, nil, err
|
||||
}
|
||||
|
||||
return FindVulnerabilitiesForPackage(provider, context.Distro, packages...), context, packages, nil
|
||||
matchers := matcher.NewDefaultMatchers(matcher.Config{})
|
||||
|
||||
return FindVulnerabilitiesForPackage(provider, context.Distro, matchers, packages), context, packages, nil
|
||||
}
|
||||
|
||||
func FindVulnerabilitiesForPackage(provider vulnerability.Provider, d *linux.Release, packages ...pkg.Package) match.Matches {
|
||||
return matcher.FindMatches(provider, d, packages...)
|
||||
func FindVulnerabilitiesForPackage(provider vulnerability.Provider, d *linux.Release, matchers []matcher.Matcher, packages []pkg.Package) match.Matches {
|
||||
return matcher.FindMatches(provider, d, matchers, packages)
|
||||
}
|
||||
|
||||
func LoadVulnerabilityDB(cfg db.Config, update bool) (vulnerability.Provider, vulnerability.MetadataProvider, *db.Status, error) {
|
||||
|
|
|
@ -35,6 +35,7 @@ func TestMatcherDpkg_matchBySourceIndirection(t *testing.T) {
|
|||
|
||||
store := newMockProvider()
|
||||
actual, err := matcher.matchUpstreamPackages(store, d, p)
|
||||
assert.NoError(t, err, "unexpected err from matchUpstreamPackages", err)
|
||||
|
||||
assert.Len(t, actual, 2, "unexpected indirect matches count")
|
||||
|
||||
|
|
|
@ -1,15 +1,119 @@
|
|||
package java
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"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"
|
||||
"github.com/anchore/grype/internal/log"
|
||||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const (
|
||||
sha1Query = `1:"%s"`
|
||||
)
|
||||
|
||||
type Matcher struct {
|
||||
SearchMavenUpstream bool
|
||||
MavenSearcher
|
||||
}
|
||||
|
||||
// MavenSearcher is the interface that wraps the GetMavenPackageBySha method.
|
||||
type MavenSearcher interface {
|
||||
// GetMavenPackageBySha provides an interface for building a package from maven data based on a sha1 digest
|
||||
GetMavenPackageBySha(string) (*pkg.Package, error)
|
||||
}
|
||||
|
||||
// mavenSearch implements the MavenSearcher interface
|
||||
type mavenSearch struct {
|
||||
client *http.Client
|
||||
baseURL string
|
||||
}
|
||||
|
||||
type mavenAPIResponse struct {
|
||||
Response struct {
|
||||
NumFound int `json:"numFound"`
|
||||
Docs []struct {
|
||||
ID string `json:"id"`
|
||||
GroupID string `json:"g"`
|
||||
ArtifactID string `json:"a"`
|
||||
Version string `json:"v"`
|
||||
P string `json:"p"`
|
||||
VersionCount int `json:"versionCount"`
|
||||
} `json:"docs"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
func (ms *mavenSearch) GetMavenPackageBySha(sha1 string) (*pkg.Package, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, ms.baseURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to initialize HTTP client: %w", err)
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Set("q", fmt.Sprintf(sha1Query, sha1))
|
||||
q.Set("rows", "1")
|
||||
q.Set("wt", "json")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
resp, err := ms.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sha1 search error: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("status %s from %s", resp.Status, req.URL.String())
|
||||
}
|
||||
|
||||
var res mavenAPIResponse
|
||||
if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
return nil, fmt.Errorf("json decode error: %w", err)
|
||||
}
|
||||
|
||||
if len(res.Response.Docs) == 0 {
|
||||
return nil, fmt.Errorf("digest %s: %w", sha1, errors.New("no artifact found"))
|
||||
}
|
||||
|
||||
// artifacts might have the same SHA-1 digests.
|
||||
// e.g. "javax.servlet:jstl" and "jstl:jstl"
|
||||
docs := res.Response.Docs
|
||||
sort.Slice(docs, func(i, j int) bool {
|
||||
return docs[i].ID < docs[j].ID
|
||||
})
|
||||
d := docs[0]
|
||||
|
||||
return &pkg.Package{
|
||||
Name: fmt.Sprintf("%s:%s", d.GroupID, d.ArtifactID),
|
||||
Version: d.Version,
|
||||
Language: syftPkg.Java,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
PomArtifactID: d.ArtifactID,
|
||||
PomGroupID: d.GroupID,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type MatcherConfig struct {
|
||||
SearchMavenUpstream bool
|
||||
MavenBaseURL string
|
||||
}
|
||||
|
||||
func NewJavaMatcher(cfg MatcherConfig) *Matcher {
|
||||
return &Matcher{
|
||||
cfg.SearchMavenUpstream,
|
||||
&mavenSearch{
|
||||
client: http.DefaultClient,
|
||||
baseURL: cfg.MavenBaseURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Matcher) PackageTypes() []syftPkg.Type {
|
||||
|
@ -21,5 +125,44 @@ func (m *Matcher) Type() match.MatcherType {
|
|||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) {
|
||||
return search.ByCriteria(store, d, p, m.Type(), search.CommonCriteria...)
|
||||
var matches []match.Match
|
||||
if m.SearchMavenUpstream {
|
||||
upstreamMatches, err := m.matchUpstreamMavenPackages(store, p)
|
||||
if err != nil {
|
||||
log.Warnf("failed to match against upstream data for %s: %v", p.Name, err)
|
||||
} else {
|
||||
matches = append(matches, upstreamMatches...)
|
||||
}
|
||||
}
|
||||
criteriaMatches, err := search.ByCriteria(store, d, p, m.Type(), search.CommonCriteria...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to match by exact package: %w", err)
|
||||
}
|
||||
|
||||
matches = append(matches, criteriaMatches...)
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
func (m *Matcher) matchUpstreamMavenPackages(store vulnerability.Provider, p pkg.Package) ([]match.Match, error) {
|
||||
var matches []match.Match
|
||||
|
||||
if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok {
|
||||
for _, d := range metadata.ArchiveDigests {
|
||||
if d.Algorithm == "sha1" {
|
||||
indirectPackage, err := m.GetMavenPackageBySha(d.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indirectMatches, err := search.ByPackageLanguage(store, *indirectPackage, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, indirectMatches...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match.ConvertToIndirectMatches(matches, p)
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
|
69
grype/matcher/java/matcher_mocks_test.go
Normal file
69
grype/matcher/java/matcher_mocks_test.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package java
|
||||
|
||||
import (
|
||||
"github.com/anchore/grype/grype/distro"
|
||||
"github.com/anchore/grype/grype/pkg"
|
||||
"github.com/anchore/grype/grype/version"
|
||||
"github.com/anchore/grype/grype/vulnerability"
|
||||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
type mockProvider struct {
|
||||
data map[syftPkg.Language]map[string][]vulnerability.Vulnerability
|
||||
}
|
||||
|
||||
func (mp *mockProvider) populateData() {
|
||||
mp.data[syftPkg.Java] = map[string][]vulnerability.Vulnerability{
|
||||
"org.springframework.spring-webmvc": {
|
||||
{
|
||||
Constraint: version.MustGetConstraint(">=5.0.0,<5.1.7", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-2",
|
||||
},
|
||||
{
|
||||
Constraint: version.MustGetConstraint(">=5.0.1,<5.1.7", version.UnknownFormat),
|
||||
ID: "CVE-2013-fake-3",
|
||||
},
|
||||
// unexpected...
|
||||
{
|
||||
Constraint: version.MustGetConstraint(">=5.0.0,<5.0.7", version.UnknownFormat),
|
||||
ID: "CVE-2013-fake-BAD",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newMockProvider() *mockProvider {
|
||||
mp := mockProvider{
|
||||
data: make(map[syftPkg.Language]map[string][]vulnerability.Vulnerability),
|
||||
}
|
||||
|
||||
mp.populateData()
|
||||
|
||||
return &mp
|
||||
}
|
||||
|
||||
type mockMavenSearcher struct {
|
||||
pkg pkg.Package
|
||||
}
|
||||
|
||||
func (m mockMavenSearcher) GetMavenPackageBySha(string) (*pkg.Package, error) {
|
||||
return &m.pkg, nil
|
||||
}
|
||||
|
||||
func newMockSearcher(pkg pkg.Package) MavenSearcher {
|
||||
return mockMavenSearcher{
|
||||
pkg,
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *mockProvider) GetByCPE(p syftPkg.CPE) ([]vulnerability.Vulnerability, error) {
|
||||
return []vulnerability.Vulnerability{}, nil
|
||||
}
|
||||
|
||||
func (mp *mockProvider) GetByDistro(d *distro.Distro, p pkg.Package) ([]vulnerability.Vulnerability, error) {
|
||||
return []vulnerability.Vulnerability{}, nil
|
||||
}
|
||||
|
||||
func (mp *mockProvider) GetByLanguage(l syftPkg.Language, p pkg.Package) ([]vulnerability.Vulnerability, error) {
|
||||
return mp.data[l][p.Name], nil
|
||||
}
|
62
grype/matcher/java/matcher_test.go
Normal file
62
grype/matcher/java/matcher_test.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package java
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/grype/grype/match"
|
||||
"github.com/anchore/grype/grype/pkg"
|
||||
"github.com/anchore/grype/internal"
|
||||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func TestMatcherJava_matchUpstreamMavenPackage(t *testing.T) {
|
||||
p := pkg.Package{
|
||||
ID: pkg.ID(uuid.NewString()),
|
||||
Name: "org.springframework.spring-webmvc",
|
||||
Version: "5.1.5.RELEASE",
|
||||
Language: syftPkg.Java,
|
||||
Type: syftPkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
ArchiveDigests: []pkg.Digest{
|
||||
{
|
||||
Algorithm: "sha1",
|
||||
Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
matcher := Matcher{
|
||||
SearchMavenUpstream: true,
|
||||
MavenSearcher: newMockSearcher(p),
|
||||
}
|
||||
store := newMockProvider()
|
||||
actual, _ := matcher.matchUpstreamMavenPackages(store, p)
|
||||
|
||||
assert.Len(t, actual, 2, "unexpected matches count")
|
||||
|
||||
foundCVEs := internal.NewStringSet()
|
||||
for _, v := range actual {
|
||||
foundCVEs.Add(v.Vulnerability.ID)
|
||||
|
||||
require.NotEmpty(t, v.Details)
|
||||
for _, d := range v.Details {
|
||||
assert.Equal(t, match.ExactIndirectMatch, d.Type, "indirect match not indicated")
|
||||
assert.Equal(t, matcher.Type(), d.Matcher, "failed to capture matcher type")
|
||||
}
|
||||
assert.Equal(t, p.Name, v.Package.Name, "failed to capture original package name")
|
||||
}
|
||||
|
||||
for _, id := range []string{"CVE-2014-fake-2", "CVE-2013-fake-3"} {
|
||||
if !foundCVEs.Contains(id) {
|
||||
t.Errorf("missing discovered CVE: %s", id)
|
||||
}
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Logf("discovered CVES: %+v", foundCVEs)
|
||||
}
|
||||
}
|
|
@ -24,50 +24,30 @@ import (
|
|||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
var controllerInstance controller
|
||||
|
||||
type Monitor struct {
|
||||
PackagesProcessed progress.Monitorable
|
||||
VulnerabilitiesDiscovered progress.Monitorable
|
||||
}
|
||||
|
||||
func init() {
|
||||
controllerInstance = newController()
|
||||
// Config contains values used by individual matcher structs for advanced configuration
|
||||
type Config struct {
|
||||
Java java.MatcherConfig
|
||||
}
|
||||
|
||||
type controller struct {
|
||||
matchers map[syftPkg.Type][]Matcher
|
||||
}
|
||||
|
||||
func newController() controller {
|
||||
ctrlr := controller{
|
||||
matchers: make(map[syftPkg.Type][]Matcher),
|
||||
}
|
||||
ctrlr.add(&dpkg.Matcher{})
|
||||
ctrlr.add(&ruby.Matcher{})
|
||||
ctrlr.add(&python.Matcher{})
|
||||
ctrlr.add(&rpmdb.Matcher{})
|
||||
ctrlr.add(&java.Matcher{})
|
||||
ctrlr.add(&javascript.Matcher{})
|
||||
ctrlr.add(&apk.Matcher{})
|
||||
ctrlr.add(&msrc.Matcher{})
|
||||
return ctrlr
|
||||
}
|
||||
|
||||
func (c *controller) add(matchers ...Matcher) {
|
||||
for _, m := range matchers {
|
||||
for _, t := range m.PackageTypes() {
|
||||
if _, ok := c.matchers[t]; ok {
|
||||
c.matchers[t] = make([]Matcher, 0)
|
||||
}
|
||||
|
||||
c.matchers[t] = append(c.matchers[t], m)
|
||||
log.Debugf("adding matcher: %+v", t)
|
||||
}
|
||||
func NewDefaultMatchers(mc Config) []Matcher {
|
||||
return []Matcher{
|
||||
&dpkg.Matcher{},
|
||||
&ruby.Matcher{},
|
||||
&python.Matcher{},
|
||||
&rpmdb.Matcher{},
|
||||
java.NewJavaMatcher(mc.Java),
|
||||
&javascript.Matcher{},
|
||||
&apk.Matcher{},
|
||||
&msrc.Matcher{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) trackMatcher() (*progress.Manual, *progress.Manual) {
|
||||
func trackMatcher() (*progress.Manual, *progress.Manual) {
|
||||
packagesProcessed := progress.Manual{}
|
||||
vulnerabilitiesDiscovered := progress.Manual{}
|
||||
|
||||
|
@ -81,9 +61,26 @@ func (c *controller) trackMatcher() (*progress.Manual, *progress.Manual) {
|
|||
return &packagesProcessed, &vulnerabilitiesDiscovered
|
||||
}
|
||||
|
||||
func (c *controller) findMatches(provider vulnerability.Provider, release *linux.Release, packages ...pkg.Package) match.Matches {
|
||||
func newMatcherIndex(matchers []Matcher) map[syftPkg.Type][]Matcher {
|
||||
matcherIndex := make(map[syftPkg.Type][]Matcher)
|
||||
for _, m := range matchers {
|
||||
for _, t := range m.PackageTypes() {
|
||||
if _, ok := matcherIndex[t]; !ok {
|
||||
matcherIndex[t] = make([]Matcher, 0)
|
||||
}
|
||||
|
||||
matcherIndex[t] = append(matcherIndex[t], m)
|
||||
log.Debugf("adding matcher: %+v", t)
|
||||
}
|
||||
}
|
||||
|
||||
return matcherIndex
|
||||
}
|
||||
|
||||
func FindMatches(provider vulnerability.Provider, release *linux.Release, matchers []Matcher, packages []pkg.Package) match.Matches {
|
||||
var err error
|
||||
res := match.NewMatches()
|
||||
matcherIndex := newMatcherIndex(matchers)
|
||||
|
||||
var d *distro.Distro
|
||||
if release != nil {
|
||||
|
@ -93,14 +90,14 @@ func (c *controller) findMatches(provider vulnerability.Provider, release *linux
|
|||
}
|
||||
}
|
||||
|
||||
packagesProcessed, vulnerabilitiesDiscovered := c.trackMatcher()
|
||||
packagesProcessed, vulnerabilitiesDiscovered := trackMatcher()
|
||||
|
||||
defaultMatcher := &stock.Matcher{}
|
||||
for _, p := range packages {
|
||||
packagesProcessed.N++
|
||||
log.Debugf("searching for vulnerability matches for pkg=%s", p)
|
||||
|
||||
matchers, ok := c.matchers[p.Type]
|
||||
matchers, ok := matcherIndex[p.Type]
|
||||
if !ok {
|
||||
matchers = []Matcher{defaultMatcher}
|
||||
}
|
||||
|
@ -124,10 +121,6 @@ func (c *controller) findMatches(provider vulnerability.Provider, release *linux
|
|||
return res
|
||||
}
|
||||
|
||||
func FindMatches(provider vulnerability.Provider, d *linux.Release, packages ...pkg.Package) match.Matches {
|
||||
return controllerInstance.findMatches(provider, d, packages...)
|
||||
}
|
||||
|
||||
func logMatches(p pkg.Package, matches []match.Match) {
|
||||
if len(matches) > 0 {
|
||||
log.Debugf("found %d vulnerabilities for pkg=%s", len(matches), p)
|
|
@ -1,8 +1,14 @@
|
|||
package pkg
|
||||
|
||||
type JavaMetadata struct {
|
||||
VirtualPath string `json:"virtualPath"`
|
||||
PomArtifactID string `json:"pomArtifactID"`
|
||||
PomGroupID string `json:"pomGroupID"`
|
||||
ManifestName string `json:"manifestName"`
|
||||
VirtualPath string `json:"virtualPath"`
|
||||
PomArtifactID string `json:"pomArtifactID"`
|
||||
PomGroupID string `json:"pomGroupID"`
|
||||
ManifestName string `json:"manifestName"`
|
||||
ArchiveDigests []Digest `json:"archiveDigests"`
|
||||
}
|
||||
|
||||
type Digest struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
|
|
@ -165,11 +165,22 @@ func javaDataFromPkg(p pkg.Package) (metadata *JavaMetadata) {
|
|||
}
|
||||
}
|
||||
|
||||
var archiveDigests []Digest
|
||||
if len(value.ArchiveDigests) > 0 {
|
||||
for _, d := range value.ArchiveDigests {
|
||||
archiveDigests = append(archiveDigests, Digest{
|
||||
Algorithm: d.Algorithm,
|
||||
Value: d.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
metadata = &JavaMetadata{
|
||||
VirtualPath: value.VirtualPath,
|
||||
PomArtifactID: artifact,
|
||||
PomGroupID: group,
|
||||
ManifestName: name,
|
||||
VirtualPath: value.VirtualPath,
|
||||
PomArtifactID: artifact,
|
||||
PomGroupID: group,
|
||||
ManifestName: name,
|
||||
ArchiveDigests: archiveDigests,
|
||||
}
|
||||
} else {
|
||||
log.Warnf("unable to extract Java metadata for %s", p)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
syftFile "github.com/anchore/syft/syft/file"
|
||||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
@ -126,6 +127,10 @@ func TestNew(t *testing.T) {
|
|||
"extra-key": "extra-value",
|
||||
},
|
||||
},
|
||||
ArchiveDigests: []syftFile.Digest{{
|
||||
Algorithm: "sha1",
|
||||
Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211",
|
||||
}},
|
||||
},
|
||||
},
|
||||
metadata: JavaMetadata{
|
||||
|
@ -133,6 +138,10 @@ func TestNew(t *testing.T) {
|
|||
PomArtifactID: "pom-artifact-ID-info",
|
||||
PomGroupID: "pom-group-ID-info",
|
||||
ManifestName: "main-section-name-info",
|
||||
ArchiveDigests: []Digest{{
|
||||
Algorithm: "sha1",
|
||||
Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -44,6 +44,7 @@ type Application struct {
|
|||
Ignore []match.IgnoreRule `yaml:"ignore" json:"ignore" mapstructure:"ignore"`
|
||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||
DB database `yaml:"db" json:"db" mapstructure:"db"`
|
||||
ExternalSources externalSources `yaml:"external-sources" json:"externalSources" mapstructure:"external-sources"`
|
||||
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
|
||||
FailOn string `yaml:"fail-on-severity" json:"fail-on-severity" mapstructure:"fail-on-severity"`
|
||||
FailOnSeverity *vulnerability.Severity `yaml:"-" json:"-"`
|
||||
|
|
39
internal/config/datasources.go
Normal file
39
internal/config/datasources.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/anchore/grype/grype/matcher/java"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMavenBaseURL = "https://search.maven.org/solrsearch/select"
|
||||
)
|
||||
|
||||
type externalSources struct {
|
||||
Enable bool `yaml:"enable" json:"enable" mapstructure:"enable"`
|
||||
Maven maven `yaml:"maven" json:"maven" mapsructure:"maven"`
|
||||
}
|
||||
|
||||
type maven struct {
|
||||
SearchUpstreamBySha1 bool `yaml:"search-upstream" json:"searchUpstreamBySha1" mapstructure:"search-maven-upstream"`
|
||||
BaseURL string `yaml:"base-url" json:"baseUrl" mapstructure:"base-url"`
|
||||
}
|
||||
|
||||
func (cfg externalSources) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("external-sources.enable", false)
|
||||
v.SetDefault("external-sources.maven.search-maven-upstream", true)
|
||||
v.SetDefault("external-sources.maven.base-url", defaultMavenBaseURL)
|
||||
}
|
||||
|
||||
func (cfg externalSources) ToJavaMatcherConfig() java.MatcherConfig {
|
||||
// always respect if global config is disabled
|
||||
smu := cfg.Maven.SearchUpstreamBySha1
|
||||
if !cfg.Enable {
|
||||
smu = cfg.Enable
|
||||
}
|
||||
return java.MatcherConfig{
|
||||
SearchMavenUpstream: smu,
|
||||
MavenBaseURL: cfg.Maven.BaseURL,
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/anchore/grype/grype"
|
||||
"github.com/anchore/grype/grype/db"
|
||||
"github.com/anchore/grype/grype/match"
|
||||
"github.com/anchore/grype/grype/matcher"
|
||||
"github.com/anchore/grype/grype/pkg"
|
||||
"github.com/anchore/grype/grype/vulnerability"
|
||||
"github.com/anchore/grype/internal"
|
||||
|
@ -378,11 +379,9 @@ func TestMatchByImage(t *testing.T) {
|
|||
t.Fatalf("could not get the source obj: %+v", err)
|
||||
}
|
||||
|
||||
actualResults := grype.FindVulnerabilitiesForPackage(
|
||||
db.NewVulnerabilityProvider(theStore),
|
||||
theDistro,
|
||||
pkg.FromCatalog(theCatalog, pkg.ProviderConfig{})...,
|
||||
)
|
||||
matchers := matcher.NewDefaultMatchers(matcher.Config{})
|
||||
|
||||
actualResults := grype.FindVulnerabilitiesForPackage(db.NewVulnerabilityProvider(theStore), theDistro, matchers, pkg.FromCatalog(theCatalog, pkg.ProviderConfig{}))
|
||||
|
||||
// build expected matches from what's discovered from the catalog
|
||||
expectedMatches := test.expectedFn(*theSource, theCatalog, theStore)
|
||||
|
|
Loading…
Reference in a new issue