Skip to content

Commit

Permalink
feat(debian): support the versions that reached EOL (#1237)
Browse files Browse the repository at this point in the history
* refactor(debian): aggregate oval and json api into salsa

* fix(vulnerability): use package-specific severity

* chore(mod): update trivy-db
  • Loading branch information
knqyf263 authored Sep 17, 2021
1 parent 8cd7de2 commit 669fd1f
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 50 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492
github.com/aquasecurity/trivy-db v0.0.0-20210907100132-2ec74c43526d
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b
github.com/caarlos0/env/v6 v6.0.0
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cheggaaa/pb/v3 v3.0.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516 h1:moQmzbp
github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516/go.mod h1:gTd97VdQ0rg8Mkiic3rPgNOQdprZ7feTAhiD5mGQjgM=
github.com/aquasecurity/tfsec v0.46.0 h1:R9djHTpk+YrFuFv2GRdfU4rRz6uk5wLrgfx1fp9K1es=
github.com/aquasecurity/tfsec v0.46.0/go.mod h1:Dafx5dX/1QV1d5en62shpzEXfq5F31IG6oNNxhleV5Y=
github.com/aquasecurity/trivy-db v0.0.0-20210907100132-2ec74c43526d h1:AMuCVa54YX5wVmvqeUZY/PSfMbHtiX1PukVZHocCLr0=
github.com/aquasecurity/trivy-db v0.0.0-20210907100132-2ec74c43526d/go.mod h1:lcUx+KZjKYLu7gCe8Gwe3ZUBUsxeRUg3mwkvzikP6kQ=
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b h1:RaS93vlHzgreZk3CYqcNgoqukwbsBEYhAiE6qmhLwB0=
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b/go.mod h1:5h8GV7Qxp/SMJ4awWHs0KRxwVkKzcwOnRkORWOnCXRU=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ=
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
Expand Down
69 changes: 33 additions & 36 deletions pkg/detector/ospkg/debian/debian.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (
"k8s.io/utils/clock"

ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/debian"
debianoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/debian-oval"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
Expand Down Expand Up @@ -53,8 +54,7 @@ func WithClock(clock clock.Clock) option {

// Scanner implements the Debian scanner
type Scanner struct {
ovalVs debianoval.VulnSrc
vs debian.VulnSrc
vs debian.VulnSrc
*options
}

Expand All @@ -68,7 +68,6 @@ func NewScanner(opts ...option) *Scanner {
opt(o)
}
return &Scanner{
ovalVs: debianoval.NewVulnSrc(),
vs: debian.NewVulnSrc(),
options: o,
}
Expand All @@ -86,55 +85,53 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV

var vulns []types.DetectedVulnerability
for _, pkg := range pkgs {
// This bucket has only fixed vulnerabilities.
// To detect vulnerabilities, it is necessary to compare versions between the installed version and the patched version.
advisories, err := s.ovalVs.Get(osVer, pkg.SrcName)
if err != nil {
return nil, xerrors.Errorf("failed to get debian OVAL: %w", err)
}

installed := utils.FormatSrcVersion(pkg)
installedVersion, err := version.NewVersion(installed)
if err != nil {
log.Logger.Debugf("failed to parse Debian installed package version: %w", err)
log.Logger.Debugf("Debian installed package version error: %s", err)
continue
}

advisories, err := s.vs.Get(osVer, pkg.SrcName)
if err != nil {
return nil, xerrors.Errorf("failed to get debian advisories: %w", err)
}

for _, adv := range advisories {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
VendorIDs: adv.VendorIDs,
PkgName: pkg.Name,
InstalledVersion: installed,
FixedVersion: adv.FixedVersion,
Layer: pkg.Layer,
}

if adv.Severity != dbTypes.SeverityUnknown {
// Package-specific severity
vuln.SeveritySource = vulnerability.Debian
vuln.Vulnerability = dbTypes.Vulnerability{
Severity: adv.Severity.String(),
}
}

// It means unfixed vulnerability. We don't have to compare versions.
if adv.FixedVersion == "" {
vulns = append(vulns, vuln)
continue
}

var fixedVersion version.Version
fixedVersion, err = version.NewVersion(adv.FixedVersion)
if err != nil {
log.Logger.Debugf("failed to parse Debian package version: %w", err)
log.Logger.Debugf("Debian advisory package version error: %s", err)
continue
}

if installedVersion.LessThan(fixedVersion) {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,
FixedVersion: adv.FixedVersion,
Layer: pkg.Layer,
}
vulns = append(vulns, vuln)
}
}

// This bucket has only unfixed vulnerabilities which don't have a patched version.
// It is unnecessary to compare versions since it should be always vulnerable to an unfixed vulnerability.
advisories, err = s.vs.Get(osVer, pkg.SrcName)
if err != nil {
return nil, xerrors.Errorf("failed to get debian advisory: %w", err)
}
for _, adv := range advisories {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,
Layer: pkg.Layer,
}
vulns = append(vulns, vuln)
}
}
return vulns, nil
}
Expand Down
16 changes: 11 additions & 5 deletions pkg/detector/ospkg/debian/debian_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
fake "k8s.io/utils/clock/testing"

ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/debian"
"github.com/aquasecurity/trivy/pkg/types"

fake "k8s.io/utils/clock/testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestScanner_Detect(t *testing.T) {
Expand Down Expand Up @@ -49,6 +50,7 @@ func TestScanner_Detect(t *testing.T) {
{
PkgName: "htpasswd",
VulnerabilityID: "CVE-2020-11985",
VendorIDs: []string{"DSA-4884-1"},
InstalledVersion: "2.4.24",
FixedVersion: "2.4.25-1",
Layer: ftypes.Layer{
Expand All @@ -59,6 +61,10 @@ func TestScanner_Detect(t *testing.T) {
PkgName: "htpasswd",
VulnerabilityID: "CVE-2021-31618",
InstalledVersion: "2.4.24",
SeveritySource: vulnerability.Debian,
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityMedium.String(),
},
Layer: ftypes.Layer{
DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
},
Expand Down
3 changes: 3 additions & 0 deletions pkg/detector/ospkg/debian/testdata/fixtures/debian.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- key: CVE-2021-31618
value:
FixedVersion: ""
Severity: 2
- bucket: debian oval 9
pairs:
- bucket: apache2
Expand All @@ -15,3 +16,5 @@
- key: CVE-2020-11985
value:
FixedVersion: "2.4.25-1"
VendorIDs:
- DSA-4884-1
28 changes: 22 additions & 6 deletions pkg/result/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,34 @@ func NewClient(dbc db.Config) Client {

// FillVulnerabilityInfo fills extra info in vulnerability objects
func (c Client) FillVulnerabilityInfo(vulns []types.DetectedVulnerability, reportType string) {
var err error

for i := range vulns {
vulns[i].Vulnerability, err = c.dbc.GetVulnerability(vulns[i].VulnerabilityID)
vulnID := vulns[i].VulnerabilityID
vuln, err := c.dbc.GetVulnerability(vulnID)
if err != nil {
log.Logger.Warnf("Error while getting vulnerability details: %s\n", err)
continue
}

// Detect which data source should be used.
source := c.detectSource(reportType)
vulns[i].Severity, vulns[i].SeveritySource = c.getVendorSeverity(&vulns[i], source)
vulns[i].PrimaryURL = c.getPrimaryURL(vulns[i].VulnerabilityID, vulns[i].References, source)

// Select the severity according to the detected source.
severity, severitySource := c.getVendorSeverity(&vuln, source)

// The vendor might provide package-specific severity like Debian.
// For example, CVE-2015-2328 in Debian has "unimportant" for mongodb and "low" for pcre3.
// In that case, we keep the severity as is.
if vulns[i].SeveritySource != "" {
severity = vulns[i].Severity
severitySource = vulns[i].SeveritySource
}

// Add the vulnerability detail
vulns[i].Vulnerability = vuln

vulns[i].Severity = severity
vulns[i].SeveritySource = severitySource
vulns[i].PrimaryURL = c.getPrimaryURL(vulnID, vuln.References, source)
vulns[i].Vulnerability.VendorSeverity = nil // Remove VendorSeverity from Results
}
}
Expand Down Expand Up @@ -98,7 +114,7 @@ func (c Client) detectSource(reportType string) string {
return source
}

func (c Client) getVendorSeverity(vuln *types.DetectedVulnerability, source string) (string, string) {
func (c Client) getVendorSeverity(vuln *dbTypes.Vulnerability, source string) (string, string) {
if vs, ok := vuln.VendorSeverity[source]; ok {
return vs.String(), source
}
Expand Down
31 changes: 31 additions & 0 deletions pkg/result/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,37 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
},
},
},
{
name: "happy path, with package-specific severity",
fixtures: []string{"testdata/fixtures/full.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
SeveritySource: vulnerability.Debian,
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
},
reportType: vulnerability.Debian,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
SeveritySource: vulnerability.Debian,
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityLow.String(),
References: []string{"http://example.com"},
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
},
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
},
},
},
{
name: "GetVulnerability returns an error",
fixtures: []string{"testdata/fixtures/sad.yaml"},
Expand Down
1 change: 1 addition & 0 deletions pkg/types/vulnerability.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
// DetectedVulnerability holds the information of detected vulnerabilities
type DetectedVulnerability struct {
VulnerabilityID string `json:",omitempty"`
VendorIDs []string `json:",omitempty"`
PkgName string `json:",omitempty"`
PkgPath string `json:",omitempty"` // It will be filled in the case of language-specific packages such as egg/wheel and gemspec
InstalledVersion string `json:",omitempty"`
Expand Down

0 comments on commit 669fd1f

Please sign in to comment.