From efb1f6938321eec3529ef4fea6608261f6771ae0 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:49:20 +0500 Subject: [PATCH] feat(sbom): add vulnerability support for SPDX formats (#7213) --- pkg/flag/options.go | 9 +-- pkg/sbom/spdx/marshal.go | 18 ++++++ pkg/sbom/spdx/marshal_test.go | 106 ++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 7 deletions(-) diff --git a/pkg/flag/options.go b/pkg/flag/options.go index 33190fb76fbe..b485b74ef677 100644 --- a/pkg/flag/options.go +++ b/pkg/flag/options.go @@ -410,15 +410,10 @@ func (o *Options) enableSBOM() { o.Scanners.Enable(types.SBOMScanner) } - if o.Format == types.FormatSPDX || o.Format == types.FormatSPDXJSON { - log.Info(`"--format spdx" and "--format spdx-json" disable security scanning`) - o.Scanners = types.Scanners{types.SBOMScanner} - } - - if o.Format == types.FormatCycloneDX { + if o.Format == types.FormatCycloneDX || o.Format == types.FormatSPDX || o.Format == types.FormatSPDXJSON { // Vulnerability scanning is disabled by default for CycloneDX. if !viper.IsSet(ScannersFlag.ConfigName) { - log.Info(`"--format cyclonedx" disables security scanning. Specify "--scanners vuln" explicitly if you want to include vulnerabilities in the CycloneDX report.`) + log.Info(fmt.Sprintf(`"--format %[1]s" disables security scanning. Specify "--scanners vuln" explicitly if you want to include vulnerabilities in the "%[1]s" report.`, o.Format)) o.Scanners = nil } o.Scanners.Enable(types.SBOMScanner) diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index 1c4a84d60109..35bd0448aa5d 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -147,6 +147,15 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, if err != nil { return nil, xerrors.Errorf("spdx package error: %w", err) } + + // Add advisories for package + // cf. https://spdx.github.io/spdx-spec/v2.3/how-to-use/#k1-including-security-information-in-a-spdx-document + if vulns, ok := bom.Vulnerabilities()[c.ID()]; ok { + for _, v := range vulns { + spdxPackage.PackageExternalReferences = append(spdxPackage.PackageExternalReferences, m.advisoryExternalReference(v.PrimaryURL)) + } + } + packages = append(packages, &spdxPackage) packageIDs[c.ID()] = spdxPackage.PackageSPDXIdentifier @@ -184,6 +193,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, relationShips = append(relationShips, m.spdxRelationShip(refA, refB, m.spdxRelationshipType(rel.Type))) } } + sortPackages(packages) sortRelationships(relationShips) sortFiles(files) @@ -268,6 +278,14 @@ func (m *Marshaler) purlExternalReference(packageURL string) *spdx.PackageExtern } } +func (m *Marshaler) advisoryExternalReference(primaryURL string) *spdx.PackageExternalReference { + return &spdx.PackageExternalReference{ + Category: common.CategorySecurity, + RefType: common.TypeSecurityAdvisory, + Locator: primaryURL, + } +} + func (m *Marshaler) spdxPackage(c *core.Component, pkgDownloadLocation string) (spdx.Package, error) { pkgID, err := calcPkgID(m.hasher, c) if err != nil { diff --git a/pkg/sbom/spdx/marshal_test.go b/pkg/sbom/spdx/marshal_test.go index 122e529c14d5..4ed35b7fc08c 100644 --- a/pkg/sbom/spdx/marshal_test.go +++ b/pkg/sbom/spdx/marshal_test.go @@ -821,6 +821,112 @@ func TestMarshaler_Marshal(t *testing.T) { }, }, }, + { + name: "happy path with vulnerability", + inputReport: types.Report{ + SchemaVersion: report.SchemaVersion, + ArtifactName: "log4j-core-2.17.0.jar", + ArtifactType: artifact.TypeFilesystem, + Results: types.Results{ + { + Target: "Java", + Class: types.ClassLangPkg, + Type: ftypes.Jar, + Packages: []ftypes.Package{ + { + Name: "org.apache.logging.log4j:log4j-core", + Version: "2.17.0", + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.apache.logging.log4j", + Name: "log4j-core", + Version: "2.17.0", + }, + }, + }, + }, + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2021-44832", + PkgName: "org.apache.logging.log4j:log4j-core", + InstalledVersion: "2.17.0", + FixedVersion: "2.3.2, 2.12.4, 2.17.1", + PrimaryURL: "https://avd.aquasec.com/nvd/cve-2021-44832", + }, + }, + }, + }, + }, + wantSBOM: &spdx.Document{ + SPDXVersion: spdx.Version, + DataLicense: spdx.DataLicense, + SPDXIdentifier: "DOCUMENT", + DocumentName: "log4j-core-2.17.0.jar", + DocumentNamespace: "http://aquasecurity.github.io/trivy/filesystem/log4j-core-2.17.0.jar-3ff14136-e09f-4df9-80ea-000000000003", + CreationInfo: &spdx.CreationInfo{ + Creators: []common.Creator{ + { + Creator: "aquasecurity", + CreatorType: "Organization", + }, + { + Creator: "trivy-0.38.1", + CreatorType: "Tool", + }, + }, + Created: "2021-08-25T12:20:30Z", + }, + Packages: []*spdx.Package{ + { + PackageSPDXIdentifier: spdx.ElementID("Package-4ee6f197f4811213"), + PackageDownloadLocation: "NONE", + PackageName: "org.apache.logging.log4j:log4j-core", + PackageVersion: "2.17.0", + PackageLicenseConcluded: "NONE", + PackageLicenseDeclared: "NONE", + PackageExternalReferences: []*spdx.PackageExternalReference{ + { + Category: tspdx.CategoryPackageManager, + RefType: tspdx.RefTypePurl, + Locator: "pkg:maven/org.apache.logging.log4j/log4j-core@2.17.0", + }, + { + Category: "SECURITY", + RefType: "advisory", + Locator: "https://avd.aquasec.com/nvd/cve-2021-44832", + }, + }, + PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, + PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, + PackageAttributionTexts: []string{ + "PkgType: jar", + }, + }, + { + PackageSPDXIdentifier: spdx.ElementID("Filesystem-121e7e7a43f02ab"), + PackageDownloadLocation: "NONE", + PackageName: "log4j-core-2.17.0.jar", + PackageAttributionTexts: []string{ + "SchemaVersion: 2", + }, + PrimaryPackagePurpose: tspdx.PackagePurposeSource, + }, + }, + Relationships: []*spdx.Relationship{ + { + RefA: spdx.DocElementID{ElementRefID: "DOCUMENT"}, + RefB: spdx.DocElementID{ElementRefID: "Filesystem-121e7e7a43f02ab"}, + Relationship: "DESCRIBES", + }, + { + RefA: spdx.DocElementID{ElementRefID: "Filesystem-121e7e7a43f02ab"}, + RefB: spdx.DocElementID{ElementRefID: "Package-4ee6f197f4811213"}, + Relationship: "CONTAINS", + }, + }, + }, + }, { name: "happy path aggregate results", inputReport: types.Report{