mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +00:00
Add integration tests (#54)
* add integration tests + add matcher types * tweak db auto update var; rm dead cache cmd * Update cmd/root.go Co-authored-by: Alfredo Deza <adeza@anchore.com> Co-authored-by: Alfredo Deza <adeza@anchore.com>
This commit is contained in:
parent
66453e65f2
commit
c8bca755ff
53 changed files with 736 additions and 297 deletions
|
@ -9,6 +9,9 @@ jobs:
|
|||
- image: circleci/golang:<< parameters.version >>
|
||||
environment:
|
||||
GO111MODULE: "on"
|
||||
# work around for recent circle CI breaking change
|
||||
# Error: "Error response from daemon: client version 1.39 is too new. Maximum supported API version is 1.38"
|
||||
DOCKER_API_VERSION: "1.38"
|
||||
# 1CPU / 2GB RAM
|
||||
resource_class: small
|
||||
steps:
|
||||
|
@ -39,6 +42,9 @@ jobs:
|
|||
- image: circleci/golang:<< parameters.version >>
|
||||
environment:
|
||||
GO111MODULE: "on"
|
||||
# work around for recent circle CI breaking change
|
||||
# Error: "Error response from daemon: client version 1.39 is too new. Maximum supported API version is 1.38"
|
||||
DOCKER_API_VERSION: "1.38"
|
||||
# 1CPU / 2GB RAM
|
||||
resource_class: small
|
||||
steps:
|
||||
|
@ -77,25 +83,21 @@ jobs:
|
|||
name: run unit tests
|
||||
command: make unit
|
||||
|
||||
# TODO: uncomment me when there are integration tests
|
||||
- run:
|
||||
name: build hash key for integration test-fixtures blobs
|
||||
command: make integration-fingerprint
|
||||
|
||||
# - run:
|
||||
# name: build hash key for tar cache
|
||||
# command: find integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee integration/test-fixtures/tar-cache.key
|
||||
- restore_cache:
|
||||
keys:
|
||||
- integration-test-tar-cache-{{ checksum "integration/test-fixtures/tar-cache.fingerprint" }}
|
||||
- run:
|
||||
name: run integration tests
|
||||
command: make integration
|
||||
|
||||
# - restore_cache:
|
||||
# keys:
|
||||
# - integration-test-tar-cache-{{ checksum "integration/test-fixtures/tar-cache.key" }}
|
||||
# - run:
|
||||
# name: run integration tests
|
||||
# command: |
|
||||
# docker version
|
||||
# make integration
|
||||
|
||||
# - save_cache:
|
||||
# key: integration-test-tar-cache-{{ checksum "integration/test-fixtures/tar-cache.key" }}
|
||||
# paths:
|
||||
# - "integration/test-fixtures/tar-cache"
|
||||
- save_cache:
|
||||
key: integration-test-tar-cache-{{ checksum "integration/test-fixtures/tar-cache.fingerprint" }}
|
||||
paths:
|
||||
- "integration/test-fixtures/tar-cache"
|
||||
|
||||
workflows:
|
||||
"Static Analysis & All Tests":
|
||||
|
|
13
Makefile
13
Makefile
|
@ -31,8 +31,7 @@ all: lint test ## Run all checks (linting, unit tests, and integration tests)
|
|||
compare:
|
||||
@cd comparison && make
|
||||
|
||||
# TODO: add me back in when integration tests are implemented
|
||||
test: unit #integration ## Run all tests (currently only unit)
|
||||
test: unit integration ## Run all tests (unit & integration tests)
|
||||
|
||||
help:
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BOLD)$(CYAN)%-25s$(RESET)%s\n", $$1, $$2}'
|
||||
|
@ -75,10 +74,12 @@ unit: ## Run unit tests (with coverage)
|
|||
@echo "Coverage: $$(cat $(COVER_TOTAL))"
|
||||
@if [ $$(echo "$$(cat $(COVER_TOTAL)) >= $(COVERAGE_THRESHOLD)" | bc -l) -ne 1 ]; then echo "$(RED)$(BOLD)Failed coverage quality gate (> $(COVERAGE_THRESHOLD)%)$(RESET)" && false; fi
|
||||
|
||||
# TODO: add me back in when integration tests are implemented
|
||||
#integration: ## Run integration tests
|
||||
# $(call title,Running integration tests)
|
||||
# go test -tags=integration ./integration
|
||||
integration: ## Run integration tests
|
||||
$(call title,Running integration tests)
|
||||
go test -v -tags=integration ./integration
|
||||
|
||||
integration/test-fixtures/tar-cache.key, integration-fingerprint:
|
||||
find integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee integration/test-fixtures/tar-cache.fingerprint
|
||||
|
||||
clear-test-cache: ## Delete all test cache (built docker image tars)
|
||||
find . -type f -wholename "**/test-fixtures/tar-cache/*.tar" -delete
|
||||
|
|
14
cmd/cache.go
14
cmd/cache.go
|
@ -1,14 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cacheCmd = &cobra.Command{
|
||||
Use: "cache",
|
||||
Short: "operate on the result cache",
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(cacheCmd)
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cacheClearCmd = &cobra.Command{
|
||||
Use: "clear",
|
||||
Short: "delete the results cache",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
os.Exit(runCacheClearCmd(cmd, args))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cacheCmd.AddCommand(cacheClearCmd)
|
||||
}
|
||||
|
||||
func runCacheClearCmd(cmd *cobra.Command, args []string) int {
|
||||
log.Error("cache CLEAR command...")
|
||||
return 0
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cacheShowCmd = &cobra.Command{
|
||||
Use: "show",
|
||||
Short: "show images that have been scanned",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
os.Exit(runCacheShowCmd(cmd, args))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cacheCmd.AddCommand(cacheShowCmd)
|
||||
}
|
||||
|
||||
func runCacheShowCmd(cmd *cobra.Command, args []string) int {
|
||||
log.Error("cache SHOW command...")
|
||||
return 0
|
||||
}
|
18
cmd/cmd.go
18
cmd/cmd.go
|
@ -20,6 +20,17 @@ var log *zap.SugaredLogger
|
|||
var cliOnlyOpts config.CliOnlyOptions
|
||||
|
||||
func init() {
|
||||
setGlobalCliOptions()
|
||||
|
||||
// read in config and setup logger
|
||||
cobra.OnInitialize(
|
||||
initAppConfig,
|
||||
initLogging,
|
||||
logAppConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func setGlobalCliOptions() {
|
||||
// setup global CLI options (available on all CLI commands)
|
||||
rootCmd.PersistentFlags().StringVarP(&cliOnlyOpts.ConfigPath, "config", "c", "", "application config file")
|
||||
|
||||
|
@ -34,13 +45,6 @@ func init() {
|
|||
}
|
||||
|
||||
rootCmd.PersistentFlags().CountVarP(&cliOnlyOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
|
||||
|
||||
// read in config and setup logger
|
||||
cobra.OnInitialize(
|
||||
initAppConfig,
|
||||
initLogging,
|
||||
logAppConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
|
|
81
cmd/root.go
81
cmd/root.go
|
@ -5,16 +5,12 @@ import (
|
|||
"os"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom"
|
||||
_distro "github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/scope"
|
||||
"github.com/anchore/vulnscan/internal"
|
||||
"github.com/anchore/vulnscan/internal/format"
|
||||
"github.com/anchore/vulnscan/internal/version"
|
||||
"github.com/anchore/vulnscan/vulnscan"
|
||||
"github.com/anchore/vulnscan/vulnscan/db"
|
||||
"github.com/anchore/vulnscan/vulnscan/presenter"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
@ -43,13 +39,16 @@ var rootCmd = &cobra.Command{
|
|||
}
|
||||
}
|
||||
|
||||
exitCode := runDefaultCmd(cmd, args)
|
||||
err := runDefaultCmd(cmd, args)
|
||||
|
||||
if appConfig.Dev.ProfileCPU {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
if err != nil {
|
||||
log.Errorf(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,8 @@ func init() {
|
|||
flag := "scope"
|
||||
rootCmd.Flags().StringP(
|
||||
"scope", "s", scope.AllLayersScope.String(),
|
||||
fmt.Sprintf("selection of layers to analyze, options=%v", scope.Options))
|
||||
fmt.Sprintf("selection of layers to analyze, options=%v", scope.Options),
|
||||
)
|
||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||
os.Exit(1)
|
||||
|
@ -69,7 +69,7 @@ func init() {
|
|||
// output & formatting options
|
||||
flag = "output"
|
||||
rootCmd.Flags().StringP(
|
||||
flag, "o", "json",
|
||||
flag, "o", presenter.JSONPresenter.String(),
|
||||
fmt.Sprintf("report output formatter, options=%v", presenter.Options),
|
||||
)
|
||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||
|
@ -78,8 +78,7 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// nolint:funlen
|
||||
func runDefaultCmd(_ *cobra.Command, args []string) int {
|
||||
func runDefaultCmd(_ *cobra.Command, args []string) error {
|
||||
if appConfig.CheckForAppUpdate {
|
||||
isAvailable, newVersion, err := version.IsUpdateAvailable()
|
||||
if err != nil {
|
||||
|
@ -93,66 +92,20 @@ func runDefaultCmd(_ *cobra.Command, args []string) int {
|
|||
}
|
||||
|
||||
userImageStr := args[0]
|
||||
scope, cleanup, err := imgbom.NewScope(userImageStr, appConfig.ScopeOpt)
|
||||
|
||||
provider, err := vulnscan.LoadVulnerabilityDb(appConfig.Db.ToCuratorConfig(), appConfig.Db.AutoUpdate)
|
||||
if err != nil {
|
||||
log.Errorf("could not produce catalog: %w", err)
|
||||
return 1
|
||||
return fmt.Errorf("failed to load vulnerability db: %w", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
log.Info("creating catalog")
|
||||
catalog, err := imgbom.Catalog(scope)
|
||||
results, catalog, _, err := vulnscan.FindVulnerabilities(provider, userImageStr, appConfig.ScopeOpt)
|
||||
if err != nil {
|
||||
log.Errorf("could not produce catalog: %w", err)
|
||||
return fmt.Errorf("failed to find vulnerabilities: %w", err)
|
||||
}
|
||||
|
||||
osObj := _distro.Identify(scope)
|
||||
|
||||
dbCurator, err := db.NewCurator(appConfig.Db.ToCuratorConfig())
|
||||
if err != nil {
|
||||
log.Errorf("could not curate database: %+v", err)
|
||||
return 1
|
||||
if err = presenter.GetPresenter(appConfig.PresenterOpt).Present(os.Stdout, catalog, results); err != nil {
|
||||
return fmt.Errorf("could not format catalog results: %w", err)
|
||||
}
|
||||
|
||||
if appConfig.Db.UpdateOnStartup {
|
||||
updateAvailable, updateEntry, err := dbCurator.IsUpdateAvailable()
|
||||
if err != nil {
|
||||
// TODO: should this be so fatal? we can certainly continue with a warning...
|
||||
log.Errorf("unable to check for vulnerability database update: %+v", err)
|
||||
return 1
|
||||
}
|
||||
if updateAvailable {
|
||||
err = dbCurator.UpdateTo(updateEntry)
|
||||
if err != nil {
|
||||
log.Errorf("unable to update vulnerability database: %+v", err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
store, err := dbCurator.GetStore()
|
||||
if err != nil {
|
||||
log.Errorf("failed to load vulnerability database: %+v", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
provider := vulnerability.NewProviderFromStore(store)
|
||||
|
||||
results := vulnscan.FindAllVulnerabilities(provider, *osObj, catalog)
|
||||
|
||||
outputOption := viper.GetString("output")
|
||||
|
||||
presenterType := presenter.ParseOption(outputOption)
|
||||
if presenterType == presenter.UnknownPresenter {
|
||||
log.Errorf("cannot find an output presenter for option: %s", outputOption)
|
||||
return 1
|
||||
}
|
||||
|
||||
err = presenter.GetPresenter(presenterType).Present(os.Stdout, catalog, results)
|
||||
if err != nil {
|
||||
log.Errorf("could not format catalog results: %+v", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/adrg/xdg v0.2.1
|
||||
github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/imgbom v0.0.0-20200717175414-b5a353349f55
|
||||
github.com/anchore/imgbom v0.0.0-20200717191633-9e285fd0e27a
|
||||
github.com/anchore/siren-db v0.0.0-20200716152335-9bc4580f72a1
|
||||
github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639
|
||||
github.com/facebookincubator/nvdtools v0.1.4-0.20200622182922-aed862a62ae6
|
||||
|
|
6
go.sum
6
go.sum
|
@ -113,12 +113,10 @@ github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db h1:LWKezJnFTF
|
|||
github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db/go.mod h1:D3rc2L/q4Hcp9eeX6AIJH4Q+kPjOtJCFhG9za90j+nU=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/imgbom v0.0.0-20200713170720-e8d11eec6992 h1:ERVRoY8sKpccEbuV53NyG/frJzIZ4n4NyOhbSGGOMSs=
|
||||
github.com/anchore/imgbom v0.0.0-20200713170720-e8d11eec6992/go.mod h1:b7euhNKBz5ReqVtal47okqWXg4YPT2/aitoWyQsDFns=
|
||||
github.com/anchore/imgbom v0.0.0-20200717123122-f03f7b32e93d h1:P8S+0tRUst2BZoTHrrsZ9QmPXtCdx9tyW7fksrZqYik=
|
||||
github.com/anchore/imgbom v0.0.0-20200717123122-f03f7b32e93d/go.mod h1:b7euhNKBz5ReqVtal47okqWXg4YPT2/aitoWyQsDFns=
|
||||
github.com/anchore/imgbom v0.0.0-20200717175414-b5a353349f55 h1:5PeLVtjW8yGRGC3MkrLwV1LqOdhawGKiWpG1Sk/PJhA=
|
||||
github.com/anchore/imgbom v0.0.0-20200717175414-b5a353349f55/go.mod h1:b7euhNKBz5ReqVtal47okqWXg4YPT2/aitoWyQsDFns=
|
||||
github.com/anchore/imgbom v0.0.0-20200717191633-9e285fd0e27a h1:JW8PTvx051Vc1LoNNxZgOYckRWox4RHNUOOw18uZYLs=
|
||||
github.com/anchore/imgbom v0.0.0-20200717191633-9e285fd0e27a/go.mod h1:b7euhNKBz5ReqVtal47okqWXg4YPT2/aitoWyQsDFns=
|
||||
github.com/anchore/siren-db v0.0.0-20200716152335-9bc4580f72a1 h1:0EorIdCoVGD/Nv6zNfXduCubAixzZ/0VH6PGrK8xKug=
|
||||
github.com/anchore/siren-db v0.0.0-20200716152335-9bc4580f72a1/go.mod h1:kw/8/5C2Shyk5TzyaLZvwABulWJNtJbFo6FaQzeQEs0=
|
||||
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e h1:QBwtrM0MXi0z+GcHk3RoSyzaQ+CLgas0bC/uOd1P+PQ=
|
||||
|
|
72
integration/db_mock_test.go
Normal file
72
integration/db_mock_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"github.com/anchore/siren-db/pkg/db"
|
||||
)
|
||||
|
||||
// integrity check
|
||||
var _ db.VulnerabilityStoreReader = &mockStore{}
|
||||
|
||||
type mockStore struct {
|
||||
backend map[string]map[string][]db.Vulnerability
|
||||
}
|
||||
|
||||
func NewMockDbStore() *mockStore {
|
||||
return &mockStore{
|
||||
backend: map[string]map[string][]db.Vulnerability{
|
||||
"github:python": {
|
||||
"Pygments": []db.Vulnerability{
|
||||
{
|
||||
ID: "CVE-python-pygments",
|
||||
VersionConstraint: "< 2.6.2",
|
||||
VersionFormat: "semver",
|
||||
},
|
||||
},
|
||||
},
|
||||
"github:gem": {
|
||||
"rails": []db.Vulnerability{
|
||||
{
|
||||
ID: "CVE-ruby-activerecord",
|
||||
VersionConstraint: "> 4.0.0, <= 4.1.1",
|
||||
VersionFormat: "semver",
|
||||
},
|
||||
},
|
||||
},
|
||||
"github:java": {
|
||||
"org.anchore:example-java-app-maven": []db.Vulnerability{
|
||||
{
|
||||
ID: "CVE-java-example-java-app",
|
||||
VersionConstraint: ">= 0.0.1, < 1.2.0",
|
||||
VersionFormat: "unknown",
|
||||
},
|
||||
},
|
||||
},
|
||||
"debian:8": {
|
||||
"apt-dev": []db.Vulnerability{
|
||||
{
|
||||
ID: "CVE-dpkg-apt",
|
||||
VersionConstraint: "<= 1.8.2",
|
||||
VersionFormat: "dpkg",
|
||||
},
|
||||
},
|
||||
},
|
||||
"rhel:8": {
|
||||
"dive": []db.Vulnerability{
|
||||
{
|
||||
ID: "CVE-rpmdb-dive",
|
||||
VersionConstraint: "<= 1.0.42",
|
||||
VersionFormat: "rpm",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *mockStore) GetVulnerability(namespace, name string) ([]db.Vulnerability, error) {
|
||||
namespaceMap := s.backend[namespace]
|
||||
if namespaceMap == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return namespaceMap[name], nil
|
||||
}
|
240
integration/match_coverage_test.go
Normal file
240
integration/match_coverage_test.go
Normal file
|
@ -0,0 +1,240 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"github.com/anchore/go-testutils"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/imgbom/imgbom/scope"
|
||||
"github.com/anchore/vulnscan/internal"
|
||||
"github.com/anchore/vulnscan/vulnscan"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
"github.com/anchore/vulnscan/vulnscan/result"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getPackagesByPath(t *testing.T, theScope scope.Scope, catalog *pkg.Catalog, thePath string) []*pkg.Package {
|
||||
t.Helper()
|
||||
refs, err := theScope.FilesByGlob(thePath)
|
||||
if err != nil {
|
||||
t.Fatalf("could not get ref by path %q: %+v", thePath, err)
|
||||
}
|
||||
if len(refs) != 1 {
|
||||
t.Fatalf("unexpected paths for %q: %+v", thePath, refs)
|
||||
}
|
||||
return catalog.PackagesByFile(refs[0])
|
||||
}
|
||||
|
||||
func addPythonMatches(t *testing.T, theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore, theResult *result.Result) {
|
||||
packages := getPackagesByPath(t, theScope, catalog, "/python/dist-info/METADATA")
|
||||
if len(packages) != 1 {
|
||||
t.Logf("Python Packages: %+v", packages)
|
||||
t.Fatalf("problem with upstream imgbom cataloger (python)")
|
||||
}
|
||||
thePkg := packages[0]
|
||||
theVuln := theStore.backend["github:python"][thePkg.Name][0]
|
||||
vulnObj, err := vulnerability.NewVulnerability(theVuln)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create vuln obj: %+v", err)
|
||||
}
|
||||
theResult.Add(thePkg, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0,
|
||||
Vulnerability: *vulnObj,
|
||||
Package: thePkg,
|
||||
SearchKey: "language[python] constraint[< 2.6.2 (semver)]",
|
||||
IndirectPackage: nil,
|
||||
Matcher: match.PythonMatcher,
|
||||
})
|
||||
}
|
||||
|
||||
func addRubyMatches(t *testing.T, theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore, theResult *result.Result) {
|
||||
packages := getPackagesByPath(t, theScope, catalog, "/ruby/Gemfile.lock")
|
||||
if len(packages) != 1 {
|
||||
t.Logf("Ruby Packages: %+v", packages)
|
||||
t.Fatalf("problem with upstream imgbom cataloger (ruby)")
|
||||
}
|
||||
thePkg := packages[0]
|
||||
theVuln := theStore.backend["github:gem"][thePkg.Name][0]
|
||||
vulnObj, err := vulnerability.NewVulnerability(theVuln)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create vuln obj: %+v", err)
|
||||
}
|
||||
theResult.Add(thePkg, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0,
|
||||
Vulnerability: *vulnObj,
|
||||
Package: thePkg,
|
||||
SearchKey: "language[ruby] constraint[> 4.0.0, <= 4.1.1 (semver)]",
|
||||
IndirectPackage: nil,
|
||||
Matcher: match.RubyBundleMatcher,
|
||||
})
|
||||
}
|
||||
|
||||
func addJavaMatches(t *testing.T, theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore, theResult *result.Result) {
|
||||
packages := make([]*pkg.Package, 0)
|
||||
for p := range catalog.Enumerate(pkg.JavaPkg) {
|
||||
packages = append(packages, p)
|
||||
}
|
||||
if len(packages) != 1 {
|
||||
t.Logf("Java Packages: %+v", packages)
|
||||
t.Fatalf("problem with upstream imgbom cataloger (java)")
|
||||
}
|
||||
thePkg := packages[0]
|
||||
|
||||
groupId := thePkg.Metadata.(pkg.JavaMetadata).PomProperties.GroupID
|
||||
lookup := groupId + ":" + thePkg.Name
|
||||
|
||||
theVuln := theStore.backend["github:java"][lookup][0]
|
||||
vulnObj, err := vulnerability.NewVulnerability(theVuln)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create vuln obj: %+v", err)
|
||||
}
|
||||
theResult.Add(thePkg, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0,
|
||||
Vulnerability: *vulnObj,
|
||||
Package: thePkg,
|
||||
SearchKey: "language[java] constraint[>= 0.0.1, < 1.2.0 (unknown)]",
|
||||
IndirectPackage: nil,
|
||||
Matcher: match.JavaMatcher,
|
||||
})
|
||||
}
|
||||
|
||||
func addDpkgMatches(t *testing.T, theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore, theResult *result.Result) {
|
||||
packages := getPackagesByPath(t, theScope, catalog, "/var/lib/dpkg/status")
|
||||
if len(packages) != 1 {
|
||||
t.Logf("Dpkg Packages: %+v", packages)
|
||||
t.Fatalf("problem with upstream imgbom cataloger (dpkg)")
|
||||
}
|
||||
thePkg := packages[0]
|
||||
// NOTE: this is an indirect match, in typical debian style
|
||||
theVuln := theStore.backend["debian:8"][thePkg.Name+"-dev"][0]
|
||||
vulnObj, err := vulnerability.NewVulnerability(theVuln)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create vuln obj: %+v", err)
|
||||
}
|
||||
theResult.Add(thePkg, match.Match{
|
||||
Type: match.ExactIndirectMatch,
|
||||
Confidence: 1.0,
|
||||
Vulnerability: *vulnObj,
|
||||
Package: thePkg,
|
||||
SearchKey: "distro[debian 8] constraint[<= 1.8.2 (deb)]",
|
||||
IndirectPackage: nil,
|
||||
Matcher: match.DpkgMatcher,
|
||||
})
|
||||
}
|
||||
|
||||
func addRhelMatches(t *testing.T, theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore, theResult *result.Result) {
|
||||
packages := getPackagesByPath(t, theScope, catalog, "/var/lib/rpm/Packages")
|
||||
if len(packages) != 1 {
|
||||
t.Logf("RPMDB Packages: %+v", packages)
|
||||
t.Fatalf("problem with upstream imgbom cataloger (RPMDB)")
|
||||
}
|
||||
thePkg := packages[0]
|
||||
theVuln := theStore.backend["rhel:8"][thePkg.Name][0]
|
||||
vulnObj, err := vulnerability.NewVulnerability(theVuln)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create vuln obj: %+v", err)
|
||||
}
|
||||
theResult.Add(thePkg, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0,
|
||||
Vulnerability: *vulnObj,
|
||||
Package: thePkg,
|
||||
SearchKey: "distro[centos 8] constraint[<= 1.0.42 (rpm)]",
|
||||
IndirectPackage: nil,
|
||||
Matcher: match.RpmDBMatcher,
|
||||
})
|
||||
}
|
||||
|
||||
func TestPkgCoverageImage(t *testing.T) {
|
||||
|
||||
observedMatchers := internal.NewStringSet()
|
||||
definedMatchers := internal.NewStringSet()
|
||||
for _, l := range match.AllMatcherTypes {
|
||||
definedMatchers.Add(l.String())
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
fixtureImage string
|
||||
expectedFn func(scope.Scope, *pkg.Catalog, *mockStore) result.Result
|
||||
}{
|
||||
{
|
||||
fixtureImage: "image-debian-match-coverage",
|
||||
expectedFn: func(theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore) result.Result {
|
||||
expectedResults := result.NewResult()
|
||||
addPythonMatches(t, theScope, catalog, theStore, &expectedResults)
|
||||
addRubyMatches(t, theScope, catalog, theStore, &expectedResults)
|
||||
addJavaMatches(t, theScope, catalog, theStore, &expectedResults)
|
||||
addDpkgMatches(t, theScope, catalog, theStore, &expectedResults)
|
||||
return expectedResults
|
||||
},
|
||||
},
|
||||
{
|
||||
fixtureImage: "image-centos-match-coverage",
|
||||
expectedFn: func(theScope scope.Scope, catalog *pkg.Catalog, theStore *mockStore) result.Result {
|
||||
expectedResults := result.NewResult()
|
||||
addRhelMatches(t, theScope, catalog, theStore, &expectedResults)
|
||||
return expectedResults
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureImage, func(t *testing.T) {
|
||||
theStore := NewMockDbStore()
|
||||
|
||||
_, cleanup := testutils.GetFixtureImage(t, "docker-archive", test.fixtureImage)
|
||||
tarPath := testutils.GetFixtureImageTarPath(t, test.fixtureImage)
|
||||
defer cleanup()
|
||||
|
||||
actualResults, catalog, theScope, err := vulnscan.FindVulnerabilities(
|
||||
vulnerability.NewProviderFromStore(theStore),
|
||||
"docker-archive://"+tarPath,
|
||||
scope.AllLayersScope,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to find vulnerabilities: %+v", err)
|
||||
}
|
||||
|
||||
// build expected matches from what's discovered from the catalog
|
||||
expectedResults := test.expectedFn(*theScope, catalog, theStore)
|
||||
|
||||
// build expected match set...
|
||||
expectedMatchSet := internal.NewStringSet()
|
||||
expectedCount := 0
|
||||
for eMatch := range expectedResults.Enumerate() {
|
||||
expectedCount++
|
||||
// NOTE: this does not include all fields...
|
||||
expectedMatchSet.Add(eMatch.String())
|
||||
}
|
||||
|
||||
// ensure that all matches are covered
|
||||
actualCount := 0
|
||||
for aMatch := range actualResults.Enumerate() {
|
||||
actualCount++
|
||||
observedMatchers.Add(aMatch.Matcher.String())
|
||||
|
||||
if !expectedMatchSet.Contains(aMatch.String()) {
|
||||
// NOTE: this is not the same as comparing an expected sequence which would allow for detailed diffing
|
||||
t.Errorf("Disjoint Match: %+v", aMatch)
|
||||
}
|
||||
}
|
||||
|
||||
if expectedCount != actualCount {
|
||||
t.Errorf("expected %d matches but got %d matches", expectedCount, actualCount)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// ensure that integration test cases stay in sync with the implemented matchers
|
||||
|
||||
observedMatchers.Remove(match.UnknownMatcherType.String())
|
||||
definedMatchers.Remove(match.UnknownMatcherType.String())
|
||||
|
||||
if len(observedMatchers) != len(definedMatchers) {
|
||||
t.Errorf("matcher coverage incomplete (matchers=%d, coverage=%d)", len(definedMatchers), len(observedMatchers))
|
||||
}
|
||||
|
||||
}
|
1
integration/test-fixtures/.gitignore
vendored
Normal file
1
integration/test-fixtures/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
!**/image-*/Dockerfile
|
|
@ -0,0 +1,2 @@
|
|||
FROM scratch
|
||||
COPY . .
|
|
@ -0,0 +1,11 @@
|
|||
NAME="CentOS Linux"
|
||||
VERSION="8 (Core)"
|
||||
ID="centos"
|
||||
ID_LIKE="rhel fedora"
|
||||
VERSION_ID="8"
|
||||
PLATFORM_ID="platform:el8"
|
||||
PRETTY_NAME="CentOS Linux 8 (Core)"
|
||||
ANSI_COLOR="0;31"
|
||||
CPE_NAME="cpe:/o:centos:centos:8"
|
||||
HOME_URL="https://www.centos.org/"
|
||||
BUG_REPORT_URL="https://bugs.centos.org/"
|
Binary file not shown.
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
docker create --name generate-rpmdb-fixture centos:latest sh -c 'tail -f /dev/null'
|
||||
|
||||
function cleanup {
|
||||
docker kill generate-rpmdb-fixture
|
||||
docker rm generate-rpmdb-fixture
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
docker start generate-rpmdb-fixture
|
||||
docker exec -i --tty=false generate-rpmdb-fixture bash <<-EOF
|
||||
mkdir -p /scratch
|
||||
cd /scratch
|
||||
rpm --initdb --dbpath /scratch
|
||||
curl -sSLO https://github.com/wagoodman/dive/releases/download/v0.9.2/dive_0.9.2_linux_amd64.rpm
|
||||
rpm --dbpath /scratch -ivh dive_0.9.2_linux_amd64.rpm
|
||||
rm dive_0.9.2_linux_amd64.rpm
|
||||
rpm --dbpath /scratch -qa
|
||||
EOF
|
||||
|
||||
docker cp generate-rpmdb-fixture:/scratch/Packages .
|
|
@ -0,0 +1,2 @@
|
|||
FROM scratch
|
||||
COPY . .
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
See the imgbom/cataloger/java/test-fixtures/java-builds dir to generate test fixtures and copy to here manually.
|
|
@ -0,0 +1,47 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: Pygments
|
||||
Version: 2.6.1
|
||||
Summary: Pygments is a syntax highlighting package written in Python.
|
||||
Home-page: https://pygments.org/
|
||||
Author: Georg Brandl
|
||||
Author-email: georg@python.org
|
||||
License: BSD License
|
||||
Keywords: syntax highlighting
|
||||
Platform: any
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Intended Audience :: End Users/Desktop
|
||||
Classifier: Intended Audience :: System Administrators
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Text Processing :: Filters
|
||||
Classifier: Topic :: Utilities
|
||||
Requires-Python: >=3.5
|
||||
|
||||
|
||||
Pygments
|
||||
~~~~~~~~
|
||||
|
||||
Pygments is a syntax highlighting package written in Python.
|
||||
|
||||
It is a generic syntax highlighter suitable for use in code hosting, forums,
|
||||
wikis or other applications that need to prettify source code. Highlights
|
||||
are:
|
||||
|
||||
* a wide range of over 500 languages and other text formats is supported
|
||||
* special attention is paid to details, increasing quality by a fair amount
|
||||
* support for new languages and formats are added easily
|
||||
* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences
|
||||
* it is usable as a command-line tool and as a library
|
||||
|
||||
:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
rails (4.1.1)
|
||||
activerecord (= 4.1.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
rails (= 4.1.1)
|
|
@ -0,0 +1,8 @@
|
|||
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
||||
NAME="Debian GNU/Linux"
|
||||
VERSION_ID="8"
|
||||
VERSION="8 (jessie)"
|
||||
ID=debian
|
||||
HOME_URL="http://www.debian.org/"
|
||||
SUPPORT_URL="http://www.debian.org/support"
|
||||
BUG_REPORT_URL="https://bugs.debian.org/"
|
|
@ -0,0 +1,35 @@
|
|||
Package: apt
|
||||
Status: install ok installed
|
||||
Priority: required
|
||||
Section: admin
|
||||
Installed-Size: 4064
|
||||
Maintainer: APT Development Team <deity@lists.debian.org>
|
||||
Architecture: amd64
|
||||
Version: 1.8.2
|
||||
Source: apt-dev
|
||||
Replaces: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~)
|
||||
Provides: apt-transport-https (= 1.8.2)
|
||||
Depends: adduser, gpgv | gpgv2 | gpgv1, debian-archive-keyring, libapt-pkg5.0 (>= 1.7.0~alpha3~), libc6 (>= 2.15), libgcc1 (>= 1:3.0), libgnutls30 (>= 3.6.6), libseccomp2 (>= 1.0.1), libstdc++6 (>= 5.2)
|
||||
Recommends: ca-certificates
|
||||
Suggests: apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), gnupg | gnupg2 | gnupg1, powermgmt-base
|
||||
Breaks: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~), aptitude (<< 0.8.10)
|
||||
Conffiles:
|
||||
/etc/apt/apt.conf.d/01autoremove 76120d358bc9037bb6358e737b3050b5
|
||||
/etc/cron.daily/apt-compat 49e9b2cfa17849700d4db735d04244f3
|
||||
/etc/kernel/postinst.d/apt-auto-removal 4ad976a68f045517cf4696cec7b8aa3a
|
||||
/etc/logrotate.d/apt 179f2ed4f85cbaca12fa3d69c2a4a1c3
|
||||
Description: commandline package manager
|
||||
This package provides commandline tools for searching and
|
||||
managing as well as querying information about packages
|
||||
as a low-level access to all features of the libapt-pkg library.
|
||||
.
|
||||
These include:
|
||||
* apt-get for retrieval of packages and information about them
|
||||
from authenticated sources and for installation, upgrade and
|
||||
removal of packages together with their dependencies
|
||||
* apt-cache for querying available information about installed
|
||||
as well as installable packages
|
||||
* apt-cdrom to use removable media as a source for packages
|
||||
* apt-config as an interface to the configuration settings
|
||||
* apt-key as an interface to manage authentication keys
|
||||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/presenter"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/anchore/imgbom/imgbom/scope"
|
||||
"github.com/anchore/vulnscan/internal"
|
||||
|
@ -21,6 +23,8 @@ type CliOnlyOptions struct {
|
|||
|
||||
type Application struct {
|
||||
ConfigPath string
|
||||
PresenterOpt presenter.Option
|
||||
Output string `mapstructure:"output"`
|
||||
ScopeOpt scope.Option
|
||||
Scope string `mapstructure:"scope"`
|
||||
Quiet bool `mapstructure:"quiet"`
|
||||
|
@ -39,9 +43,9 @@ type Logging struct {
|
|||
}
|
||||
|
||||
type Database struct {
|
||||
Dir string `mapstructure:"cache-dir"`
|
||||
UpdateURL string `mapstructure:"update-url"`
|
||||
UpdateOnStartup bool `mapstructure:"update-on-startup"`
|
||||
Dir string `mapstructure:"cache-dir"`
|
||||
UpdateURL string `mapstructure:"update-url"`
|
||||
AutoUpdate bool `mapstructure:"auto-update"`
|
||||
}
|
||||
|
||||
type Development struct {
|
||||
|
@ -64,7 +68,7 @@ func setNonCliDefaultValues(v *viper.Viper) {
|
|||
// TODO: change me to the production URL before release
|
||||
v.SetDefault("db.update-url", "http://localhost:5000/listing.json")
|
||||
// TODO: set this to true before release
|
||||
v.SetDefault("db.update-on-startup", false)
|
||||
v.SetDefault("db.auto-update", false)
|
||||
v.SetDefault("dev.profile-cpu", false)
|
||||
v.SetDefault("check-for-app-update", true)
|
||||
}
|
||||
|
@ -96,6 +100,13 @@ func LoadConfigFromFile(v *viper.Viper, cliOpts *CliOnlyOptions) (*Application,
|
|||
}
|
||||
|
||||
func (cfg *Application) Build() error {
|
||||
// set the presenter
|
||||
presenterOption := presenter.ParseOption(cfg.Output)
|
||||
if presenterOption == presenter.UnknownPresenter {
|
||||
return fmt.Errorf("bad --output value '%s'", cfg.Output)
|
||||
}
|
||||
cfg.PresenterOpt = presenterOption
|
||||
|
||||
// set the scope
|
||||
scopeOption := scope.ParseOption(cfg.Scope)
|
||||
if scopeOption == scope.UnknownScope {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package vulnscan
|
||||
|
||||
// note: must be a single word, all lowercase
|
||||
const LibraryName = "vulnscan"
|
||||
const DbSchemaConstraint = ">= 1.0.0, < 2.0.0"
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/anchore/siren-db/pkg/store"
|
||||
"github.com/anchore/vulnscan/internal/file"
|
||||
"github.com/anchore/vulnscan/internal/log"
|
||||
"github.com/anchore/vulnscan/vulnscan"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
|
@ -33,9 +32,9 @@ type Curator struct {
|
|||
}
|
||||
|
||||
func NewCurator(cfg Config) (Curator, error) {
|
||||
constraint, err := version.NewConstraint(vulnscan.DbSchemaConstraint)
|
||||
constraint, err := version.NewConstraint(DbSchemaConstraint)
|
||||
if err != nil {
|
||||
return Curator{}, fmt.Errorf("unable to set DB curator version constraint (%s): %w", vulnscan.DbSchemaConstraint, err)
|
||||
return Curator{}, fmt.Errorf("unable to set DB curator version constraint (%s): %w", DbSchemaConstraint, err)
|
||||
}
|
||||
|
||||
return Curator{
|
||||
|
@ -74,7 +73,7 @@ func (c *Curator) Status() Status {
|
|||
return Status{
|
||||
Age: metadata.Built,
|
||||
SchemaVersion: metadata.Version.String(),
|
||||
SchemaConstraint: vulnscan.DbSchemaConstraint,
|
||||
SchemaConstraint: DbSchemaConstraint,
|
||||
Location: c.config.DbDir,
|
||||
Err: err,
|
||||
}
|
||||
|
|
3
vulnscan/db/schema_constraint.go
Normal file
3
vulnscan/db/schema_constraint.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package db
|
||||
|
||||
const DbSchemaConstraint = ">= 1.0.0, < 2.0.0"
|
|
@ -1,25 +0,0 @@
|
|||
package vulnscan
|
||||
|
||||
import (
|
||||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/matcher"
|
||||
"github.com/anchore/vulnscan/vulnscan/result"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindAllVulnerabilities(store vulnerability.Provider, o distro.Distro, catalog *pkg.Catalog) result.Result {
|
||||
res := result.NewResult()
|
||||
for p := range catalog.Enumerate() {
|
||||
res.Merge(FindVulnerabilities(store, o, p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func FindVulnerabilities(store vulnerability.Provider, o distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
res := result.NewResult()
|
||||
for _, p := range packages {
|
||||
res.Merge(matcher.FindMatches(store, o, p))
|
||||
}
|
||||
return res
|
||||
}
|
79
vulnscan/lib.go
Normal file
79
vulnscan/lib.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package vulnscan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/db"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/logger"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom"
|
||||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/imgbom/imgbom/scope"
|
||||
"github.com/anchore/vulnscan/internal/log"
|
||||
"github.com/anchore/vulnscan/vulnscan/matcher"
|
||||
"github.com/anchore/vulnscan/vulnscan/result"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
// note: lib name must be a single word, all lowercase
|
||||
const LibraryName = "vulnscan"
|
||||
|
||||
func FindVulnerabilities(provider vulnerability.Provider, userImageStr string, scopeOpt scope.Option) (result.Result, *pkg.Catalog, *scope.Scope, error) {
|
||||
log.Info("Cataloging image")
|
||||
catalog, theScope, theDistro, err := imgbom.Catalog(userImageStr, scopeOpt)
|
||||
if err != nil {
|
||||
return result.Result{}, nil, nil, err
|
||||
}
|
||||
|
||||
return FindVulnerabilitiesForCatalog(provider, *theDistro, catalog), catalog, theScope, nil
|
||||
}
|
||||
|
||||
func FindVulnerabilitiesForCatalog(provider vulnerability.Provider, d distro.Distro, catalog *pkg.Catalog) result.Result {
|
||||
res := result.NewResult()
|
||||
for p := range catalog.Enumerate() {
|
||||
res.Merge(FindVulnerabilitiesForPackage(provider, d, p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func FindVulnerabilitiesForPackage(provider vulnerability.Provider, d distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
res := result.NewResult()
|
||||
for _, p := range packages {
|
||||
res.Merge(matcher.FindMatches(provider, d, p))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func LoadVulnerabilityDb(cfg db.Config, update bool) (vulnerability.Provider, error) {
|
||||
dbCurator, err := db.NewCurator(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not curate database: %w", err)
|
||||
}
|
||||
|
||||
if update {
|
||||
updateAvailable, updateEntry, err := dbCurator.IsUpdateAvailable()
|
||||
if err != nil {
|
||||
// TODO: should this be so fatal? we can certainly continue with a warning...
|
||||
return nil, fmt.Errorf("unable to check for vulnerability database update: %w", err)
|
||||
}
|
||||
if updateAvailable {
|
||||
err = dbCurator.UpdateTo(updateEntry)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to update vulnerability database: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
store, err := dbCurator.GetStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vulnerability.NewProviderFromStore(store), nil
|
||||
}
|
||||
|
||||
func SetLogger(logger logger.Logger) {
|
||||
log.Log = logger
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package vulnscan
|
||||
|
||||
import (
|
||||
"github.com/anchore/vulnscan/internal/log"
|
||||
"github.com/anchore/vulnscan/vulnscan/logger"
|
||||
)
|
||||
|
||||
func SetLogger(logger logger.Logger) {
|
||||
log.Log = logger
|
||||
}
|
|
@ -16,13 +16,13 @@ type Match struct {
|
|||
// TODO: is this a good name for what it represents? (which is an audit trail of HOW we got this match from the store)
|
||||
SearchKey string
|
||||
IndirectPackage *pkg.Package
|
||||
Matcher string
|
||||
Matcher MatcherType
|
||||
}
|
||||
|
||||
func (m Match) String() string {
|
||||
return fmt.Sprintf("Match(pkg=%s vuln=%s confidence=%f type='%s' key='%s' foundBy='%s')", m.Package, m.Vulnerability.String(), m.Confidence, m.Type, m.SearchKey, m.Matcher)
|
||||
return fmt.Sprintf("Match(pkg=%s vuln=%s type='%s' key='%s' foundBy='%s')", m.Package, m.Vulnerability.String(), m.Type, m.SearchKey, m.Matcher)
|
||||
}
|
||||
|
||||
func (m Match) Summary() string {
|
||||
return fmt.Sprintf("vuln='%s' confidence=%0.2f type='%s' key='%s' foundBy='%s')", m.Vulnerability.ID, m.Confidence, m.Type, m.SearchKey, m.Matcher)
|
||||
return fmt.Sprintf("vuln='%s' type='%s' key='%s' foundBy='%s')", m.Vulnerability.ID, m.Type, m.SearchKey, m.Matcher)
|
||||
}
|
||||
|
|
37
vulnscan/match/matcher_type.go
Normal file
37
vulnscan/match/matcher_type.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package match
|
||||
|
||||
const (
|
||||
UnknownMatcherType MatcherType = iota
|
||||
RubyBundleMatcher
|
||||
DpkgMatcher
|
||||
RpmDBMatcher
|
||||
JavaMatcher
|
||||
PythonMatcher
|
||||
)
|
||||
|
||||
var matcherTypeStr = []string{
|
||||
"UnknownMatcherType",
|
||||
"ruby-bundle-matcher",
|
||||
"dpkg-matcher",
|
||||
"rpmdb-matcher",
|
||||
"java-matcher",
|
||||
"python-matcher",
|
||||
}
|
||||
|
||||
var AllMatcherTypes = []MatcherType{
|
||||
RubyBundleMatcher,
|
||||
DpkgMatcher,
|
||||
RpmDBMatcher,
|
||||
JavaMatcher,
|
||||
PythonMatcher,
|
||||
}
|
||||
|
||||
type MatcherType int
|
||||
|
||||
func (f MatcherType) String() string {
|
||||
if int(f) >= len(matcherTypeStr) || f < 0 {
|
||||
return matcherTypeStr[0]
|
||||
}
|
||||
|
||||
return matcherTypeStr[f]
|
||||
}
|
|
@ -9,8 +9,6 @@ const (
|
|||
FuzzyMatch
|
||||
)
|
||||
|
||||
type Type int
|
||||
|
||||
var typeStr = []string{
|
||||
"UnknownMatchType",
|
||||
"Exact-Direct Match",
|
||||
|
@ -18,6 +16,8 @@ var typeStr = []string{
|
|||
"Fuzzy Match",
|
||||
}
|
||||
|
||||
type Type int
|
||||
|
||||
func ParseType(userStr string) Type {
|
||||
switch strings.ToLower(userStr) {
|
||||
case strings.ToLower(ExactDirectMatch.String()):
|
||||
|
|
|
@ -11,23 +11,23 @@ import (
|
|||
type Matcher struct {
|
||||
}
|
||||
|
||||
func (m *Matcher) Types() []pkg.Type {
|
||||
func (m *Matcher) PackageTypes() []pkg.Type {
|
||||
return []pkg.Type{pkg.BundlerPkg}
|
||||
}
|
||||
|
||||
func (m *Matcher) Name() string {
|
||||
return "bundler-matcher"
|
||||
func (m *Matcher) Type() match.MatcherType {
|
||||
return match.RubyBundleMatcher
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, _ distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
var matches = make([]match.Match, 0)
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, langMatches...)
|
||||
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Name())
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindMatchesByPackageCPE(store vulnerability.ProviderByCPE, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
func FindMatchesByPackageCPE(store vulnerability.ProviderByCPE, p *pkg.Package, upstreamMatcher match.MatcherType) ([]match.Match, error) {
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
|
@ -42,7 +42,7 @@ func FindMatchesByPackageCPE(store vulnerability.ProviderByCPE, p *pkg.Package,
|
|||
Confidence: 0.9, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
Matcher: upstreamMatcher,
|
||||
SearchKey: fmt.Sprintf("cpe[%s] constraint[%s]", cpe.BindToFmtString(), vuln.Constraint.String()),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ func TestFindMatchesByPackageCPE(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
store := newMockProviderByCPE()
|
||||
actual, err := FindMatchesByPackageCPE(store, &test.p, "SOME_OTHER_MATCHER")
|
||||
actual, err := FindMatchesByPackageCPE(store, &test.p, match.PythonMatcher)
|
||||
if err != nil {
|
||||
t.Fatalf("error while finding matches: %+v", err)
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ func TestFindMatchesByPackageCPE(t *testing.T) {
|
|||
t.Errorf("failed to capture correct original package: %s", a.Package.Name)
|
||||
}
|
||||
|
||||
if a.Matcher != "SOME_OTHER_MATCHER" {
|
||||
if a.Matcher != match.PythonMatcher {
|
||||
t.Errorf("failed to capture matcher name: %s", a.Matcher)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindMatchesByPackageDistro(store vulnerability.ProviderByDistro, d distro.Distro, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
func FindMatchesByPackageDistro(store vulnerability.ProviderByDistro, d distro.Distro, p *pkg.Package, upstreamMatcher match.MatcherType) ([]match.Match, error) {
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
|
@ -37,7 +37,7 @@ func FindMatchesByPackageDistro(store vulnerability.ProviderByDistro, d distro.D
|
|||
Confidence: 1.0, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
Matcher: upstreamMatcher,
|
||||
SearchKey: fmt.Sprintf("distro[%s] constraint[%s]", d, vuln.Constraint.String()),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ func TestFindMatchesByPackageDistro(t *testing.T) {
|
|||
}
|
||||
|
||||
store := newMockProviderByDistro()
|
||||
actual, err := FindMatchesByPackageDistro(store, d, &p, "SOME_OTHER_MATCHER")
|
||||
actual, err := FindMatchesByPackageDistro(store, d, &p, match.PythonMatcher)
|
||||
if err != nil {
|
||||
t.Fatalf("error while finding matches: %+v", err)
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func TestFindMatchesByPackageDistro(t *testing.T) {
|
|||
t.Errorf("failed to capture correct original package: %s", a.Package.Name)
|
||||
}
|
||||
|
||||
if a.Matcher != "SOME_OTHER_MATCHER" {
|
||||
if a.Matcher != match.PythonMatcher {
|
||||
t.Errorf("failed to capture matcher name: %s", a.Matcher)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindMatchesByPackageLanguage(store vulnerability.ProviderByLanguage, l pkg.Language, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
func FindMatchesByPackageLanguage(store vulnerability.ProviderByLanguage, l pkg.Language, p *pkg.Package, upstreamMatcher match.MatcherType) ([]match.Match, error) {
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
|
@ -36,7 +36,7 @@ func FindMatchesByPackageLanguage(store vulnerability.ProviderByLanguage, l pkg.
|
|||
Confidence: 1.0, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
Matcher: upstreamMatcher,
|
||||
SearchKey: fmt.Sprintf("language[%s] constraint[%s]", l, vuln.Constraint.String()),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func TestFindMatchesByPackageLanguage(t *testing.T) {
|
|||
}
|
||||
|
||||
store := newMockProviderByLanguage()
|
||||
actual, err := FindMatchesByPackageLanguage(store, p.Language, &p, "SOME_OTHER_MATCHER")
|
||||
actual, err := FindMatchesByPackageLanguage(store, p.Language, &p, match.PythonMatcher)
|
||||
if err != nil {
|
||||
t.Fatalf("error while finding matches: %+v", err)
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ func TestFindMatchesByPackageLanguage(t *testing.T) {
|
|||
t.Errorf("failed to capture correct original package: %s", a.Package.Name)
|
||||
}
|
||||
|
||||
if a.Matcher != "SOME_OTHER_MATCHER" {
|
||||
if a.Matcher != match.PythonMatcher {
|
||||
t.Errorf("failed to capture matcher name: %s", a.Matcher)
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ func newController() controller {
|
|||
|
||||
func (c *controller) add(matchers ...Matcher) {
|
||||
for _, m := range matchers {
|
||||
for _, t := range m.Types() {
|
||||
for _, t := range m.PackageTypes() {
|
||||
if _, ok := c.matchers[t]; ok {
|
||||
c.matchers[t] = make([]Matcher, 0)
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func (c *controller) add(matchers ...Matcher) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *controller) findMatches(s vulnerability.Provider, o distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
func (c *controller) findMatches(provider vulnerability.Provider, d distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
res := result.NewResult()
|
||||
for _, p := range packages {
|
||||
log.Debugf("searching for vulnerability matches for pkg=%s", p)
|
||||
|
@ -59,7 +59,7 @@ func (c *controller) findMatches(s vulnerability.Provider, o distro.Distro, pack
|
|||
log.Errorf("no matchers available for package pkg=%s", p)
|
||||
}
|
||||
for _, m := range matchers {
|
||||
matches, err := m.Match(s, o, p)
|
||||
matches, err := m.Match(provider, d, p)
|
||||
if err != nil {
|
||||
log.Errorf("matcher failed for pkg=%s: %+v", p, err)
|
||||
} else {
|
||||
|
@ -71,8 +71,8 @@ func (c *controller) findMatches(s vulnerability.Provider, o distro.Distro, pack
|
|||
return res
|
||||
}
|
||||
|
||||
func FindMatches(s vulnerability.Provider, o distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
return controllerInstance.findMatches(s, o, packages...)
|
||||
func FindMatches(provider vulnerability.Provider, d distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
return controllerInstance.findMatches(provider, d, packages...)
|
||||
}
|
||||
|
||||
func logMatches(p *pkg.Package, matches []match.Match) {
|
||||
|
|
|
@ -14,12 +14,12 @@ import (
|
|||
type Matcher struct {
|
||||
}
|
||||
|
||||
func (m *Matcher) Types() []pkg.Type {
|
||||
func (m *Matcher) PackageTypes() []pkg.Type {
|
||||
return []pkg.Type{pkg.DebPkg}
|
||||
}
|
||||
|
||||
func (m *Matcher) Name() string {
|
||||
return "dpkg-matcher"
|
||||
func (m *Matcher) Type() match.MatcherType {
|
||||
return match.DpkgMatcher
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, d distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
|
@ -31,7 +31,7 @@ func (m *Matcher) Match(store vulnerability.Provider, d distro.Distro, p *pkg.Pa
|
|||
}
|
||||
matches = append(matches, sourceMatches...)
|
||||
|
||||
exactMatches, err := common.FindMatchesByPackageDistro(store, d, p, m.Name())
|
||||
exactMatches, err := common.FindMatchesByPackageDistro(store, d, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to match by exact package name: %w", err)
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func (m *Matcher) matchBySourceIndirection(store vulnerability.ProviderByDistro,
|
|||
// use the source package name
|
||||
indirectPackage.Name = sourcePkgName
|
||||
|
||||
matches, err := common.FindMatchesByPackageDistro(store, d, &indirectPackage, m.Name())
|
||||
matches, err := common.FindMatchesByPackageDistro(store, d, &indirectPackage, m.Type())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find vulnerabilities by dkpg source indirection: %w", err)
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func (m *Matcher) matchBySourceIndirection(store vulnerability.ProviderByDistro,
|
|||
matches[idx].Type = match.ExactIndirectMatch
|
||||
matches[idx].Package = p
|
||||
matches[idx].IndirectPackage = &indirectPackage
|
||||
matches[idx].Matcher = m.Name()
|
||||
matches[idx].Matcher = m.Type()
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
|
|
|
@ -45,8 +45,8 @@ func TestMatcherDpkg_matchBySourceIndirection(t *testing.T) {
|
|||
t.Errorf("failed to capture correct original package: %s", a.Package.Name)
|
||||
}
|
||||
|
||||
if a.Matcher != matcher.Name() {
|
||||
t.Errorf("failed to capture matcher name: %s", a.Matcher)
|
||||
if a.Matcher != matcher.Type() {
|
||||
t.Errorf("failed to capture matcher type: %s", a.Matcher)
|
||||
}
|
||||
|
||||
if a.IndirectPackage == nil {
|
||||
|
|
|
@ -11,23 +11,23 @@ import (
|
|||
type Matcher struct {
|
||||
}
|
||||
|
||||
func (m *Matcher) Types() []pkg.Type {
|
||||
func (m *Matcher) PackageTypes() []pkg.Type {
|
||||
return []pkg.Type{pkg.JavaPkg, pkg.JenkinsPluginPkg}
|
||||
}
|
||||
|
||||
func (m *Matcher) Name() string {
|
||||
return "java-matcher"
|
||||
func (m *Matcher) Type() match.MatcherType {
|
||||
return match.JavaMatcher
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, _ distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
var matches = make([]match.Match, 0)
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, langMatches...)
|
||||
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Name())
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
)
|
||||
|
||||
type Matcher interface {
|
||||
Types() []pkg.Type
|
||||
PackageTypes() []pkg.Type
|
||||
Type() match.MatcherType
|
||||
Match(vulnerability.Provider, distro.Distro, *pkg.Package) ([]match.Match, error)
|
||||
}
|
||||
|
|
|
@ -11,23 +11,23 @@ import (
|
|||
type Matcher struct {
|
||||
}
|
||||
|
||||
func (m *Matcher) Types() []pkg.Type {
|
||||
func (m *Matcher) PackageTypes() []pkg.Type {
|
||||
return []pkg.Type{pkg.EggPkg, pkg.WheelPkg}
|
||||
}
|
||||
|
||||
func (m *Matcher) Name() string {
|
||||
return "python-matcher"
|
||||
func (m *Matcher) Type() match.MatcherType {
|
||||
return match.PythonMatcher
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, _ distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
var matches = make([]match.Match, 0)
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, langMatches...)
|
||||
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Name())
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@ import (
|
|||
type Matcher struct {
|
||||
}
|
||||
|
||||
func (m *Matcher) Types() []pkg.Type {
|
||||
func (m *Matcher) PackageTypes() []pkg.Type {
|
||||
return []pkg.Type{pkg.RpmPkg}
|
||||
}
|
||||
|
||||
func (m *Matcher) Name() string {
|
||||
return "rpmdb-matcher"
|
||||
func (m *Matcher) Type() match.MatcherType {
|
||||
return match.RpmDBMatcher
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, d distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
return common.FindMatchesByPackageDistro(store, d, p, m.Name())
|
||||
return common.FindMatchesByPackageDistro(store, d, p, m.Type())
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func (pres *Presenter) Present(output io.Writer, catalog *pkg.Catalog, results r
|
|||
ResultObj{
|
||||
Cve: match.Vulnerability.ID,
|
||||
FoundBy: FoundBy{
|
||||
Matcher: match.Matcher,
|
||||
Matcher: match.Matcher.String(),
|
||||
SearchKey: match.SearchKey,
|
||||
},
|
||||
Package: Package{Name: p.Name, Version: p.Version, Type: p.Type.String()},
|
||||
|
|
|
@ -36,12 +36,14 @@ func TestJsonPresenter(t *testing.T) {
|
|||
Type: match.ExactDirectMatch,
|
||||
Vulnerability: vulnerability.Vulnerability{ID: "CVE-1999-0001"},
|
||||
Package: &pkg1,
|
||||
Matcher: match.DpkgMatcher,
|
||||
}
|
||||
|
||||
var match2 = match.Match{
|
||||
Type: match.ExactIndirectMatch,
|
||||
Vulnerability: vulnerability.Vulnerability{ID: "CVE-1999-0002"},
|
||||
Package: &pkg1,
|
||||
Matcher: match.DpkgMatcher,
|
||||
}
|
||||
|
||||
results := result.NewResult()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
"cve": "CVE-1999-0001",
|
||||
"found-by": {
|
||||
"matcher": "",
|
||||
"matcher": "dpkg-matcher",
|
||||
"search-key": ""
|
||||
},
|
||||
"package": {
|
||||
|
@ -14,7 +14,7 @@
|
|||
{
|
||||
"cve": "CVE-1999-0002",
|
||||
"found-by": {
|
||||
"matcher": "",
|
||||
"matcher": "dpkg-matcher",
|
||||
"search-key": ""
|
||||
},
|
||||
"package": {
|
||||
|
|
|
@ -46,8 +46,8 @@ func (r *Result) Enumerate() <-chan match.Match {
|
|||
go func() {
|
||||
defer close(channel)
|
||||
for _, matches := range r.byPackage {
|
||||
for _, match := range matches {
|
||||
channel <- match
|
||||
for _, m := range matches {
|
||||
channel <- m
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -7,14 +7,11 @@ import (
|
|||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/siren-db/pkg/db"
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
"github.com/facebookincubator/nvdtools/wfn"
|
||||
)
|
||||
|
||||
type StoreProvider struct {
|
||||
store db.VulnerabilityStoreReader
|
||||
// TODO: allows the ability to have a db cache for keeping rich objects around
|
||||
// or to have a sync.Pool of warm objects
|
||||
}
|
||||
|
||||
func NewProviderFromStore(store db.VulnerabilityStoreReader) *StoreProvider {
|
||||
|
@ -33,17 +30,12 @@ func (pr *StoreProvider) GetByDistro(d distro.Distro, p *pkg.Package) ([]*Vulner
|
|||
}
|
||||
|
||||
for _, vuln := range allPkgVulns {
|
||||
format := version.ParseFormat(vuln.VersionFormat)
|
||||
|
||||
constraint, err := version.GetConstraint(vuln.VersionConstraint, format)
|
||||
vulnObj, err := NewVulnerability(vuln)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to parse distro='%s' constraint='%s' format='%s': %w", d, vuln.VersionConstraint, format, err)
|
||||
return nil, fmt.Errorf("provider failed to parse distro='%s': %w", d, err)
|
||||
}
|
||||
|
||||
vulns = append(vulns, &Vulnerability{
|
||||
Constraint: constraint,
|
||||
ID: vuln.ID,
|
||||
})
|
||||
vulns = append(vulns, vulnObj)
|
||||
}
|
||||
|
||||
return vulns, nil
|
||||
|
@ -65,17 +57,12 @@ func (pr *StoreProvider) GetByLanguage(l pkg.Language, p *pkg.Package) ([]*Vulne
|
|||
}
|
||||
|
||||
for _, vuln := range allPkgVulns {
|
||||
format := version.ParseFormat(vuln.VersionFormat)
|
||||
|
||||
constraint, err := version.GetConstraint(vuln.VersionConstraint, format)
|
||||
vulnObj, err := NewVulnerability(vuln)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to parse language='%s' constraint='%s' format='%s': %w", l, vuln.VersionConstraint, format, err)
|
||||
return nil, fmt.Errorf("provider failed to parse language='%s': %w", l, err)
|
||||
}
|
||||
|
||||
vulns = append(vulns, &Vulnerability{
|
||||
Constraint: constraint,
|
||||
ID: vuln.ID,
|
||||
})
|
||||
vulns = append(vulns, vulnObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,21 +83,12 @@ func (pr *StoreProvider) GetByCPE(requestCPE cpe.CPE) ([]*Vulnerability, error)
|
|||
}
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
// TODO: should we encode vendor + name? or leave it as just package name (product)?
|
||||
|
||||
allPkgVulns, err := pr.store.GetVulnerability(namespace, requestCPE.Product)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to fetch namespace='%s' product='%s': %w", namespace, requestCPE.Product, err)
|
||||
}
|
||||
|
||||
for _, vuln := range allPkgVulns {
|
||||
format := version.ParseFormat(vuln.VersionFormat)
|
||||
|
||||
constraint, err := version.GetConstraint(vuln.VersionConstraint, format)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to parse cpe='%s' constraint='%s' format='%s': %w", requestCPE.BindToFmtString(), vuln.VersionConstraint, format, err)
|
||||
}
|
||||
|
||||
vulnCPEs, err := cpe.NewSlice(vuln.CPEs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -120,11 +98,14 @@ func (pr *StoreProvider) GetByCPE(requestCPE cpe.CPE) ([]*Vulnerability, error)
|
|||
candidateMatchCpes := cpe.MatchWithoutVersion(requestCPE, vulnCPEs)
|
||||
|
||||
if len(candidateMatchCpes) > 0 {
|
||||
vulns = append(vulns, &Vulnerability{
|
||||
Constraint: constraint,
|
||||
CPEs: candidateMatchCpes,
|
||||
ID: vuln.ID,
|
||||
})
|
||||
vulnObj, err := NewVulnerability(vuln)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to parse cpe='%s': %w", requestCPE.BindToFmtString(), err)
|
||||
}
|
||||
|
||||
vulnObj.CPEs = candidateMatchCpes
|
||||
|
||||
vulns = append(vulns, vulnObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package vulnerability
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/siren-db/pkg/db"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
|
@ -14,6 +16,21 @@ type Vulnerability struct {
|
|||
ID string
|
||||
}
|
||||
|
||||
func NewVulnerability(vuln db.Vulnerability) (*Vulnerability, error) {
|
||||
format := version.ParseFormat(vuln.VersionFormat)
|
||||
|
||||
constraint, err := version.GetConstraint(vuln.VersionConstraint, format)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse constraint='%s' format='%s': %w", vuln.VersionConstraint, format, err)
|
||||
}
|
||||
|
||||
return &Vulnerability{
|
||||
Constraint: constraint,
|
||||
ID: vuln.ID,
|
||||
CPEs: make([]cpe.CPE, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v Vulnerability) String() string {
|
||||
return fmt.Sprintf("Vuln(id=%s constraint='%s')", v.ID, v.Constraint)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue