diff --git a/scripts/contrib/oe-image-files-spdx/.gitignore b/scripts/contrib/oe-image-files-spdx/.gitignore new file mode 100644 index 0000000000..285851c984 --- /dev/null +++ b/scripts/contrib/oe-image-files-spdx/.gitignore @@ -0,0 +1,8 @@ +*.spdx.json +*.pyc +*.bak +*.swp +*.swo +*.swn +venv/* +.venv/* diff --git a/scripts/contrib/oe-image-files-spdx/README.md b/scripts/contrib/oe-image-files-spdx/README.md new file mode 100644 index 0000000000..44f76eacd8 --- /dev/null +++ b/scripts/contrib/oe-image-files-spdx/README.md @@ -0,0 +1,24 @@ +# OE Image Files from SBoM + +This is an example python script that will list the packaged files with their +checksums based on the SPDX 3.0.1 SBoM. + +It can be used as a template for other programs to investigate output based on +OE SPDX SBoMs + +## Installation + +This project can be installed using an virtual environment: +``` +python3 -m venv .venv +.venv/bin/activate +python3 -m pip install -e '.[dev]' +``` + +## Usage + +After installing, the `oe-image-files` program can be used to show the files, e.g.: + +``` +oe-image-files core-image-minimal-qemux86-64.rootfs.spdx.json +``` diff --git a/scripts/contrib/oe-image-files-spdx/pyproject.toml b/scripts/contrib/oe-image-files-spdx/pyproject.toml new file mode 100644 index 0000000000..3fab5dd605 --- /dev/null +++ b/scripts/contrib/oe-image-files-spdx/pyproject.toml @@ -0,0 +1,23 @@ +[project] +name = "oe-image-files" +description = "Displays all packaged files on the root file system" +dynamic = ["version"] +requires-python = ">= 3.8" +readme = "README.md" + +dependencies = [ + "spdx_python_model @ git+https://github.com/spdx/spdx-python-model.git@aa40861f11d1b5d20edba7101835341a70d91179", +] + +[project.scripts] +oe-image-files = "oe_image_files:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.version] +path = "src/oe_image_files/version.py" + +[tool.hatch.metadata] +allow-direct-references = true diff --git a/scripts/contrib/oe-image-files-spdx/src/oe_image_files/__init__.py b/scripts/contrib/oe-image-files-spdx/src/oe_image_files/__init__.py new file mode 100644 index 0000000000..c28a133f2d --- /dev/null +++ b/scripts/contrib/oe-image-files-spdx/src/oe_image_files/__init__.py @@ -0,0 +1 @@ +from .main import main diff --git a/scripts/contrib/oe-image-files-spdx/src/oe_image_files/main.py b/scripts/contrib/oe-image-files-spdx/src/oe_image_files/main.py new file mode 100644 index 0000000000..8476bf6369 --- /dev/null +++ b/scripts/contrib/oe-image-files-spdx/src/oe_image_files/main.py @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: MIT + +import argparse +from pathlib import Path + + +from spdx_python_model import v3_0_1 as spdx_3_0_1 +from .version import VERSION + + +def main(): + parser = argparse.ArgumentParser( + description="Show the packaged files and checksums in an OE image from the SPDX SBoM" + ) + parser.add_argument("file", help="SPDX 3 input file", type=Path) + parser.add_argument("--version", "-V", action="version", version=VERSION) + + args = parser.parse_args() + + # Load SPDX data from file into a new object set + objset = spdx_3_0_1.SHACLObjectSet() + with args.file.open("r") as f: + d = spdx_3_0_1.JSONLDDeserializer() + d.read(f, objset) + + # Find the top level SPDX Document object + for o in objset.foreach_type(spdx_3_0_1.SpdxDocument): + doc = o + break + else: + print("ERROR: No SPDX Document found!") + return 1 + + # Find the root SBoM in the document + for o in doc.rootElement: + if isinstance(o, spdx_3_0_1.software_Sbom): + sbom = o + break + else: + print("ERROR: SBoM not found in document") + return 1 + + # Find the root file system package in the SBoM + for o in sbom.rootElement: + if ( + isinstance(o, spdx_3_0_1.software_Package) + and o.software_primaryPurpose == spdx_3_0_1.software_SoftwarePurpose.archive + ): + root_package = o + break + else: + print("ERROR: Package not found in document") + return 1 + + # Find all relationships of type "contains" that go FROM the root file + # system + files = [] + for rel in objset.foreach_type(spdx_3_0_1.Relationship): + if not rel.relationshipType == spdx_3_0_1.RelationshipType.contains: + continue + + if not rel.from_ is root_package: + continue + + # Iterate over all files in the TO of the relationship + for o in rel.to: + if not isinstance(o, spdx_3_0_1.software_File): + continue + + # Find the SHA 256 hash of the file (if any) + for h in o.verifiedUsing: + if ( + isinstance(h, spdx_3_0_1.Hash) + and h.algorithm == spdx_3_0_1.HashAlgorithm.sha256 + ): + files.append((o.name, h.hashValue)) + break + else: + files.append((o.name, "")) + + # Print files + files.sort(key=lambda x: x[0]) + for name, hash_val in files: + print(f"{name} - {hash_val}") + + return 0 diff --git a/scripts/contrib/oe-image-files-spdx/src/oe_image_files/version.py b/scripts/contrib/oe-image-files-spdx/src/oe_image_files/version.py new file mode 100644 index 0000000000..901e5110b2 --- /dev/null +++ b/scripts/contrib/oe-image-files-spdx/src/oe_image_files/version.py @@ -0,0 +1 @@ +VERSION = "0.0.1"