mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 07:40:48 -08:00
feat(vuln): support dependency graph for RHEL/CentOS (#3094)
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
This commit is contained in:
2
go.mod
2
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
||||
1
integration/testdata/almalinux-8.json.golden
vendored
1
integration/testdata/almalinux-8.json.golden
vendored
@@ -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",
|
||||
|
||||
1
integration/testdata/amazon-1.json.golden
vendored
1
integration/testdata/amazon-1.json.golden
vendored
@@ -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",
|
||||
|
||||
2
integration/testdata/amazon-2.json.golden
vendored
2
integration/testdata/amazon-2.json.golden
vendored
@@ -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",
|
||||
|
||||
2
integration/testdata/centos-6.json.golden
vendored
2
integration/testdata/centos-6.json.golden
vendored
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
3
integration/testdata/centos-7.json.golden
vendored
3
integration/testdata/centos-7.json.golden
vendored
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
3
integration/testdata/photon-30.json.golden
vendored
3
integration/testdata/photon-30.json.golden
vendored
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
1
integration/testdata/ubi-7.json.golden
vendored
1
integration/testdata/ubi-7.json.golden
vendored
@@ -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": {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
|
||||
@@ -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".`)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user