Skip to content

Commit

Permalink
feat(ISV-5321): update SPDX document name value (#329)
Browse files Browse the repository at this point in the history
This commit sets a name field of the component image SBOM to image
pullspec made of repository and image digest. The pullspec is parsed
from image purl.

The name is originally set in the build phase but it refers to an
internal Quay repository. In the release time we need to set it to a
public repository name that is available to customer.

JIRA: ISV-5321

Signed-off-by: Ales Raszka <[email protected]>
  • Loading branch information
Allda authored Dec 2, 2024
1 parent b718f53 commit e150237
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 16 deletions.
35 changes: 22 additions & 13 deletions sbom/test_update_component_sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@


class TestUpdateComponentSBOM(unittest.TestCase):

def test_get_component_to_purls_map(self) -> None:
release_note_images = [
{"component": "comp1", "purl": "purl1"},
Expand Down Expand Up @@ -65,7 +64,7 @@ def test_update_spdx_sbom(self) -> None:
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "purl1",
"referenceLocator": "pkg:oci/package@sha256:123",
}
],
},
Expand All @@ -75,37 +74,46 @@ def test_update_spdx_sbom(self) -> None:
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "purl2",
"referenceLocator": "pkg:oci/package@sha256:456",
}
],
},
]
}
mapping = {
"comp1": ["updated_purl1", "updated_purl2"],
"comp2": ["updated_purl3", "updated_purl4"],
"comp1": [
"pkg:oci/package@sha256:123?repository_url=quay.io/foo/bar",
"pkg:oci/package@sha256:234?repository_url=quay.io/foo/bar",
],
"comp2": [
"pkg:oci/package@sha256:456?repository_url=quay.io/foo/bar",
"pkg:oci/package@sha256:567?repository_url=quay.io/foo/bar",
],
}

update_spdx_sbom(sbom, mapping)
assert sbom == {
"name": "quay.io/foo/bar@sha256:456",
"packages": [
{
"name": "comp1",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "purl1",
"referenceLocator": "pkg:oci/package@sha256:123",
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "updated_purl1",
"referenceLocator": "pkg:oci/package@sha256:123"
"?repository_url=quay.io/foo/bar",
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "updated_purl2",
"referenceLocator": "pkg:oci/package@sha256:234"
"?repository_url=quay.io/foo/bar",
},
],
},
Expand All @@ -115,21 +123,23 @@ def test_update_spdx_sbom(self) -> None:
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "purl2",
"referenceLocator": "pkg:oci/package@sha256:456",
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "updated_purl3",
"referenceLocator": "pkg:oci/package@sha256:456"
"?repository_url=quay.io/foo/bar",
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "updated_purl4",
"referenceLocator": "pkg:oci/package@sha256:567"
"?repository_url=quay.io/foo/bar",
},
],
},
]
],
}

@patch("update_component_sbom.glob.glob")
Expand All @@ -143,7 +153,6 @@ def test_update_sboms_with_cyclonedex_format(
mock_mapping: MagicMock,
mock_glob: MagicMock,
) -> None:

# combining the content of data.json and sbom, since there can only be one read_data
# defined in the mock_open
test_cyclonedx_sbom = {"bomFormat": "CycloneDX", "releaseNotes": {"images": "foo"}}
Expand Down
30 changes: 27 additions & 3 deletions sbom/update_component_sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import argparse
import glob
import json
import logging
import os
from collections import defaultdict
from typing import DefaultDict, Dict, List
import logging

from packageurl import PackageURL

LOG = logging.getLogger("update_component_sbom")

Expand Down Expand Up @@ -54,26 +56,48 @@ def update_cyclonedx_sbom(sbom: Dict, component_to_purls_map: Dict[str, List[str
component["purl"] = component_to_purls_map[component["name"]][0]


def get_image_pullspec_from_purl(purl: str) -> str:
"""
Parse the image purl to get the image pullspec.
The pullspec is made of the repository URL and digest that is available as
the version in the purl.
Args:
purl (str): A package URL for an image.
Returns:
str: Image pullspec with repository URL and digest.
"""
parsed_purl = PackageURL.from_string(purl)
repository = parsed_purl.qualifiers["repository_url"]
return f"{repository}@{parsed_purl.version}"


def update_spdx_sbom(sbom: Dict, component_to_purls_map: Dict[str, List[str]]) -> None:
"""
Update the purl in an SBOM with SPDX format
Update the purl in an SBOM with SPDX format and set the SBOM document name
Args:
sbom: SPDX SBOM file to update.
component_to_purls_map: dictionary mapping of component names to list of purls.
"""
LOG.info("Updating SPDX sbom")
for package in sbom["packages"]:
if package["name"] in component_to_purls_map:
purls = component_to_purls_map[package["name"]]
purl_external_refs = [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": purl,
}
for purl in component_to_purls_map[package["name"]]
for purl in purls
]
package["externalRefs"].extend(purl_external_refs)

# Set the SBOM document name to the first image pullspec
sbom["name"] = get_image_pullspec_from_purl(purls[0])


def update_sboms(data_path: str, input_path: str, output_path: str) -> None:
"""
Expand Down

0 comments on commit e150237

Please sign in to comment.