Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The script will gather the images in the following way:
8. Aggregate the outputs of all `get-images.sh` scripts to one output
9. If user passed an argument `--append-images` then the script will amend a list of images we need for airgap testing

## Produce SBOM for images in a bundle
## Produce SBOM for a list of images

### Prerequisites

Expand Down Expand Up @@ -68,22 +68,26 @@ pip3 install -r scripts/requirements.txt

### Run SBOM producing script for images

You can get a list of all the SBOMs for the images used by the bundle by running the following command:
First, prepare a file containing the list of container images (one image per line), for example:

```
python3 scripts/get_bundle_images_sbom.py <bundle_path>
nginx:1.25
redis:7
ghcr.io/org/app:1.0
```

For example for the 1.9 bundle, run:
Then run:

```
python3 scripts/get_bundle_images_sbom.py releases/1.9/stable/bundle.yaml
python3 scripts/get_bundle_images_sbom.py images.txt
```

> [!WARNING]
> To produce the SBOMs of all images in the bundle (~100 images), the script can take up to a few hours depending on the network and processing resources.
> To produce the SBOMs for a large list of images (~100 images), the script can take up to a few hours depending on the network and processing resources.

The script creates a compressed file under the repo's root with the name `images_SBOM.tar.gz`. The script will store all the SBOMs there. For each image, there will be the SBOM file formatted as `<image_name>.spdx.json` inside the compressed file.
The script creates a directory named `images_SBOM` where individual SBOM files are stored as `<image_name>.spdx.json`. If an SBOM file for an image already exists, the script will skip regenerating it (caching behavior).

After processing, the script creates a compressed file `images_SBOM.tar.gz` containing all generated SBOM files.

---

Expand Down
54 changes: 35 additions & 19 deletions scripts/get_bundle_images_sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,41 @@
import logging
import os
from pathlib import Path
import shutil
import subprocess
import tarfile

from get_all_images import get_bundle_images

SBOM_DIR = "images_SBOM"

log = logging.getLogger(__name__)


def get_bundle_images_sbom(bundle_path: str) -> None:
def get_images_sbom(images: list[str]) -> None:

log.info(f"Images received as input: {images}")

# get a list of images in the bundle
bundle_images = get_bundle_images(bundle_path)
sbom_path = Path(SBOM_DIR)
sbom_path.mkdir(parents=True, exist_ok=True)

log.info(f"Images gathered from the bundle: {bundle_images}")
total = len(images)

Path(SBOM_DIR).mkdir(parents=True, exist_ok=True)
for idx, image in enumerate(images, start=1):
output_file = sbom_path / f"{image}.spdx.json"

if output_file.exists():
log.info(
f"[{idx}/{total}] Skipping {image}, SBOM already exists at {output_file}"
)
continue

for image in bundle_images:
log.info(f"Creating SBOM for {image}")
log.info(f"[{idx}/{total}] Creating SBOM for {image}")
try:
subprocess.check_call(
['syft', 'scan', image, '-o', f'spdx-json={image}.spdx.json'],
["syft", "scan", image, "-o", f"spdx-json={image}.spdx.json"],
cwd=SBOM_DIR,
)
except subprocess.CalledProcessError as e:
log.error(f"Error scanning {image}: {e.output}")
raise e
log.error(f"Error scanning {image}: {e}")
raise

sbom_files = os.listdir(SBOM_DIR)

Expand All @@ -40,17 +45,28 @@ def get_bundle_images_sbom(bundle_path: str) -> None:
for file in sbom_files:
tar.add(f"{SBOM_DIR}/{file}")

# Cleanup files
shutil.rmtree(SBOM_DIR)


def main():
parser = argparse.ArgumentParser(description="Get SBOMs for all images in a bundle.")
parser.add_argument("bundle", help="the bundle.yaml file to be scanned.")
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s - %(message)s",
)
parser = argparse.ArgumentParser(description="Get SBOMs for a list of images.")
parser.add_argument(
"images_file",
help="Path to a file containing container images (one per line).",
)

args = parser.parse_args()

get_bundle_images_sbom(args.bundle)
images_file_path = Path(args.images_file)
if not images_file_path.exists():
raise FileNotFoundError(f"Images file not found: {images_file_path}")

with images_file_path.open("r") as f:
images_list = [line.strip() for line in f if line.strip()]

get_images_sbom(images_list)


if __name__ == "__main__":
Expand Down