From cacd45250bef9151f065bd686c5339e1b5d51898 Mon Sep 17 00:00:00 2001 From: anthonyharrison Date: Sun, 24 Mar 2024 19:47:37 +0000 Subject: [PATCH] feat: Add alternative root location for deb distribution (fixes #13) --- README.md | 10 ++++++---- distro2sbom/cli.py | 9 ++++++++- distro2sbom/distrobuilder/dpkgbuilder.py | 14 +++++++++++--- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d32ac81..58e5277 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ up for testing using different versions of Python. ## Usage ``` -usage: distro2sbom [-h] [--distro {rpm,deb,windows,auto}] [-i INPUT_FILE] [-n NAME] [-r RELEASE] [-p PACKAGE] [-s] [-d] [--sbom {spdx,cyclonedx}] [--format {tag,json,yaml}] - [-o OUTPUT_FILE] [-V] +usage: distro2sbom [-h] [--distro {rpm,deb,windows,auto}] [-i INPUT_FILE] [-n NAME] [-r RELEASE] [-p PACKAGE] [-s] [--root ROOT] + [-d] [--sbom {spdx,cyclonedx}] [--format {tag,json,yaml}] [-o OUTPUT_FILE] [-V] Distro2Sbom generates a Software Bill of Materials for the specified package or distribution. @@ -46,8 +46,7 @@ Input: -p PACKAGE, --package PACKAGE identity of package within distribution -s, --system generate SBOM for installed system - -Output: + --root ROOT location of distribution packages Output: -d, --debug add debug information @@ -57,6 +56,7 @@ Output: specify format of software bill of materials (sbom) (default: tag) -o OUTPUT_FILE, --output-file OUTPUT_FILE output filename (default: output to stdout) + ``` ## Operation @@ -126,6 +126,8 @@ This option is not supported if the `--distro` option is set to 'windows'. The `--system` option is used to generate an SBOM for all the applications installed on the system. Note that this option will take some time to complete as it is dependent on the number of installed applications. This option is not supported if the `--distro` option is set to 'windows'. +The `--root` option is used to specify an alternative directory location for the installed packages. This option only applies for 'deb' distributions. + At least one of the `--input-file`, `--package` or `--system` options must be specified. If multiple options are specified, the `--input-file` option followed by the `--system` option will be assumed. The `--sbom` option is used to specify the format of the generated SBOM (the default is SPDX). The `--format` option diff --git a/distro2sbom/cli.py b/distro2sbom/cli.py index 71bcf89..318990e 100644 --- a/distro2sbom/cli.py +++ b/distro2sbom/cli.py @@ -95,6 +95,11 @@ def main(argv=None): default=False, help="generate SBOM for installed system", ) + input_group.add_argument( + "--root", + action="store", + help="location of distribution packages", + ) output_group = parser.add_argument_group("Output") output_group.add_argument( @@ -140,6 +145,7 @@ def main(argv=None): "release": None, "package": "", "system": False, + "root" : "" } raw_args = parser.parse_args(argv[1:]) @@ -172,6 +178,7 @@ def main(argv=None): print("Input file:", args["input_file"]) print("Distro name:", args["name"]) print("Distro release:", args["release"]) + print("Distro root:", args["root"]) print("Package:", args["package"]) print("System SBOM:", args["system"]) print("SBOM type:", args["sbom"]) @@ -198,7 +205,7 @@ def main(argv=None): return -1 if distro_type == "deb": - sbom_build = DpkgBuilder(args["name"], args["release"], args["debug"]) + sbom_build = DpkgBuilder(args["name"], args["release"], args["debug"], root=args["root"]) elif distro_type == "rpm": sbom_build = RpmBuilder(args["name"], args["release"], args["debug"]) elif distro_type == "windows": diff --git a/distro2sbom/distrobuilder/dpkgbuilder.py b/distro2sbom/distrobuilder/dpkgbuilder.py index d02fd57..075bfda 100644 --- a/distro2sbom/distrobuilder/dpkgbuilder.py +++ b/distro2sbom/distrobuilder/dpkgbuilder.py @@ -12,7 +12,7 @@ class DpkgBuilder(DistroBuilder): - def __init__(self, name, release, debug=False): + def __init__(self, name, release, debug=False, root=""): super().__init__(debug) self.sbom_package = SBOMPackage() self.sbom_relationship = SBOMRelationship() @@ -26,6 +26,7 @@ def __init__(self, name, release, debug=False): self.name = name.replace(" ", "-") self.release = release self.parent = f"Distro-{self.name}" + self.root=root def parse_data(self, filename): # Process file containing installed applications @@ -128,6 +129,11 @@ def get_metadata_from_file(self, package): return license_text, copyright_text + def dpkg_command(self, command_string): + command = f"dpkg" + if self.root != "": + command=f"{command} --root {self.root}" + return self.run_program(f"{command} {command_string}") def process_package(self, package_name, parent="-"): if self.debug: print(f"Process package {package_name}. Parent {parent}") @@ -140,7 +146,7 @@ def process_package(self, package_name, parent="-"): self.sbom_relationships.append(self.sbom_relationship.get_relationship()) return 0 self.distro_packages.append(package_name) - out = self.run_program(f"dpkg -s {package_name}") + out = self.dpkg_command(f"-s {package_name}") # If package not found, no metadata returned if len(out) > 0: self.metadata = {} @@ -154,6 +160,8 @@ def process_package(self, package_name, parent="-"): self.sbom_package.initialise() package = self.get("Package").lower().replace("_", "-") version = self.get("Version") + if len(package) == 0: + print(f"error with {package_name} processing") self.sbom_package.set_name(package) self.sbom_package.set_version(version) if parent == "-": @@ -264,7 +272,7 @@ def process_system(self): self.sbom_relationship.set_relationship(self.parent, "DESCRIBES", distro_root) self.sbom_relationships.append(self.sbom_relationship.get_relationship()) # Get installed packages - out = self.run_program("dpkg -l") + out = self.dpkg_command("-l") for line in out: if line[:2] == "ii": # For each installed package