Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
32 changes: 31 additions & 1 deletion .github/workflows/acceptance-test.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
name: 'Acceptance'
on:
push:
workflow_dispatch:
# ... only act on pushes to main
branches:
- main
# ... do not act on release tags
tags-ignore:
- v*

env:
GO_VERSION: "1.14.x"

jobs:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
Build-Snapshot-Artifacts:
Expand Down Expand Up @@ -68,4 +71,31 @@ jobs:
name: artifacts
path: snapshot/**/*

# TODO: currently we only do a snapshot build, later we will add explicit acceptance tests per-platform

# TODO: add basic acceptance tests against snapshot artifacts

# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
Inline-Compare:
needs: [ Build-Snapshot-Artifacts ]
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v2

- name: Fingerprint inline-compare sources
run: make compare-fingerprint

- name: Restore inline reports cache
id: cache
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/test/inline-compare/inline-reports
key: inline-reports-${{ hashFiles('**/inline-compare.fingerprint') }}

- uses: actions/download-artifact@v2
with:
name: artifacts
path: snapshot

- name: Compare Anchore inline-scan results against snapshot build output
run: make compare-snapshot
14 changes: 13 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ on:
# ... only act on release tags
tags:
- 'v*'

env:
GO_VERSION: "1.14.x"

jobs:
wait-for-checks:
runs-on: ubuntu-latest
Expand All @@ -32,6 +34,15 @@ jobs:
checkName: "Build-Snapshot-Artifacts"
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Check inline comparison test results
uses: fountainhead/[email protected]
id: inline-compare
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/acceptance-test.yaml)
checkName: "Inline-Compare"
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Check static anaylysis, unit, and integration test results
uses: fountainhead/[email protected]
id: sa-unit-int
Expand All @@ -42,10 +53,11 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Quality gate
if: steps.sa-unit-int.outputs.conclusion != 'success' || steps.snapshot.outputs.conclusion != 'success'
if: steps.sa-unit-int.outputs.conclusion != 'success' || steps.inline-compare.outputs.conclusion != 'success' || steps.snapshot.outputs.conclusion != 'success'
run: |
echo "Static/Unit/Integration Status : ${{ steps.sa-unit-int.outputs.conclusion }}"
echo "Build Snapshot Artifacts Status: ${{ steps.snapshot.outputs.conclusion }}"
echo "Inline Compare Status: ${{ steps.inline-compare.outputs.conclusion }}"
false

release:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/dist/
*.profile
.server
*.fingerprint
Dockerfile
/metadata.json
/listing.json
Expand Down
28 changes: 23 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ COVERAGE_THRESHOLD := 60
DISTDIR=./dist
SNAPSHOTDIR=./snapshot
GITTREESTATE=$(if $(shell git status --porcelain),dirty,clean)
SNAPSHOT_CMD=$(shell realpath $(shell pwd)/$(SNAPSHOTDIR)/grype_linux_amd64/grype)

ifeq "$(strip $(VERSION))" ""
override VERSION = $(shell git describe --always --tags --dirty)
Expand Down Expand Up @@ -51,10 +52,6 @@ endef
all: clean static-analysis test ## Run all checks (linting, license check, unit, integration, and linux acceptance tests tests)
@printf '$(SUCCESS)All checks pass!$(RESET)\n'

.PHONY: compare
compare:
@cd test/inline-compare && make

.PHONY: test
test: unit integration acceptance-linux ## Run all tests (unit, integration, and linux acceptance tests )

Expand Down Expand Up @@ -100,6 +97,7 @@ validate-schema:
# ensure the codebase is only referencing a single grype-db schema version, multiple is not allowed
python test/validate_schema.py

.PHONY: lint-fix
lint-fix: ## Auto-format all source code + run golangci lint fixers
$(call title,Running lint fixers)
gofmt -w -s .
Expand All @@ -109,6 +107,7 @@ lint-fix: ## Auto-format all source code + run golangci lint fixers
check-licenses:
$(TEMPDIR)/bouncer check

.PHONY: unit
unit: ## Run unit tests (with coverage)
$(call title,Running unit tests)
mkdir -p $(RESULTSDIR)
Expand All @@ -117,16 +116,21 @@ 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

.PHONY: integration
integration: ## Run integration tests
$(call title,Running integration tests)
go test -v -tags=integration ./test/integration

integration/test-fixtures/tar-cache.key, integration-fingerprint:
# note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted
.PHONY: integration-fingerprint
integration-fingerprint:
find test/integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee test/integration/test-fixtures/tar-cache.fingerprint

.PHONY: clear-test-cache
clear-test-cache: ## Delete all test cache (built docker image tars)
find . -type f -wholename "**/test-fixtures/tar-cache/*.tar" -delete

.PHONY: check-pipeline
check-pipeline: ## Run local CircleCI pipeline locally (sanity check)
$(call title,Check pipeline)
# note: this is meant for local development & testing of the pipeline, NOT to be run in CI
Expand All @@ -152,6 +156,20 @@ $(SNAPSHOTDIR): ## Build snapshot release binaries and packages
.PHONY: acceptance-linux
acceptance-linux: $(SNAPSHOTDIR) ## Run acceptance tests on build snapshot binaries and packages (Linux)

# note: this is used by CI to determine if the inline-scan report cache should be busted for the inline-compare tests
.PHONY: compare-fingerprint
compare-fingerprint: ## Compare a snapshot build run of grype against inline-scan
find test/inline-compare/* -type f -exec md5sum {} + | grep -v '\-reports' | grep -v 'fingerprint' | awk '{print $1}' | sort | md5sum | tee test/inline-compare/inline-compare.fingerprint

.PHONY: compare-snapshot
compare-snapshot: $(SNAPSHOTDIR) ## Compare a main branch build run of grype against inline-scan
chmod 755 $(SNAPSHOT_CMD)
@cd test/inline-compare && GRYPE_CMD=$(SNAPSHOT_CMD) make

.PHONY: compare
compare:
@cd test/inline-compare && make

.PHONY: release
release: clean-dist ## Build and publish final binaries and packages
$(call title,Publishing release artifacts)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ go 1.14
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/go-version v1.2.2-0.20200808191332-3efe44edd7a9
github.com/anchore/grype-db v0.0.0-20200807151757-5aee0401bf56
github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19
github.com/anchore/syft v0.1.0-beta.2.0.20200807140516-817ce610368c
github.com/anchore/syft v0.1.0-beta.2.0.20200810143344-2d452bf59e0f
github.com/dustin/go-humanize v1.0.0
github.com/facebookincubator/nvdtools v0.1.4-0.20200622182922-aed862a62ae6
github.com/go-test/deep v1.0.7
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,16 @@ 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/go-version v1.2.2-0.20200808191332-3efe44edd7a9 h1:8oUx5gYrdJrB/eGwOmGGs6Fxu9gtCwGomO1uUnH7x88=
github.com/anchore/go-version v1.2.2-0.20200808191332-3efe44edd7a9/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/grype-db v0.0.0-20200807151757-5aee0401bf56 h1:Hf1i3Imipp+2dmf70U+l7+aYIkzfd3myoUG0t+dBw5w=
github.com/anchore/grype-db v0.0.0-20200807151757-5aee0401bf56/go.mod h1:LINmipRzG88vnJEWvgMMDVCFH1qZsj7+bjmpERlSyaA=
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e h1:QBwtrM0MXi0z+GcHk3RoSyzaQ+CLgas0bC/uOd1P+PQ=
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g=
github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19 h1:iJ6du/cA9KJ0ctP905u2zCcpJubsy6kxLZBvG4XG+uY=
github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs=
github.com/anchore/syft v0.1.0-beta.2.0.20200807140516-817ce610368c h1:nxWfD5olJGXCABqvj8ulecDFCTdWTnrXygybWs6LH1c=
github.com/anchore/syft v0.1.0-beta.2.0.20200807140516-817ce610368c/go.mod h1:v/x/mLoNlq5cIjlLmTcdLahHnbHfi+w1VrM6Jvcf7Y4=
github.com/anchore/syft v0.1.0-beta.2.0.20200810143344-2d452bf59e0f h1:PGP2KD5mUI56RwEYG2qtBr/kVhh/9NTxp2mhH5ANQ/Q=
github.com/anchore/syft v0.1.0-beta.2.0.20200810143344-2d452bf59e0f/go.mod h1:6nY2Hlnsb7HILqGVz92Ya9cSGHesCSoNYBnWWk3aS1M=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
Expand Down Expand Up @@ -791,8 +793,8 @@ github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240 h1:r6BlIP7CV
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/wagoodman/go-progress v0.0.0-20200807221327-51d465df1451 h1:ULknorKcCigmaFEBfB99pzEQmYY2E0F5Yp/bIyaBdEI=
github.com/wagoodman/go-progress v0.0.0-20200807221327-51d465df1451/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/wagoodman/go-rpmdb v0.0.0-20200719223757-ce54a4b0607b h1:elYGLFZPymeTWJ6qA3tIzFet3LQ9D/Jl6HLWNyFjdQc=
github.com/wagoodman/go-rpmdb v0.0.0-20200719223757-ce54a4b0607b/go.mod h1:MjoIZzKmbYfcpbC6ARWMcHijAjtLBViDaHcayXKWQWI=
github.com/wagoodman/go-rpmdb v0.0.0-20200810111121-8136676cb95c h1:eEWc4HjIq0gSno1apdb5MjRn2995xNrNmRTiJyjUJd8=
github.com/wagoodman/go-rpmdb v0.0.0-20200810111121-8136676cb95c/go.mod h1:MjoIZzKmbYfcpbC6ARWMcHijAjtLBViDaHcayXKWQWI=
github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163 h1:qoZwR+bHbFFNirY4Yt7lqbOXnFAMnlFfR89w0TXwjrc=
github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163/go.mod h1:DzXZ1wfRedNhC3KQTick8Gf3CEPMFHsP5k4R/ldjKtw=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
Expand Down
80 changes: 79 additions & 1 deletion grype/matcher/rpmdb/matcher.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package rpmdb

import (
"fmt"
"regexp"

"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/matcher/common"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/pkg"
"github.com/jinzhu/copier"
)

// the source-rpm field has something akin to "util-linux-ng-2.17.2-12.28.el6_9.2.src.rpm"
// in which case the pattern will extract out "util-linux-ng" as the left-most capture group
var rpmPackageNamePattern = regexp.MustCompile(`(?P<name>^[a-zA-Z0-9\-]+)-\d+\.`)

type Matcher struct {
}

Expand All @@ -20,5 +28,75 @@ func (m *Matcher) Type() match.MatcherType {
}

func (m *Matcher) Match(store vulnerability.Provider, d distro.Distro, p *pkg.Package) ([]match.Match, error) {
return common.FindMatchesByPackageDistro(store, d, p, m.Type())
matches := make([]match.Match, 0)

sourceMatches, err := m.matchBySourceIndirection(store, d, p)
if err != nil {
return nil, fmt.Errorf("failed to match by source indirection: %w", err)
}
matches = append(matches, sourceMatches...)

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)
}
matches = append(matches, exactMatches...)

return matches, nil
}

func (m *Matcher) matchBySourceIndirection(store vulnerability.ProviderByDistro, d distro.Distro, p *pkg.Package) ([]match.Match, error) {
value, ok := p.Metadata.(pkg.RpmMetadata)
if !ok {
return nil, fmt.Errorf("bad rpmdb metadata type='%T'", value)
}

// ignore packages without source indirection hints
if value.SourceRpm == "" {
return []match.Match{}, nil
}

// convert the source-rpm package name (e.g. util-linux-ng-2.17.2-12.28.el6_9.2.src.rpm) to a package name (util-linux-ng)
groupMatches := rpmPackageNamePattern.FindStringSubmatch(value.SourceRpm)
if len(groupMatches) == 0 {
return []match.Match{}, nil
} else if len(groupMatches) > 2 {
// TODO: we should not do this
return []match.Match{}, fmt.Errorf("found multiple RPM packages matches: %+v", groupMatches)
}
// note: the result is match is the full match followed by the sub matches, in our case we're interested in the first capture group
sourceRpmPackageName := groupMatches[1]

// don't include matches if the source package name matches the current package name
if sourceRpmPackageName == p.Name {
return []match.Match{}, nil
}

// use source package name for exact package name matching
var indirectPackage pkg.Package

// TODO: we should add a copy() function onto package instead of relying on a 3rd party package
err := copier.Copy(&indirectPackage, p)
if err != nil {
return nil, fmt.Errorf("failed to copy package: %w", err)
}

// use the source package name
indirectPackage.Name = sourceRpmPackageName

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)
}

// we want to make certain that we are tracking the match based on the package from the SBOM (not the indirect package)
// however, we also want to keep the indirect package around for future reference
for idx := range matches {
matches[idx].Type = match.ExactIndirectMatch
matches[idx].Package = p
matches[idx].IndirectPackage = &indirectPackage
matches[idx].Matcher = m.Type()
}

return matches, nil
}
60 changes: 60 additions & 0 deletions grype/matcher/rpmdb/matcher_mocks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package rpmdb

import (
"strings"

"github.com/anchore/grype/grype/version"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/pkg"
)

type mockProvider struct {
data map[string]map[string][]*vulnerability.Vulnerability
}

func newMockProvider() *mockProvider {
pr := mockProvider{
data: make(map[string]map[string][]*vulnerability.Vulnerability),
}
pr.stub()
return &pr
}

func (pr *mockProvider) stub() {
pr.data["rhel:8"] = map[string][]*vulnerability.Vulnerability{
// direct...
"neutron-libs": {
{
Constraint: version.MustGetConstraint("<= 7.1.3-6", version.RpmFormat),
ID: "CVE-2014-fake-1",
},
},
// indirect...
"neutron": {
// expected...
{
Constraint: version.MustGetConstraint("< 7.1.4-5", version.RpmFormat),
ID: "CVE-2014-fake-2",
},
{
Constraint: version.MustGetConstraint("< 8.0.2-0", version.RpmFormat),
ID: "CVE-2013-fake-3",
},
// unexpected...
{
Constraint: version.MustGetConstraint("< 7.0.4-1", version.RpmFormat),
ID: "CVE-2013-fake-BAD",
},
},
}
}

func (pr *mockProvider) GetByDistro(d distro.Distro, p *pkg.Package) ([]*vulnerability.Vulnerability, error) {
var ty = strings.ToLower(d.Type.String())
if d.Type == distro.CentOS || d.Type == distro.RedHat {
ty = "rhel"
}

return pr.data[ty+":"+d.FullVersion()][p.Name], nil
}
Loading