From 562a30ce931c5d9b41aa712b2154906b3500524c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Mon, 11 Nov 2024 14:39:41 +0100 Subject: [PATCH] osbuild-depsolve-dnf: add SBOM support for DNF5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable generating SBOM documents for depsolved transactions when using DNF5. Enable SBOM testing with DNF5 in unit tests. Signed-off-by: Tomáš Hozza --- osbuild/solver/dnf5.py | 19 ++++++++++++++++++- tools/osbuild-depsolve-dnf | 9 --------- tools/test/test_depsolve.py | 7 ------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/osbuild/solver/dnf5.py b/osbuild/solver/dnf5.py index 90e1e814..3570183f 100755 --- a/osbuild/solver/dnf5.py +++ b/osbuild/solver/dnf5.py @@ -2,7 +2,7 @@ import os import os.path import tempfile from datetime import datetime -from typing import List +from typing import Dict, List import libdnf5 as dnf5 from libdnf5.base import GoalProblem_NO_PROBLEM as NO_PROBLEM @@ -19,6 +19,8 @@ from osbuild.solver import ( modify_rootdir_path, read_keys, ) +from osbuild.util.sbom.dnf5 import dnf_pkgset_to_sbom_pkgset +from osbuild.util.sbom.spdx import bom_pkgset_to_spdx2_doc def remote_location(package, schemes=("http", "ftp", "file", "https")): @@ -276,6 +278,17 @@ class DNF5(SolverBase): def _timestamp_to_rfc3339(timestamp): return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%dT%H:%M:%SZ') + @staticmethod + def _sbom_for_pkgset(pkgset: List[dnf5.rpm.Package]) -> Dict: + """ + Create an SBOM document for the given package set. + + For now, only SPDX v2 is supported. + """ + pkgset = dnf_pkgset_to_sbom_pkgset(pkgset) + spdx_doc = bom_pkgset_to_spdx2_doc(pkgset) + return spdx_doc.to_dict() + def dump(self): """dump returns a list of all available packages""" packages = [] @@ -445,4 +458,8 @@ class DNF5(SolverBase): "packages": packages, "repos": repositories, } + + if "sbom" in arguments: + response["sbom"] = self._sbom_for_pkgset(last_transaction) + return response diff --git a/tools/osbuild-depsolve-dnf b/tools/osbuild-depsolve-dnf index 9e7132ad..3877d32f 100755 --- a/tools/osbuild-depsolve-dnf +++ b/tools/osbuild-depsolve-dnf @@ -161,15 +161,6 @@ def validate_request(request): sbom = request["arguments"].get("sbom") if sbom is not None: - # NB: check the DNF5 flag here, instead of in the dnf5 module, - # to consistently return this error message, even if there are other - # potential errors in the request, such as broken repository. - if config.get("use_dnf5", False): - return { - "kind": "InvalidRequest", - "reason": "SBOM support for DNF5 is not implemented" - } - if command != "depsolve": return { "kind": "InvalidRequest", diff --git a/tools/test/test_depsolve.py b/tools/test/test_depsolve.py index 94ef0161..2e0b464e 100644 --- a/tools/test/test_depsolve.py +++ b/tools/test/test_depsolve.py @@ -1280,13 +1280,6 @@ def test_depsolve(tmp_path, repo_servers, dnf_config, detect_fn, with_sbom, test res, exit_code = depsolve(transactions, repo_configs, root_dir, cache_dir, dnf_config, opt_metadata, with_sbom) - # NB: dnf5 implementation does not support SBOM yet - if dnf_config.get("use_dnf5", False) and with_sbom: - assert exit_code != 0 - assert res["kind"] == "InvalidRequest" - assert res["reason"] == "SBOM support for DNF5 is not implemented" - continue - if test_case.get("error", False): assert exit_code != 0 assert res["kind"] == test_case["error_kind"]