From 7f5a6d479e5212decbf816b0505953e8f2f66a66 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 31 Dec 2020 19:40:25 +0200 Subject: [PATCH] feat(redhat): support modular packages (#790) --- pkg/detector/ospkg/redhat/redhat.go | 39 +++++----- pkg/detector/ospkg/redhat/redhat_test.go | 90 +++++++++++++----------- 2 files changed, 71 insertions(+), 58 deletions(-) diff --git a/pkg/detector/ospkg/redhat/redhat.go b/pkg/detector/ospkg/redhat/redhat.go index 33107fc388..1d3d8cac66 100644 --- a/pkg/detector/ospkg/redhat/redhat.go +++ b/pkg/detector/ospkg/redhat/redhat.go @@ -62,12 +62,14 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV var vulns []types.DetectedVulnerability for _, pkg := range pkgs { - if !s.isSupported(pkg) { + if !s.isFromSupportedVendor(pkg) { + log.Logger.Debugf("Skipping %s: unsupported vendor", pkg.Name) continue } // For Red Hat Security Data API containing only source package names - advisories, err := s.vs.Get(osVer, pkg.SrcName) + pkgName := addModularNamespace(pkg.SrcName, pkg.Modularitylabel) + advisories, err := s.vs.Get(osVer, pkgName) if err != nil { return nil, xerrors.Errorf("failed to get Red Hat advisories: %w", err) } @@ -88,8 +90,9 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV vulns = append(vulns, vuln) } - // For Red Hat OVAL containing only binary package names - advisories, err = s.vs.Get(osVer, pkg.Name) + // For Red Hat OVAL v2 containing only binary package names + pkgName = addModularNamespace(pkg.Name, pkg.Modularitylabel) + advisories, err = s.vs.Get(osVer, pkgName) if err != nil { return nil, xerrors.Errorf("failed to get Red Hat advisories: %w", err) } @@ -136,20 +139,6 @@ func (s *Scanner) isSupportedVersion(now time.Time, osFamily, osVer string) bool return now.Before(eolDate) } -func (s *Scanner) isSupported(pkg ftypes.Package) bool { - if !s.isFromSupportedVendor(pkg) { - log.Logger.Debugf("Skipping %s: unsupported vendor", pkg.Name) - return false - } - - // Skip modular packages until OVALv2 is supported - if pkg.Modularitylabel != "" { - log.Logger.Debugf("Skipping modular package %s (%s) as temporary workaround", pkg.Name, pkg.Modularitylabel) - return false - } - return true -} - func (s *Scanner) isFromSupportedVendor(pkg ftypes.Package) bool { for _, s := range excludedVendorsSuffix { if strings.HasSuffix(pkg.Release, s) { @@ -158,3 +147,17 @@ func (s *Scanner) isFromSupportedVendor(pkg ftypes.Package) bool { } return true } + +func addModularNamespace(name, label string) string { + // e.g. npm, nodejs:12:8030020201124152102:229f0a1c => nodejs:12::npm + var count int + for i, r := range label { + if r == ':' { + count++ + } + if count == 2 { + return label[:i] + "::" + name + } + } + return name +} diff --git a/pkg/detector/ospkg/redhat/redhat_test.go b/pkg/detector/ospkg/redhat/redhat_test.go index 424009fccb..f90693bd25 100644 --- a/pkg/detector/ospkg/redhat/redhat_test.go +++ b/pkg/detector/ospkg/redhat/redhat_test.go @@ -177,6 +177,56 @@ func TestScanner_Detect(t *testing.T) { }, }, }, + { + name: "happy path: modular packages", + args: args{ + osVer: "8.3", + pkgs: []ftypes.Package{ + { + Name: "php", + Version: "7.2.24", + Release: "1.module_el8.2.0+313+b04d0a66", + Arch: "x86_64", + Epoch: 0, + SrcName: "php", + SrcVersion: "7.2.24", + SrcRelease: "1.module_el8.2.0+313+b04d0a66", + SrcEpoch: 0, + Modularitylabel: "php:7.2:8020020200507003613:2c7ca891", + Layer: ftypes.Layer{ + DiffID: "sha256:3e968ecc016e1b9aa19023798229bf2d25c813d1bf092533f38b056aff820524", + }, + }, + }, + }, + get: []dbTypes.GetExpectation{ + { + Args: dbTypes.GetArgs{ + Release: "8", + PkgName: "php:7.2::php", + }, + Returns: dbTypes.GetReturns{ + Advisories: []dbTypes.Advisory{ + { + VulnerabilityID: "CVE-2019-11043", + FixedVersion: "7.3.5-5.module+el8.1.0+4560+e0eee7d6", + }, + }, + }, + }, + }, + want: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2019-11043", + PkgName: "php", + InstalledVersion: "7.2.24-1.module_el8.2.0+313+b04d0a66", + FixedVersion: "7.3.5-5.module+el8.1.0+4560+e0eee7d6", + Layer: ftypes.Layer{ + DiffID: "sha256:3e968ecc016e1b9aa19023798229bf2d25c813d1bf092533f38b056aff820524", + }, + }, + }, + }, { name: "happy path: packages from remi repository are skipped", args: args{ @@ -216,46 +266,6 @@ func TestScanner_Detect(t *testing.T) { }, want: []types.DetectedVulnerability(nil), }, - { - name: "happy path: modular packages are skipped", - args: args{ - osVer: "8.3", - pkgs: []ftypes.Package{ - { - Name: "php", - Version: "7.2.24", - Release: "1.module_el8.2.0+313+b04d0a66", - Arch: "x86_64", - Epoch: 0, - SrcName: "php", - SrcVersion: "7.2.24", - SrcRelease: "1.module_el8.2.0+313+b04d0a66", - SrcEpoch: 0, - Modularitylabel: "php:7.2:8020020200507003613:2c7ca891", - Layer: ftypes.Layer{ - DiffID: "sha256:3e968ecc016e1b9aa19023798229bf2d25c813d1bf092533f38b056aff820524", - }, - }, - }, - }, - get: []dbTypes.GetExpectation{ - { - Args: dbTypes.GetArgs{ - Release: "8", - PkgName: "php", - }, - Returns: dbTypes.GetReturns{ - Advisories: []dbTypes.Advisory{ - { - VulnerabilityID: "CVE-2019-11043", - FixedVersion: "7.3.5-5.module+el8.1.0+4560+e0eee7d6", - }, - }, - }, - }, - }, - want: []types.DetectedVulnerability(nil), - }, { name: "sad path: Get returns an error", args: args{