feat(vuln): support dependency graph for RHEL/CentOS (#3094)

Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
This commit is contained in:
Teppei Fukuda
2022-10-31 11:07:41 +02:00
committed by GitHub
parent 9468056c0f
commit 7912f585a3
33 changed files with 151 additions and 57 deletions

2
go.mod
View File

@@ -259,7 +259,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/klauspost/compress v1.15.11 // indirect
github.com/knqyf263/go-rpmdb v0.0.0-20220607073645-842f01763e21
github.com/knqyf263/go-rpmdb v0.0.0-20221030142135-919c8a52f04f
github.com/knqyf263/nested v0.0.1
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect

4
go.sum
View File

@@ -1082,8 +1082,8 @@ github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 h1:aC6MEAs3PE3lWD7lqrJfDxHd6hcced9R4JTZu85cJwU=
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
github.com/knqyf263/go-rpmdb v0.0.0-20220607073645-842f01763e21 h1:3E1B04qBvkGmr6oXPSwLpuAF0wekN67CKseKGRjj6Yo=
github.com/knqyf263/go-rpmdb v0.0.0-20220607073645-842f01763e21/go.mod h1:zp6SMcRd0GB+uwNJjr+DkrNZdQZ4er2HMO6KyD0vIGU=
github.com/knqyf263/go-rpmdb v0.0.0-20221030142135-919c8a52f04f h1:oz80cOWEcx/tTh5T0g43oz5W7zZw8jm7zD5BR9tQjX8=
github.com/knqyf263/go-rpmdb v0.0.0-20221030142135-919c8a52f04f/go.mod h1:zp6SMcRd0GB+uwNJjr+DkrNZdQZ4er2HMO6KyD0vIGU=
github.com/knqyf263/nested v0.0.1 h1:Sv26CegUMhjt19zqbBKntjwESdxe5hxVPSk0+AKjdUc=
github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=

View File

@@ -53,6 +53,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2021-3712",
"PkgID": "openssl-libs@1.1.1k-4.el8.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.1.1k-4.el8",
"FixedVersion": "1:1.1.1k-5.el8_5",

View File

@@ -54,6 +54,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-5481",
"PkgID": "curl@7.61.1-11.91.amzn1.x86_64",
"PkgName": "curl",
"InstalledVersion": "7.61.1-11.91.amzn1",
"FixedVersion": "7.61.1-12.93.amzn1",

View File

@@ -54,6 +54,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-5481",
"PkgID": "curl@7.61.1-9.amzn2.0.1.x86_64",
"PkgName": "curl",
"InstalledVersion": "7.61.1-9.amzn2.0.1",
"FixedVersion": "7.61.1-12.amzn2.0.1",
@@ -111,6 +112,7 @@
},
{
"VulnerabilityID": "CVE-2019-5436",
"PkgID": "curl@7.61.1-9.amzn2.0.1.x86_64",
"PkgName": "curl",
"InstalledVersion": "7.61.1-9.amzn2.0.1",
"FixedVersion": "7.61.1-11.amzn2.0.2",

View File

@@ -76,6 +76,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2020-29573",
"PkgID": "glibc@2.12-1.212.el6.x86_64",
"PkgName": "glibc",
"InstalledVersion": "2.12-1.212.el6",
"Layer": {
@@ -119,6 +120,7 @@
"VendorIDs": [
"RHSA-2019:2471"
],
"PkgID": "openssl@1.0.1e-57.el6.x86_64",
"PkgName": "openssl",
"InstalledVersion": "1.0.1e-57.el6",
"FixedVersion": "1.0.1e-58.el6_10",

View File

@@ -69,6 +69,7 @@
"VendorIDs": [
"RHSA-2019:2304"
],
"PkgID": "openssl-libs@1.0.2k-16.el7.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.el7",
"FixedVersion": "1:1.0.2k-19.el7",
@@ -149,6 +150,7 @@
"VendorIDs": [
"RHSA-2019:2304"
],
"PkgID": "openssl-libs@1.0.2k-16.el7.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.el7",
"FixedVersion": "1:1.0.2k-19.el7",

View File

@@ -69,6 +69,7 @@
"VendorIDs": [
"RHSA-2019:2304"
],
"PkgID": "openssl-libs@1.0.2k-16.el7.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.el7",
"FixedVersion": "1:1.0.2k-19.el7",

View File

@@ -66,6 +66,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-18276",
"PkgID": "bash@4.2.46-31.el7.x86_64",
"PkgName": "bash",
"InstalledVersion": "4.2.46-31.el7",
"Layer": {
@@ -113,6 +114,7 @@
"VendorIDs": [
"RHSA-2019:2304"
],
"PkgID": "openssl-libs@1.0.2k-16.el7.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.el7",
"FixedVersion": "1:1.0.2k-19.el7",
@@ -193,6 +195,7 @@
"VendorIDs": [
"RHSA-2019:2304"
],
"PkgID": "openssl-libs@1.0.2k-16.el7.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.el7",
"FixedVersion": "1:1.0.2k-19.el7",

View File

@@ -30,6 +30,7 @@
{
"SPDXID": "SPDXRef-Package-5a18334f22149877",
"attributionTexts": [
"PkgID: bash@4.2.46-31.el7.x86_64",
"LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b",
"LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a"
],
@@ -50,6 +51,7 @@
{
"SPDXID": "SPDXRef-Package-e16b1cbaa5186199",
"attributionTexts": [
"PkgID: openssl-libs@1.0.2k-16.el7.x86_64",
"LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b",
"LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a"
],

View File

@@ -33,6 +33,7 @@ PackageSourceInfo: built package from: bash 4.2.46-31.el7
PackageLicenseConcluded: GPLv3+
PackageLicenseDeclared: GPLv3+
ExternalRef: PACKAGE-MANAGER purl pkg:rpm/centos/bash@4.2.46-31.el7?arch=x86_64&distro=centos-7.6.1810
PackageAttributionText: PkgID: bash@4.2.46-31.el7.x86_64
PackageAttributionText: LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b
PackageAttributionText: LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a
@@ -46,6 +47,7 @@ PackageSourceInfo: built package from: openssl-libs 1:1.0.2k-16.el7
PackageLicenseConcluded: OpenSSL
PackageLicenseDeclared: OpenSSL
ExternalRef: PACKAGE-MANAGER purl pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64&distro=centos-7.6.1810
PackageAttributionText: PkgID: openssl-libs@1.0.2k-16.el7.x86_64
PackageAttributionText: LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b
PackageAttributionText: LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a

View File

@@ -62,6 +62,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "openSUSE-SU-2020:0062-1",
"PkgID": "libopenssl1_1@1.1.0i-lp151.8.3.1.x86_64",
"PkgName": "libopenssl1_1",
"InstalledVersion": "1.1.0i-lp151.8.3.1",
"FixedVersion": "1.1.0i-lp151.8.6.1",
@@ -86,6 +87,7 @@
},
{
"VulnerabilityID": "openSUSE-SU-2020:0062-1",
"PkgID": "openssl-1_1@1.1.0i-lp151.8.3.1.x86_64",
"PkgName": "openssl-1_1",
"InstalledVersion": "1.1.0i-lp151.8.3.1",
"FixedVersion": "1.1.0i-lp151.8.6.1",

View File

@@ -63,6 +63,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-3823",
"PkgID": "curl@7.61.1-8.el8.x86_64",
"PkgName": "curl",
"InstalledVersion": "7.61.1-8.el8",
"FixedVersion": "7.61.1-11.el8",
@@ -119,6 +120,7 @@
},
{
"VulnerabilityID": "CVE-2019-5436",
"PkgID": "curl@7.61.1-8.el8.x86_64",
"PkgName": "curl",
"InstalledVersion": "7.61.1-8.el8",
"FixedVersion": "7.61.1-12.el8",

View File

@@ -64,6 +64,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-18276",
"PkgID": "bash@4.4.18-1.ph3.x86_64",
"PkgName": "bash",
"InstalledVersion": "4.4.18-1.ph3",
"FixedVersion": "4.4.18-2.ph3",
@@ -114,6 +115,7 @@
},
{
"VulnerabilityID": "CVE-2019-5481",
"PkgID": "curl@7.61.1-4.ph3.x86_64",
"PkgName": "curl",
"InstalledVersion": "7.61.1-4.ph3",
"FixedVersion": "7.61.1-5.ph3",
@@ -171,6 +173,7 @@
},
{
"VulnerabilityID": "CVE-2019-5481",
"PkgID": "curl-libs@7.61.1-4.ph3.x86_64",
"PkgName": "curl-libs",
"InstalledVersion": "7.61.1-4.ph3",
"FixedVersion": "7.61.1-5.ph3",

View File

@@ -53,6 +53,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2021-3712",
"PkgID": "openssl-libs@1.1.1k-4.el8.x86_64",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.1.1k-4.el8",
"FixedVersion": "1:1.1.1k-5.el8_5",

View File

@@ -77,6 +77,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-18276",
"PkgID": "bash@4.2.46-33.el7.x86_64",
"PkgName": "bash",
"InstalledVersion": "4.2.46-33.el7",
"Layer": {

View File

@@ -87,6 +87,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
if installedVersion.LessThan(fixedVersion) {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
FixedVersion: fixedVersion.String(),

View File

@@ -100,6 +100,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
if installedVersion.LessThan(fixedVersion) {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
FixedVersion: adv.FixedVersion,

View File

@@ -85,6 +85,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
fixedVersion := version.NewVersion(adv.FixedVersion)
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
Ref: pkg.Ref,

View File

@@ -77,6 +77,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
fixedVersion := version.NewVersion(adv.FixedVersion)
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
Ref: pkg.Ref,

View File

@@ -155,6 +155,7 @@ func (s *Scanner) detect(osVer string, pkg ftypes.Package) ([]types.DetectedVuln
vulnID := adv.VulnerabilityID
vuln := types.DetectedVulnerability{
VulnerabilityID: vulnID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: utils.FormatVersion(pkg),
Ref: pkg.Ref,

View File

@@ -87,6 +87,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
if installedVersion.LessThan(fixedVersion) {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
FixedVersion: fixedVersion.String(),

View File

@@ -129,6 +129,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
fixedVersion := version.NewVersion(adv.FixedVersion)
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
Ref: pkg.Ref,

View File

@@ -2,17 +2,20 @@ package rpm
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
rpmdb "github.com/knqyf263/go-rpmdb/pkg"
"github.com/samber/lo"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/log"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/log"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/utils"
)
@@ -21,7 +24,7 @@ func init() {
analyzer.RegisterAnalyzer(&rpmPkgAnalyzer{})
}
const version = 2
const version = 3
var (
requiredFiles = []string{
@@ -76,30 +79,15 @@ func (a rpmPkgAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput)
}
func (a rpmPkgAnalyzer) parsePkgInfo(rc io.Reader) ([]types.Package, []string, error) {
tmpDir, err := os.MkdirTemp("", "rpm")
filePath, err := writeToTempFile(rc)
if err != nil {
return nil, nil, xerrors.Errorf("failed to create a temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
filename := filepath.Join(tmpDir, "Packages")
f, err := os.Create(filename)
if err != nil {
return nil, nil, xerrors.Errorf("failed to create a package file: %w", err)
}
if _, err = io.Copy(f, rc); err != nil {
return nil, nil, xerrors.Errorf("failed to copy a package file: %w", err)
}
// The temp file must be closed before being opened as Berkeley DB.
if err = f.Close(); err != nil {
return nil, nil, xerrors.Errorf("failed to close a temp file: %w", err)
return nil, nil, xerrors.Errorf("temp file error: %w", err)
}
defer os.RemoveAll(filepath.Dir(filePath)) // Remove the temp dir
// rpm-python 4.11.3 rpm-4.11.3-35.el7.src.rpm
// Extract binary package names because RHSA refers to binary package names.
db, err := rpmdb.Open(filename)
db, err := rpmdb.Open(filePath)
if err != nil {
return nil, nil, xerrors.Errorf("failed to open RPM DB: %w", err)
}
@@ -114,6 +102,7 @@ func (a rpmPkgAnalyzer) parsePkgInfo(rc io.Reader) ([]types.Package, []string, e
var pkgs []types.Package
var installedFiles []string
provides := map[string]string{}
for _, pkg := range pkgList {
arch := pkg.Arch
if arch == "" {
@@ -141,6 +130,7 @@ func (a rpmPkgAnalyzer) parsePkgInfo(rc io.Reader) ([]types.Package, []string, e
}
p := types.Package{
ID: fmt.Sprintf("%s@%s-%s.%s", pkg.Name, pkg.Version, pkg.Release, pkg.Arch),
Name: pkg.Name,
Epoch: pkg.EpochNum(),
Version: pkg.Version,
@@ -152,14 +142,38 @@ func (a rpmPkgAnalyzer) parsePkgInfo(rc io.Reader) ([]types.Package, []string, e
SrcRelease: srcRel,
Modularitylabel: pkg.Modularitylabel,
Licenses: []string{pkg.License},
DependsOn: pkg.Requires, // Will be replaced with package IDs
}
pkgs = append(pkgs, p)
installedFiles = append(installedFiles, files...)
// It contains mappings between package-providing files and package IDs
// e.g.
// "libc.so.6()(64bit)" => "glibc-2.12-1.212.el6.x86_64"
// "rtld(GNU_HASH)" => "glibc-2.12-1.212.el6.x86_64"
for _, provide := range pkg.Provides {
provides[provide] = p.ID
}
}
// Replace required files with package IDs
consolidateDependencies(pkgs, provides)
return pkgs, installedFiles, nil
}
func (a rpmPkgAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return utils.StringInSlice(filePath, requiredFiles)
}
func (a rpmPkgAnalyzer) Type() analyzer.Type {
return analyzer.TypeRpm
}
func (a rpmPkgAnalyzer) Version() int {
return version
}
// splitFileName returns a name, version, release, epoch, arch:
//
// e.g.
@@ -191,18 +205,6 @@ func splitFileName(filename string) (name, ver, rel string, err error) {
return name, ver, rel, nil
}
func (a rpmPkgAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return utils.StringInSlice(filePath, requiredFiles)
}
func (a rpmPkgAnalyzer) Type() analyzer.Type {
return analyzer.TypeRpm
}
func (a rpmPkgAnalyzer) Version() int {
return version
}
func packageProvidedByVendor(pkgVendor string) bool {
for _, vendor := range osVendors {
if strings.HasPrefix(pkgVendor, vendor) {
@@ -211,3 +213,45 @@ func packageProvidedByVendor(pkgVendor string) bool {
}
return false
}
func writeToTempFile(rc io.Reader) (string, error) {
tmpDir, err := os.MkdirTemp("", "rpm")
if err != nil {
return "", xerrors.Errorf("failed to create a temp dir: %w", err)
}
filePath := filepath.Join(tmpDir, "Packages")
f, err := os.Create(filePath)
if err != nil {
return "", xerrors.Errorf("failed to create a package file: %w", err)
}
if _, err = io.Copy(f, rc); err != nil {
return "", xerrors.Errorf("failed to copy a package file: %w", err)
}
// The temp file must be closed before being opened as Berkeley DB.
if err = f.Close(); err != nil {
return "", xerrors.Errorf("failed to close a temp file: %w", err)
}
return filePath, nil
}
func consolidateDependencies(pkgs []types.Package, provides map[string]string) {
for i := range pkgs {
// e.g. "libc.so.6()(64bit)" => "glibc-2.12-1.212.el6.x86_64"
pkgs[i].DependsOn = lo.FilterMap(pkgs[i].DependsOn, func(d string, _ int) (string, bool) {
if pkgID, ok := provides[d]; ok && pkgs[i].ID != pkgID {
return pkgID, true
}
return "", false
})
sort.Strings(pkgs[i].DependsOn)
pkgs[i].DependsOn = slices.Compact(pkgs[i].DependsOn)
if len(pkgs[i].DependsOn) == 0 {
pkgs[i].DependsOn = nil
}
}
}

View File

@@ -585,17 +585,22 @@ func TestParseRpmInfo(t *testing.T) {
require.NoError(t, err)
defer f.Close()
pkgs, _, err := a.parsePkgInfo(f)
got, _, err := a.parsePkgInfo(f)
require.NoError(t, err)
sort.Slice(tc.pkgs, func(i, j int) bool {
return tc.pkgs[i].Name < tc.pkgs[j].Name
})
sort.Slice(pkgs, func(i, j int) bool {
return pkgs[i].Name < pkgs[j].Name
sort.Slice(got, func(i, j int) bool {
return got[i].Name < got[j].Name
})
assert.Equal(t, tc.pkgs, pkgs)
for i := range got {
got[i].ID = ""
got[i].DependsOn = nil // TODO: add tests
}
assert.Equal(t, tc.pkgs, got)
})
}
}

View File

@@ -39,9 +39,12 @@ type Package struct {
Modularitylabel string `json:",omitempty"` // only for Red Hat based distributions
BuildInfo *BuildInfo `json:",omitempty"` // only for Red Hat
Ref string `json:",omitempty"` // identifier which can be used to reference the component elsewhere
Indirect bool `json:",omitempty"` // this package is direct dependency of the project or not
DependsOn []string `json:",omitempty"` // dependencies of this package
Ref string `json:",omitempty"` // identifier which can be used to reference the component elsewhere
Indirect bool `json:",omitempty"` // this package is direct dependency of the project or not
// Dependencies of this package
// Note: it may have interdependencies, which may lead to infinite loops.
DependsOn []string `json:",omitempty"`
Layer Layer `json:",omitempty"`

View File

@@ -175,7 +175,9 @@ func (f *ReportFlagGroup) ToOptions(out io.Writer) (ReportOptions, error) {
// "--dependency-tree" option is available only with "--format table".
if dependencyTree {
log.Logger.Infof(`"--dependency-tree" only shows vulnerable package dependencies of "package-lock.json", "Cargo.lock" and OS packages`)
log.Logger.Infof(`"--dependency-tree" only shows the dependents of vulnerable packages. ` +
`Note that it is the reverse of the usual dependency tree, which shows the packages that depend on the vulnerable package. ` +
`It supports "package-lock.json", "Cargo.lock" and OS packages. Please see the document for the detail.`)
if format != report.FormatTable {
log.Logger.Warn(`"--dependency-tree" can be used only with "--format table".`)
}

View File

@@ -240,8 +240,8 @@ Total: 2 (MEDIUM: 1, HIGH: 1)
│ sanitize-html │ CVE-2021-26539 │ MEDIUM │ 1.20.0 │ 2.3.1 │ │
└───────────────┴────────────────┴──────────┴───────────────────┴───────────────┴────────┘
Dependency Origin Tree
======================
Dependency Origin Tree (Reversed)
=================================
package-lock.json
├── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
│ └── isomorphic-fetch@2.2.1

View File

@@ -135,8 +135,8 @@ func (r *vulnerabilityRenderer) renderDependencyTree() {
}
root := treeprint.NewWithRoot(fmt.Sprintf(`
Dependency Origin Tree
======================
Dependency Origin Tree (Reversed)
=================================
%s`, r.result.Target))
// This count is next to the package ID.
@@ -164,7 +164,7 @@ Dependency Origin Tree
seen[vuln.PkgID] = struct{}{}
branch := root.AddBranch(topLvlID)
addParents(branch, vuln.PkgID, parents)
addParents(branch, vuln.PkgID, parents, map[string]struct{}{})
}
r.printf(root.String())
@@ -175,14 +175,15 @@ func (r *vulnerabilityRenderer) printf(format string, args ...interface{}) {
_ = tml.Fprintf(r.w, format, args...)
}
func addParents(topItem treeprint.Tree, pkgID string, parentMap map[string][]string) {
parents, ok := parentMap[pkgID]
if !ok {
return
}
for _, parent := range parents {
func addParents(topItem treeprint.Tree, pkgID string, parentMap map[string][]string, seen map[string]struct{}) {
seen[pkgID] = struct{}{} // to avoid infinite loops
for _, parent := range parentMap[pkgID] {
if _, ok := seen[parent]; ok {
return
}
branch := topItem.AddBranch(parent)
addParents(branch, parent, parentMap)
addParents(branch, parent, parentMap, seen)
}
}

View File

@@ -37,6 +37,7 @@ const (
PropertyRepoTag = "RepoTag"
// Package properties
PropertyPkgID = "PkgID"
PropertyPkgType = "PkgType"
PropertySrcName = "SrcName"
PropertySrcVersion = "SrcVersion"
@@ -436,6 +437,7 @@ func cdxProperties(pkgType string, pkg ftypes.Package) *[]cdx.Property {
name string
value string
}{
{PropertyPkgID, pkg.ID},
{PropertyPkgType, pkgType},
{PropertyFilePath, pkg.FilePath},
{PropertySrcName, pkg.SrcName},

View File

@@ -265,6 +265,8 @@ func toPackage(component cdx.Component) (string, *ftypes.Package, error) {
for _, prop := range lo.FromPtr(component.Properties) {
if strings.HasPrefix(prop.Name, Namespace) {
switch strings.TrimPrefix(prop.Name, Namespace) {
case PropertyPkgID:
pkg.ID = prop.Value
case PropertySrcName:
pkg.SrcName = prop.Value
case PropertySrcVersion:

View File

@@ -41,6 +41,7 @@ const (
PropertyRepoTag = "RepoTag"
// Package properties
PropertyPkgID = "PkgID"
PropertyLayerDiffID = "LayerDiffID"
PropertyLayerDigest = "LayerDigest"
@@ -279,6 +280,7 @@ func (m *Marshaler) pkgToSpdxPackage(t string, class types.ResultClass, metadata
pkgExtRefs := []*spdx.PackageExternalReference2_2{purlExternalReference(packageURL.String())}
var attrTexts []string
attrTexts = appendAttributionText(attrTexts, PropertyPkgID, pkg.ID)
attrTexts = appendAttributionText(attrTexts, PropertyLayerDigest, pkg.Layer.Digest)
attrTexts = appendAttributionText(attrTexts, PropertyLayerDiffID, pkg.Layer.DiffID)

View File

@@ -178,6 +178,7 @@ func parsePkg(spdxPkg spdx.Package2_2) (*ftypes.Package, error) {
break // Take the first file name
}
pkg.ID = lookupAttributionTexts(spdxPkg.PackageAttributionTexts, PropertyPkgID)
pkg.Layer.Digest = lookupAttributionTexts(spdxPkg.PackageAttributionTexts, PropertyLayerDigest)
pkg.Layer.DiffID = lookupAttributionTexts(spdxPkg.PackageAttributionTexts, PropertyLayerDiffID)