Skip to content

Commit

Permalink
CDX 1.4
Browse files Browse the repository at this point in the history
Signed-off-by: Jonathan Ringer <[email protected]>
  • Loading branch information
jonringer committed Sep 30, 2024
1 parent 85a5b32 commit b9da1dd
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 5 deletions.
31 changes: 30 additions & 1 deletion src/sbomnix/cdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
#
# SPDX-License-Identifier: Apache-2.0

""" CycloneDX utils """
################################################################################

# CycloneDX

""" Module for generating cdx entries """

import re

from reuse._licenses import LICENSE_MAP as SPDX_LICENSES
from common.utils import LOG, LOG_SPAM
from vulnxscan.utils import _vuln_source, _vuln_url


def _drv_to_cdx_licenses_entry(drv, column_name, cdx_license_type):
Expand Down Expand Up @@ -128,3 +133,27 @@ def _drv_to_cdx_dependency(drv, deps_list, uid="store_path"):
if deps_list:
dependency["dependsOn"] = deps_list
return dependency


def _vuln_to_cdx_vuln(vuln):
"""Return cdx vulnerability entry from vulnix row"""
vulnerability = {}
vulnerability["bom-ref"] = vuln.store_path
vulnerability["id"] = vuln.vuln_id
source = {}
source["url"] = _vuln_url(vuln)
source["name"] = _vuln_source(vuln)
vulnerability["source"] = source
vulnerability["ratings"] = []
# If the vulnerability is still being assessed, it will be missing a valid number
if vuln.severity != "":
rating = {}
rating["source"] = source
rating["score"] = vuln.severity
vulnerability["ratings"].append(rating)
vulnerability["tools"] = []
for scanner in vuln.scanner:
tool = {}
tool["name"] = scanner
vulnerability["tools"].append(tool)
return vulnerability
41 changes: 38 additions & 3 deletions src/sbomnix/sbomdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#
# SPDX-License-Identifier: Apache-2.0

# pylint: disable=too-many-instance-attributes, too-many-arguments
# pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-locals

""" Module for generating SBOMs in various formats """

from tempfile import NamedTemporaryFile
import uuid
import logging
import json
Expand All @@ -18,10 +19,11 @@
import numpy as np
from reuse._licenses import LICENSE_MAP as SPDX_LICENSES
from nixgraph.graph import NixDependencies
from sbomnix.cdx import _drv_to_cdx_component, _drv_to_cdx_dependency
from sbomnix.cdx import _drv_to_cdx_component, _drv_to_cdx_dependency, _vuln_to_cdx_vuln
from sbomnix.nix import Store, find_deriver
from sbomnix.meta import Meta
from common.utils import LOG, df_to_csv_file, get_py_pkg_version
from vulnxscan.vulnscan import VulnScan

###############################################################################

Expand Down Expand Up @@ -168,7 +170,7 @@ def to_cdx(self, cdx_path, printinfo=True):
"""Export sbomdb to cyclonedx json file"""
cdx = {}
cdx["bomFormat"] = "CycloneDX"
cdx["specVersion"] = "1.3"
cdx["specVersion"] = "1.4"
cdx["version"] = 1
cdx["serialNumber"] = f"urn:uuid:{self.uuid}"
cdx["metadata"] = {}
Expand Down Expand Up @@ -202,6 +204,39 @@ def to_cdx(self, cdx_path, printinfo=True):
deps = self._lookup_dependencies(drv, uid=self.uid)
dependency = _drv_to_cdx_dependency(drv, deps, uid=self.uid)
cdx["dependencies"].append(dependency)
scanner = VulnScan()
scanner.scan_vulnix(self.target_deriver, self.buildtime)
# Write incomplete sbom to a temporary path, then perform a vulnerability scan
with NamedTemporaryFile(
delete=False, prefix="vulnxscan_", suffix=".json"
) as fcdx:
self._write_json(fcdx.name, cdx, printinfo=False)
scanner.scan_grype(fcdx.name)
scanner.scan_osv(fcdx.name)
cdx["vulnerabilities"] = []
# Union all scans into a single dataframe
vulns = pd.concat(
[scanner.df_grype, scanner.df_osv, scanner.df_vulnix],
ignore_index=True,
)
# Concat adds a modified column, remove
vulns.drop("modified", axis=1, inplace=True)
# Deduplicate repeated vulnerabilities, making the scanner column into an array
vuln_grouped = vulns.groupby(
["package", "version", "severity", "vuln_id"],
as_index=False,
).agg({"scanner": pd.Series.unique})
# Do a join so we have access to bom-ref
vulnix_components = pd.merge(
left=vuln_grouped,
right=self.df_sbomdb,
how="left",
left_on=["package", "version"],
right_on=["pname", "version"],
)
for vuln in vulnix_components.itertuples():
vulnix_vuln = _vuln_to_cdx_vuln(vuln)
cdx["vulnerabilities"].append(vulnix_vuln)
self._write_json(cdx_path, cdx, printinfo)

def to_spdx(self, spdx_path, printinfo=True):
Expand Down
10 changes: 9 additions & 1 deletion src/vulnxscan/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,19 @@ def _vuln_url(row):
nvd_url = "https://nvd.nist.gov/vuln/detail/"
if "cve" in row.vuln_id.lower():
return f"{nvd_url}{row.vuln_id}"
if row.osv:
if getattr(row, "osv", False) or ("osv" in getattr(row, "scanner", [])):
return f"{osv_url}{row.vuln_id}"
return ""


def _vuln_source(row):
if "cve" in row.vuln_id.lower():
return "NVD"
if getattr(row, "osv", False) or ("osv" in getattr(row, "scanner", [])):
return "OSV"
return ""


def _is_patched(row):
if row.vuln_id and str(row.vuln_id).lower() in str(row.patches).lower():
patches = row.patches.split()
Expand Down

0 comments on commit b9da1dd

Please sign in to comment.