From 72fbb39c9a39cc318f63eae23868e73c16eab4df Mon Sep 17 00:00:00 2001 From: Alexey Ovchinnikov Date: Mon, 13 Jan 2025 18:29:47 -0600 Subject: [PATCH] Adding SPDX support Added SDPX format support for SBOM Support for SPDX format was added to fetch-depds command and also to merge_syft_sboms. No changes were made in particular package manager generating components which are then converted to cyclonedx format. SPDX sbom can be obtained by calling Sbom.to_spdx(). New switch sbom-type was added to merge_syft_sboms, so user can choose which output format should be generated - default is cyclonedx. Once all tooling is ready to consume spdx sboms, cutoff changes in this repository can be started. SPDXRef-DocumentRoot-File- includes all spdx packages and is set to be described by SPDXRef-DOCUMENT. This way of spdx generation is closer to way syft generates spdx Co-authered-by: Alexey Ovchinnikov Signed-off-by: Jindrich Luza --- cachi2/core/models/sbom.py | 321 ++- cachi2/interface/cli.py | 63 +- pyproject.toml | 1 + tests/unit/conftest.py | 9 + tests/unit/data/alpine.pretty.json | 2514 +++++++++++++++++ tests/unit/data/sboms/cachi2.bom.spdx.json | 195 ++ tests/unit/data/sboms/merged.bom.spdx.json | 488 ++++ tests/unit/data/sboms/syft.bom.spdx.json | 244 ++ ...thing.more.simple.0.100.0.spdx.pretty.json | 112 + .../something.simple0.100.0.spdx.pretty.json | 87 + tests/unit/models/test_sbom.py | 1173 +++++++- tests/unit/test_cli.py | 86 +- tests/unit/test_merge_spdx.py | 353 +++ 13 files changed, 5627 insertions(+), 19 deletions(-) create mode 100644 tests/unit/data/alpine.pretty.json create mode 100644 tests/unit/data/sboms/cachi2.bom.spdx.json create mode 100644 tests/unit/data/sboms/merged.bom.spdx.json create mode 100644 tests/unit/data/sboms/syft.bom.spdx.json create mode 100644 tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json create mode 100644 tests/unit/data/something.simple0.100.0.spdx.pretty.json create mode 100644 tests/unit/test_merge_spdx.py diff --git a/cachi2/core/models/sbom.py b/cachi2/core/models/sbom.py index fd8fc90b2..c0f6f52d8 100644 --- a/cachi2/core/models/sbom.py +++ b/cachi2/core/models/sbom.py @@ -2,16 +2,20 @@ import hashlib import json import logging -from functools import reduce -from itertools import groupby +from collections import defaultdict +from functools import cached_property, partial, reduce +from itertools import chain, groupby +from pathlib import Path from typing import Annotated, Any, Dict, Iterable, Literal, Optional, Union from urllib.parse import urlparse import pydantic from packageurl import PackageURL +from typing_extensions import Self from cachi2.core.models.property_semantics import Property, PropertySet from cachi2.core.models.validators import unique_sorted +from cachi2.core.utils import first_for, partition_by log = logging.getLogger(__name__) @@ -94,17 +98,109 @@ class Sbom(pydantic.BaseModel): https://cyclonedx.org/docs/1.4/json """ + model_config = pydantic.ConfigDict(extra="forbid") + bom_format: Literal["CycloneDX"] = pydantic.Field(alias="bomFormat", default="CycloneDX") components: list[Component] = [] metadata: Metadata = Metadata() spec_version: str = pydantic.Field(alias="specVersion", default="1.4") version: int = 1 + def __add__(self, other: Union["Sbom", "SPDXSbom"]) -> "Sbom": + if isinstance(other, self.__class__): + return Sbom( + components=merge_component_properties( + chain.from_iterable(s.components for s in [self, other]) + ) + ) + else: + return self + other.to_cyclonedx() + @pydantic.field_validator("components") def _unique_components(cls, components: list[Component]) -> list[Component]: """Sort and de-duplicate components.""" return unique_sorted(components, by=lambda component: component.key()) + def to_cyclonedx(self) -> Self: + """Return self, self is already the right type of Sbom.""" + # This is a short-cut, but since it is unlikely that we would ever add more Sbom types + # it is acceptable. If, however this ever happens a proper base class will be needed. + return self + + def to_spdx(self, doc_namespace: str) -> "SPDXSbom": + """Convert a CycloneDX SBOM to an SPDX SBOM. + + Args: + doc_namespace: SPDX document namespace. Namespace is URI of indicating + + """ + + def create_document_root() -> SPDXPackage: + return SPDXPackage(name="", versionInfo="", SPDXID="SPDXRef-DocumentRoot-File-") + + def create_root_relationship() -> SPDXRelation: + return SPDXRelation( + spdxElementId="SPDXRef-DOCUMENT", + comment="", + relatedSpdxElement="SPDXRef-DocumentRoot-File-", + relationshipType="DESCRIBES", + ) + + def link_to_root(packages: Union[list[SPDXPackage], list[SPDXFile]]) -> list[SPDXRelation]: + relationships, root_id, rtype = [], "SPDXRef-DocumentRoot-File-", "CONTAINS" + pRel = partial(SPDXRelation, spdxElementId=root_id, comment="", relationshipType=rtype) + for package in packages: + if package.SPDXID == "SPDXRef-DocumentRoot-File-": + continue + relationships.append(pRel(relatedSpdxElement=package.SPDXID)) + return relationships + + def libs_to_packages(libraries: list[Component]) -> list[SPDXPackage]: + packages, annottr, now = [], "Tool: cachi2:jsonencoded", spdx_now() + args = dict(annotator=annottr, annotationDate=now, annotationType="OTHER") + pAnnotation = partial(SPDXPackageAnnotation, **args) + + # noqa for trivial helpers. + mkcomm = lambda p: json.dumps(dict(name=f"{p.name}", value=f"{p.value}")) # noqa: E731 + hashdict = lambda c: dict(name=c.name, version=c.version, purl=c.purl) # noqa: E731 + erefbase = dict(referenceCategory="PACKAGE-MANAGER", referenceType="purl") + erefdict = lambda c: dict(referenceLocator=c.purl, **erefbase) # noqa: E731 + + for component in libraries: + package_hash = SPDXPackage._calculate_package_hash_from_dict(hashdict(component)) + packages.append( + SPDXPackage( + SPDXID=f"SPDXRef-Package-{component.name}-{component.version}-{package_hash}", + name=component.name, + versionInfo=component.version, + externalRefs=[erefdict(component)], + annotations=[pAnnotation(comment=mkcomm(p)) for p in component.properties], + ) + ) + return packages + + def files_to_packages(cydxfiles: list[Component]) -> list[SPDXFile]: + return [SPDXFile(SPDXID="SPDXRef-File-{f.name}", fileName=f.name) for f in cydxfiles] + + # Main function body. + cydxfiles, libraries = partition_by(lambda c: c.type == "library", self.components) + # mypy is upset by partition_by being broadly typed. + packages = [create_document_root()] + libs_to_packages(libraries) # type: ignore + files = files_to_packages(cydxfiles) # type: ignore + relationships = [create_root_relationship()] + link_to_root(packages) + link_to_root(files) + # noqa for a trivial helper. + creator = lambda tool: [f"Tool: {tool.name}", f"Organization: {tool.vendor}"] # noqa: E731 + return SPDXSbom( + packages=packages, + relationships=relationships, + files=files, + documentNamespace=doc_namespace, + creationInfo=SPDXCreationInfo( + creators=sum([creator(tool) for tool in self.metadata.tools], []), + created=spdx_now(), + ), + ) + class SPDXPackageExternalRefReferenceLocatorURI(pydantic.BaseModel): """SPDX Package External Reference with URI reference locator.""" @@ -376,6 +472,223 @@ def __hash__(self) -> int: ) +def _unique(elements: Iterable) -> list: + out, seen = [], set() + for el in elements: + if el in seen: + continue + seen.add(el) + out.append(el) + return out + + +class SPDXSbom(pydantic.BaseModel): + """Software bill of materials in the SPDX format. + + See full specification at: + https://spdx.github.io/spdx-spec/v2.3 + """ + + # NOTE: The model is intentionally made non-strict for now because a strict model rejects + # SBOMs generated by Syft. It is unclear at the moment if additional preprocessing will + # be happening or desired. + + spdxVersion: Literal["SPDX-2.3"] = "SPDX-2.3" + SPDXID: Literal["SPDXRef-DOCUMENT"] = "SPDXRef-DOCUMENT" + dataLicense: Literal["CC0-1.0"] = "CC0-1.0" + name: str = "" + documentNamespace: str + + creationInfo: SPDXCreationInfo + packages: list[SPDXPackage] = [] + files: list[SPDXFile] = [] + relationships: list[SPDXRelation] = [] + + def __hash__(self) -> int: + return hash( + hash(self.name + self.documentNamespace) + + hash(SPDXCreationInfo) + + sum(hash(p) for p in self.packages) + + sum(hash(f) for f in self.files) + + sum(hash(r) for r in self.relationships) + ) + + @classmethod + def from_file(cls, path: Path) -> "SPDXSbom": + """Consume a SPDX json directly from a file.""" + return cls.model_validate_json(path.read_text()) + + @staticmethod + def deduplicate_spdx_packages(items: Iterable[SPDXPackage]) -> list[SPDXPackage]: + """Deduplicate SPDX packages and merge external references. + + Deduplication is very conservative and does not consider two packages same if + their purls differ even if their type, name and version match. A package will be + dropped iff it is a full purl match. + """ + unique_items: dict[int, SPDXPackage] = {} + for item in items: + purls = _extract_purls(item.externalRefs) + if purls: + purl_key = hash(sum(hash(p) for p in _parse_purls(purls))) + else: + # This is likely just the root. + log.warning(f"No purls found for {item}.") + purl_key = hash(("", item.name, item.versionInfo or "")) + + if purl_key in unique_items: + unique_items[purl_key].externalRefs.extend(item.externalRefs) + unique_items[purl_key].annotations.extend(item.annotations) + else: + unique_items[purl_key] = item.model_copy(deep=True) + + for item in unique_items.values(): + item.externalRefs = sorted( + set(item.externalRefs), + key=lambda ref: (ref.referenceLocator, ref.referenceType, ref.referenceCategory), + ) + item.annotations = sorted( + set(item.annotations), + key=lambda ann: (ann.annotator, ann.annotationDate, ann.comment), + ) + return sorted(unique_items.values(), key=lambda item: (item.name, item.versionInfo or "")) + + @pydantic.field_validator("packages") + def _unique_packages(cls, packages: list[SPDXPackage]) -> list[SPDXPackage]: + """Sort and de-duplicate components.""" + return cls.deduplicate_spdx_packages(packages) + + @cached_property + def root_id(self) -> str: + """Return the root_id of this SBOM.""" + direct_relationships, inverse_relationships = defaultdict(list), dict() + for rel in self.relationships: + direct_relationships[rel.spdxElementId].append(rel.relatedSpdxElement) + inverse_relationships[rel.relatedSpdxElement] = rel.spdxElementId + # noqa because the name is bound to make local intent clearer and + # first_for() call easier to follow. + unidirectionally_related_package = ( + lambda p: inverse_relationships.get(p) == self.SPDXID # noqa: E731 + ) + # Note: defaulting to top-level SPDXID is inherited from the original implementation. + # It is unclear if it is really needed, but is left around to match the precedent. + root_id = first_for(unidirectionally_related_package, direct_relationships, self.SPDXID) + return root_id + + # NOTE: having this as cached will cause trouble when sequentially + # constructing the object off of an empty state. + @property + def non_root_packages(self) -> list[SPDXPackage]: + """Return non-root packages.""" + return [p for p in self.packages if p.SPDXID != self.root_id] + + @staticmethod + def retarget_and_prune_relationships( + from_sbom: "SPDXSbom", + to_sbom: "SPDXSbom", + ) -> list[SPDXRelation]: + """Retarget and prune relationships.""" + out = [] + for r in from_sbom.relationships: + # Do a copy to ensure we are not pulling a carpet from underneath us: + new_rel = r.model_copy(deep=True) + if new_rel.spdxElementId == from_sbom.root_id: + new_rel.spdxElementId = to_sbom.root_id + if new_rel.relatedSpdxElement == from_sbom.root_id: + new_rel.spdxElementId = to_sbom.SPDXID + # Old top-level "DESCRIBES" must go: + if not ( + new_rel.relatedSpdxElement == from_sbom.root_id + and new_rel.relationshipType == "DESCRIBES" + ): + out.append(new_rel) + return out + + def __add__(self, other: Union["SPDXSbom", Sbom]) -> "SPDXSbom": + if isinstance(other, self.__class__): + # Packages are not going to be modified so it is OK to just pass + # references around. + merged_packages = self.packages + other.non_root_packages + # Relationships, on the other hand, are amended, so new + # relationships will be constructed. Further, identical + # relationships should be dropped. Deduplication based on building + # a set is considered safe because all fields of all elements are + # used to compute a hash. + processed_other = self.retarget_and_prune_relationships(from_sbom=other, to_sbom=self) + merged_relationships = _unique(self.relationships + processed_other) + # The same as packages: files are not modified, so keeping them as is. + # Identical file entries should be skipped. + merged_files = _unique(self.files + other.files) + res = self.model_copy( + update={ + # At the moment of writing pydantic does not deem it necessary to + # validate updated fields because we should just trust them [1]. + "packages": self.deduplicate_spdx_packages(merged_packages), + "relationships": merged_relationships, + "files": merged_files, + }, + deep=True, + ) + return res + elif isinstance(other, Sbom): + return self + other.to_spdx(doc_namespace="NOASSERTION") + else: + self_class = self.__class__.__name__ + other_class = other.__class__.__name__ + raise ValueError(f"Cannot merge {other_class} to {self_class}") + + def to_spdx(self, *a: Any, **k: Any) -> Self: + """Return self, ignore arguments, self is already a SPDX document.""" + # This is a short-cut, but since it is unlikely that we would ever add more Sbom types + # it is acceptable. If, however this ever happens a proper base class will be needed. + return self + + def to_cyclonedx(self) -> Sbom: + """Convert a SPDX SBOM to a CycloneDX SBOM.""" + components = [] + for package in self.packages: + properties = [ + ( + Property(**json.loads(an.comment)) + if an.annotator.endswith(":jsonencoded") + else Property(name=an.annotator, value=an.comment) + ) + for an in package.annotations + ] + pComponent = partial( + Component, name=package.name, version=package.versionInfo, properties=properties + ) + purls = _extract_purls(package.externalRefs) + + # cyclonedx doesn't support multiple purls, therefore + # new component is created for each purl + components += [pComponent(purl=purl) for purl in purls] + # if there's no purl and no package name or version, it's just wrapping element for + # spdx package which is one layer bellow SPDXDocument in relationships + if not any((purls, package.name, package.versionInfo)): + continue + # if there's no purl, add it as single component + elif not purls: + components.append(pComponent(purl="")) + tools = [] + name, vendor = None, None + # Following approach is used as position of "Organization" and "Tool" is not + # guaranteed by the standard + for creator in self.creationInfo.creators: + if creator.startswith("Organization:"): + vendor = creator.replace("Organization:", "").strip() + elif creator.startswith("Tool:"): + name = creator.replace("Tool:", "").strip() + if name is not None and vendor is not None: + tools.append(Tool(vendor=vendor, name=name)) + name, vendor = None, None + + return Sbom( + components=components, + metadata=Metadata(tools=tools), + ) + + def merge_component_properties(components: Iterable[Component]) -> list[Component]: """Sort and de-duplicate components while merging their `properties`.""" components = sorted(components, key=Component.key) @@ -389,3 +702,7 @@ def merge_component_group(component_group: Iterable[Component]) -> Component: return component.model_copy(update={"properties": merged_prop_set.to_properties()}) return [merge_component_group(g) for _, g in grouped_components] + + +# References +# [1] https://github.com/pydantic/pydantic/blob/6fa92d139a297a26725dec0a7f9b0cce912d6a7f/pydantic/main.py#L383 diff --git a/cachi2/interface/cli.py b/cachi2/interface/cli.py index 8bba86a22..a7c2f7290 100644 --- a/cachi2/interface/cli.py +++ b/cachi2/interface/cli.py @@ -1,12 +1,12 @@ +import enum import functools import importlib.metadata import json import logging import shutil import sys -from itertools import chain from pathlib import Path -from typing import Any, Callable, Optional +from typing import Any, Callable, List, Optional, Union import pydantic import typer @@ -16,7 +16,7 @@ from cachi2.core.extras.envfile import EnvFormat, generate_envfile from cachi2.core.models.input import Flag, PackageInput, Request, parse_user_input from cachi2.core.models.output import BuildConfig -from cachi2.core.models.sbom import Sbom, merge_component_properties +from cachi2.core.models.sbom import Sbom, SPDXSbom, spdx_now from cachi2.core.resolver import inject_files_post, resolve_packages, supported_package_managers from cachi2.core.rooted_path import RootedPath from cachi2.interface.logging import LogLevel, setup_logging @@ -37,6 +37,20 @@ ) +class SBOMFormat(str, enum.Enum): + """The type of SBOM to generate.""" + + cyclonedx = "cyclonedx" + spdx = "spdx" + + +SBOM_TYPE_OPTION = typer.Option( + SBOMFormat.cyclonedx, + "--sbom-output-type", + help=("Format of generated SBOM. Default is CycloneDX"), +) + + Paths = list[Path] @@ -179,6 +193,7 @@ def fetch_deps( "already have a vendor/ directory (will fail if changes would be made)." ), ), + sbom_type: SBOMFormat = SBOM_TYPE_OPTION, ) -> None: """Fetch dependencies for supported package managers. @@ -284,7 +299,10 @@ def combine_option_and_json_flags(json_flags: list[Flag]) -> list[str]: request_output.build_config.model_dump_json(indent=2, exclude_none=True) ) - sbom = request_output.generate_sbom() + if sbom_type == SBOMFormat.cyclonedx: + sbom: Union[Sbom, SPDXSbom] = request_output.generate_sbom() + else: + sbom = request_output.generate_sbom().to_spdx(doc_namespace="NOASSERTION") request.output_dir.join_within_root("bom.json").path.write_text( # the Sbom model has camelCase aliases in some fields sbom.model_dump_json(indent=2, by_alias=True, exclude_none=True) @@ -399,26 +417,43 @@ def merge_sboms( help="Names of files with SBOMs to merge.", ), output_sbom_file_name: Optional[Path] = OUTFILE_OPTION, + sbom_type: SBOMFormat = SBOM_TYPE_OPTION, + sbom_name: Optional[str] = typer.Option( + None, "--sbom-name", help="Name of the resulting merged SBOM." + ), ) -> None: """Merge two or more SBOMs into one. - The command works with Cachi2-generated SBOMs only. You might want to run + The command works with Cachi2-generated SBOMs and with a supported subset of + SPDX SBOMs. You might want to run - cachi2 fetch-deps + cachi2 fetch-deps first to produce SBOMs to merge. """ - sboms_to_merge = [] + sboms_to_merge: List[Union[SPDXSbom, Sbom]] = [] for sbom_file in sbom_files_to_merge: + sbom_dict = json.loads(sbom_file.read_text()) + # Remove extra fields which are not in Sbom or SPDXSbom models + # Both SBom and SPDXSBom models are only subset of cyclonedx and SPDX specifications + # Therefore we need to make sure only fields accepted by the models are present try: - sboms_to_merge.append(Sbom.model_validate_json(sbom_file.read_text())) + sboms_to_merge.append(Sbom(**sbom_dict)) except pydantic.ValidationError: - raise UnexpectedFormat(f"{sbom_file} does not appear to be a valid Cachi2 SBOM.") - sbom = Sbom( - components=merge_component_properties( - chain.from_iterable(s.components for s in sboms_to_merge) - ) - ) + try: + sboms_to_merge.append(SPDXSbom(**sbom_dict)) + except pydantic.ValidationError: + raise UnexpectedFormat(f"{sbom_file} does not appear to be a valid Cachi2 SBOM.") + # start_sbom will later coerce every other SBOM to its type. + start_sbom: Union[Sbom, SPDXSbom] # this visual noise is demanded by mypy. + if sbom_type == SBOMFormat.cyclonedx: + start_sbom = sboms_to_merge[0].to_cyclonedx() + else: + start_sbom = sboms_to_merge[0].to_spdx(doc_namespace="NOASSERTION") + if sbom_name is not None: + start_sbom.name = sbom_name + start_sbom.creationInfo.created = spdx_now() + sbom = sum(sboms_to_merge[1:], start=start_sbom) sbom_json = sbom.model_dump_json(indent=2, by_alias=True, exclude_none=True) if output_sbom_file_name is not None: diff --git a/pyproject.toml b/pyproject.toml index decd06b95..d2a339042 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ dependencies = [ "tomli", "typer", "createrepo-c", + "packageurl-python" ] [project.optional-dependencies] dev = [ diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 2de3f7350..4af1e8941 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,5 +1,7 @@ import tarfile from pathlib import Path +from typing import Generator +from unittest import mock import git import pytest @@ -57,3 +59,10 @@ def input_request(tmp_path: Path, request: pytest.FixtureRequest) -> Request: output_dir=tmp_path / "output", packages=package_input, ) + + +@pytest.fixture +def isodate() -> Generator: + with mock.patch("datetime.datetime") as mock_datetime: + mock_datetime.now.return_value.isoformat.return_value = "2021-07-01T00:00:00.000000" + yield mock_datetime diff --git a/tests/unit/data/alpine.pretty.json b/tests/unit/data/alpine.pretty.json new file mode 100644 index 000000000..0c5e02c6a --- /dev/null +++ b/tests/unit/data/alpine.pretty.json @@ -0,0 +1,2514 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "alpine", + "documentNamespace": "https://anchore.com/syft/image/alpine-23a16066-6f89-4a51-9c35-aa776bcc77d7", + "creationInfo": { + "licenseListVersion": "3.22", + "creators": [ + "Organization: Anchore, Inc", + "Tool: syft-0.100.0" + ], + "created": "2024-12-11T00:58:09Z" + }, + "packages": [ + { + "name": "alpine-baselayout", + "SPDXID": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "versionInfo": "3.6.8-r0", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "Alpine base dir structure and init scripts", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_baselayout:alpine-baselayout:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_baselayout:alpine_baselayout:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine-baselayout:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine_baselayout:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/alpine-baselayout@3.6.8-r0?arch=x86_64&distro=alpine-3.21.0" + } + ] + }, + { + "name": "alpine-baselayout-data", + "SPDXID": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "versionInfo": "3.6.8-r0", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "Alpine base dir structure and init scripts", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-baselayout-data:alpine-baselayout-data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-baselayout-data:alpine_baselayout_data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_baselayout_data:alpine-baselayout-data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_baselayout_data:alpine_baselayout_data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-baselayout:alpine-baselayout-data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-baselayout:alpine_baselayout_data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_baselayout:alpine-baselayout-data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_baselayout:alpine_baselayout_data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine-baselayout-data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine_baselayout_data:3.6.8-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/alpine-baselayout-data@3.6.8-r0?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.21.0" + } + ] + }, + { + "name": "alpine-keys", + "SPDXID": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "versionInfo": "2.5-r0", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://alpinelinux.org", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", + "copyrightText": "NOASSERTION", + "description": "Public keys for Alpine Linux packages", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-keys:alpine-keys:2.5-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-keys:alpine_keys:2.5-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_keys:alpine-keys:2.5-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_keys:alpine_keys:2.5-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine-keys:2.5-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine_keys:2.5-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/alpine-keys@2.5-r0?arch=x86_64&distro=alpine-3.21.0" + } + ] + }, + { + "name": "alpine-release", + "SPDXID": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "versionInfo": "3.21.0-r0", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://alpinelinux.org", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", + "copyrightText": "NOASSERTION", + "description": "Alpine release data", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-release:alpine-release:3.21.0-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine-release:alpine_release:3.21.0-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_release:alpine-release:3.21.0-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine_release:alpine_release:3.21.0-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine-release:3.21.0-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:alpine:alpine_release:3.21.0-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/alpine-release@3.21.0-r0?arch=x86_64&upstream=alpine-base&distro=alpine-3.21.0" + } + ] + }, + { + "name": "apk-tools", + "SPDXID": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "versionInfo": "2.14.6-r2", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://gitlab.alpinelinux.org/alpine/apk-tools", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "Alpine Package Keeper - package manager for alpine", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:apk-tools:apk-tools:2.14.6-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:apk-tools:apk_tools:2.14.6-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:apk_tools:apk-tools:2.14.6-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:apk_tools:apk_tools:2.14.6-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:apk:apk-tools:2.14.6-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:apk:apk_tools:2.14.6-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/apk-tools@2.14.6-r2?arch=x86_64&distro=alpine-3.21.0" + } + ] + }, + { + "name": "busybox", + "SPDXID": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "versionInfo": "1.37.0-r8", + "supplier": "Person: Sören Tempel ", + "originator": "Person: Sören Tempel ", + "downloadLocation": "https://busybox.net/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "Size optimized toolbox of many common UNIX utilities", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox:busybox:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/busybox@1.37.0-r8?arch=x86_64&distro=alpine-3.21.0" + } + ] + }, + { + "name": "busybox-binsh", + "SPDXID": "SPDXRef-Package-apk-busybox-binsh-79dd77b653445aa8", + "versionInfo": "1.37.0-r8", + "supplier": "Person: Sören Tempel ", + "originator": "Person: Sören Tempel ", + "downloadLocation": "https://busybox.net/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "busybox ash /bin/sh", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox-binsh:busybox-binsh:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox-binsh:busybox_binsh:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox_binsh:busybox-binsh:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox_binsh:busybox_binsh:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox:busybox-binsh:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:busybox:busybox_binsh:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/busybox-binsh@1.37.0-r8?arch=x86_64&upstream=busybox&distro=alpine-3.21.0" + } + ] + }, + { + "name": "ca-certificates-bundle", + "SPDXID": "SPDXRef-Package-apk-ca-certificates-bundle-67dec00a55941450", + "versionInfo": "20241010-r0", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "(MPL-2.0 AND MIT)", + "copyrightText": "NOASSERTION", + "description": "Pre generated bundle of Mozilla certificates", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca-certificates-bundle:ca_certificates_bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca_certificates_bundle:ca-certificates-bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca_certificates_bundle:ca_certificates_bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca-certificates:ca-certificates-bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca-certificates:ca_certificates_bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca_certificates:ca-certificates-bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca_certificates:ca_certificates_bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:mozilla:ca-certificates-bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:mozilla:ca_certificates_bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca:ca-certificates-bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ca:ca_certificates_bundle:20241010-r0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/ca-certificates-bundle@20241010-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.21.0" + } + ] + }, + { + "name": "libcrypto3", + "SPDXID": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "versionInfo": "3.3.2-r4", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://www.openssl.org/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "Apache-2.0", + "copyrightText": "NOASSERTION", + "description": "Crypto library from openssl", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libcrypto3:libcrypto3:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libcrypto3:libcrypto:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libcrypto:libcrypto3:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libcrypto:libcrypto:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/libcrypto3@3.3.2-r4?arch=x86_64&upstream=openssl&distro=alpine-3.21.0" + } + ] + }, + { + "name": "libssl3", + "SPDXID": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "versionInfo": "3.3.2-r4", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://www.openssl.org/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "Apache-2.0", + "copyrightText": "NOASSERTION", + "description": "SSL shared libraries", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libssl3:libssl3:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libssl3:libssl:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libssl:libssl3:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:libssl:libssl:3.3.2-r4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/libssl3@3.3.2-r4?arch=x86_64&upstream=openssl&distro=alpine-3.21.0" + } + ] + }, + { + "name": "musl", + "SPDXID": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "versionInfo": "1.2.5-r8", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://musl.libc.org/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", + "copyrightText": "NOASSERTION", + "description": "the musl c library (libc) implementation", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl-libc:musl:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl_libc:musl:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl:musl:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/musl@1.2.5-r8?arch=x86_64&distro=alpine-3.21.0" + } + ] + }, + { + "name": "musl-utils", + "SPDXID": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "versionInfo": "1.2.5-r8", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://musl.libc.org/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "(MIT AND BSD-2-Clause AND GPL-2.0-or-later)", + "copyrightText": "NOASSERTION", + "description": "the musl c library (libc) implementation", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl-utils:musl-utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl-utils:musl_utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl_utils:musl-utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl_utils:musl_utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl-libc:musl-utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl-libc:musl_utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl:musl-utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:musl:musl_utils:1.2.5-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/musl-utils@1.2.5-r8?arch=x86_64&upstream=musl&distro=alpine-3.21.0" + } + ] + }, + { + "name": "scanelf", + "SPDXID": "SPDXRef-Package-apk-scanelf-5732e2d080a0a28c", + "versionInfo": "1.3.8-r1", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "Scan ELF binaries for stuff", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:scanelf:scanelf:1.3.8-r1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/scanelf@1.3.8-r1?arch=x86_64&upstream=pax-utils&distro=alpine-3.21.0" + } + ] + }, + { + "name": "ssl_client", + "SPDXID": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "versionInfo": "1.37.0-r8", + "supplier": "Person: Sören Tempel ", + "originator": "Person: Sören Tempel ", + "downloadLocation": "https://busybox.net/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-only", + "copyrightText": "NOASSERTION", + "description": "External ssl_client for busybox wget", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ssl-client:ssl-client:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ssl-client:ssl_client:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ssl_client:ssl-client:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ssl_client:ssl_client:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ssl:ssl-client:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ssl:ssl_client:1.37.0-r8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/ssl_client@1.37.0-r8?arch=x86_64&upstream=busybox&distro=alpine-3.21.0" + } + ] + }, + { + "name": "zlib", + "SPDXID": "SPDXRef-Package-apk-zlib-cd1596a99504840a", + "versionInfo": "1.3.1-r2", + "supplier": "Person: Natanael Copa ", + "originator": "Person: Natanael Copa ", + "downloadLocation": "https://zlib.net/", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "Zlib", + "copyrightText": "NOASSERTION", + "description": "A compression/decompression Library", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:zlib:zlib:1.3.1-r2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:apk/alpine/zlib@1.3.1-r2?arch=x86_64&distro=alpine-3.21.0" + } + ] + }, + { + "name": "alpine", + "SPDXID": "SPDXRef-DocumentRoot-Image-alpine", + "versionInfo": "sha256:2c43f33bd1502ec7818bce9eea60e062d04eeadc4aa31cad9dabecb1e48b647b", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "2c43f33bd1502ec7818bce9eea60e062d04eeadc4aa31cad9dabecb1e48b647b" + } + ], + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:oci/alpine@sha256:2c43f33bd1502ec7818bce9eea60e062d04eeadc4aa31cad9dabecb1e48b647b?arch=amd64" + } + ], + "primaryPackagePurpose": "CONTAINER" + } + ], + "files": [ + { + "fileName": "/bin/busybox", + "SPDXID": "SPDXRef-File-bin-busybox-468f4889734362f5", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/alpine-release", + "SPDXID": "SPDXRef-File-etc-alpine-release-fd2f197c9c548c66", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-4a6a0840.rsa.pub-e31ca5e7df53db52", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5243ef4b.rsa.pub-dd82cc7a531a4f8a", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5261cecb.rsa.pub-8503eca0329855ac", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-6165ee59.rsa.pub-9fc2b76378071420", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/apk/keys/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-61666e3f.rsa.pub-e174ab22f2beeb60", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/busybox-paths.d/busybox", + "SPDXID": "SPDXRef-File-etc-busybox-paths.d-busybox-3cbfe16761873575", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/crontabs/root", + "SPDXID": "SPDXRef-File-etc-crontabs-root-86d9785f3abff156", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/fstab", + "SPDXID": "SPDXRef-File-etc-fstab-b80c3cb56c980b08", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/group", + "SPDXID": "SPDXRef-File-etc-group-7db3625241f6856d", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/hostname", + "SPDXID": "SPDXRef-File-etc-hostname-e7d0d8adb0625ce4", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/hosts", + "SPDXID": "SPDXRef-File-etc-hosts-6ac577ecad2ec5b0", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/inittab", + "SPDXID": "SPDXRef-File-etc-inittab-4d8d5f0587154018", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/issue", + "SPDXID": "SPDXRef-File-etc-issue-b413295f2e1636b9", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/logrotate.d/acpid", + "SPDXID": "SPDXRef-File-etc-logrotate.d-acpid-e680a539c74e700d", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/modprobe.d/aliases.conf", + "SPDXID": "SPDXRef-File-etc-modprobe.d-aliases.conf-b29978812ba855fa", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/modprobe.d/blacklist.conf", + "SPDXID": "SPDXRef-File-etc-modprobe.d-blacklist.conf-b58b1a80d90b95b5", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/modprobe.d/i386.conf", + "SPDXID": "SPDXRef-File-etc-modprobe.d-i386.conf-f54933ed0cb86bd2", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/modprobe.d/kms.conf", + "SPDXID": "SPDXRef-File-etc-modprobe.d-kms.conf-bae009a41fd01a1d", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/modules", + "SPDXID": "SPDXRef-File-etc-modules-a3196be7d01a0aec", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/motd", + "SPDXID": "SPDXRef-File-etc-motd-4eb653645b7f13ee", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/network/if-up.d/dad", + "SPDXID": "SPDXRef-File-etc-network-if-up.d-dad-ae53711b4c2ae508", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/nsswitch.conf", + "SPDXID": "SPDXRef-File-etc-nsswitch.conf-668425e791126652", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/passwd", + "SPDXID": "SPDXRef-File-etc-passwd-52c8226245401a54", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/profile", + "SPDXID": "SPDXRef-File-etc-profile-773e11b623fa2206", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/profile.d/20locale.sh", + "SPDXID": "SPDXRef-File-etc-profile.d-20locale.sh-ff541b2c108c7e57", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/profile.d/README", + "SPDXID": "SPDXRef-File-etc-profile.d-README-22834df769d2095e", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/profile.d/color_prompt.sh.disabled", + "SPDXID": "SPDXRef-File-etc-profile.d-color-prompt.sh.disabled-96f77289dc1c6105", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/protocols", + "SPDXID": "SPDXRef-File-etc-protocols-2ebaf2d5275a87c3", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/secfixes.d/alpine", + "SPDXID": "SPDXRef-File-etc-secfixes.d-alpine-31f4ab357226cd60", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/securetty", + "SPDXID": "SPDXRef-File-etc-securetty-f5ddc6e8bf552536", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/services", + "SPDXID": "SPDXRef-File-etc-services-5e64bee7d0512520", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/shadow", + "SPDXID": "SPDXRef-File-etc-shadow-e52a006d3d1244db", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/shells", + "SPDXID": "SPDXRef-File-etc-shells-5f409797065e8a24", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/ssl/certs/ca-certificates.crt", + "SPDXID": "SPDXRef-File-etc-ssl-certs-ca-certificates.crt-860b9d53645feb47", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/ssl/ct_log_list.cnf", + "SPDXID": "SPDXRef-File-etc-ssl-ct-log-list.cnf-97ee0f53ad135fdd", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/ssl/ct_log_list.cnf.dist", + "SPDXID": "SPDXRef-File-etc-ssl-ct-log-list.cnf.dist-7386b7ea31c8254f", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/ssl/openssl.cnf", + "SPDXID": "SPDXRef-File-etc-ssl-openssl.cnf-9afb2be8c7429f0d", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/ssl/openssl.cnf.dist", + "SPDXID": "SPDXRef-File-etc-ssl-openssl.cnf.dist-70e74fc481bbd7b7", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/sysctl.conf", + "SPDXID": "SPDXRef-File-etc-sysctl.conf-c068f4c2abc469f7", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/etc/udhcpc/udhcpc.conf", + "SPDXID": "SPDXRef-File-etc-udhcpc-udhcpc.conf-378d389540b30911", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/lib/apk/db/installed", + "SPDXID": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/lib/ld-musl-x86_64.so.1", + "SPDXID": "SPDXRef-File-lib-ld-musl-x86-64.so.1-6e7e3004534b86d1", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/sbin/apk", + "SPDXID": "SPDXRef-File-sbin-apk-0c90571c31cc24d4", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/sbin/ldconfig", + "SPDXID": "SPDXRef-File-sbin-ldconfig-f058d85fa2770b4f", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/bin/getconf", + "SPDXID": "SPDXRef-File-usr-bin-getconf-cb10741d9c217d07", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/bin/getent", + "SPDXID": "SPDXRef-File-usr-bin-getent-d36d4602a8c0ad50", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/bin/iconv", + "SPDXID": "SPDXRef-File-usr-bin-iconv-b33e7a49b30565e7", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/bin/ldd", + "SPDXID": "SPDXRef-File-usr-bin-ldd-485cab4248b11e3f", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/bin/scanelf", + "SPDXID": "SPDXRef-File-usr-bin-scanelf-97db8aae69dc1643", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/bin/ssl_client", + "SPDXID": "SPDXRef-File-usr-bin-ssl-client-b4bab8e79035429e", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/engines-3/afalg.so", + "SPDXID": "SPDXRef-File-usr-lib-engines-3-afalg.so-51e3b429173363b7", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/engines-3/capi.so", + "SPDXID": "SPDXRef-File-usr-lib-engines-3-capi.so-2cac453818c05153", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/engines-3/loader_attic.so", + "SPDXID": "SPDXRef-File-usr-lib-engines-3-loader-attic.so-b2bd5535e09327d5", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/engines-3/padlock.so", + "SPDXID": "SPDXRef-File-usr-lib-engines-3-padlock.so-2230852154a764a9", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/libapk.so.2.14.0", + "SPDXID": "SPDXRef-File-usr-lib-libapk.so.2.14.0-4e2deeb47f39a10e", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/libcrypto.so.3", + "SPDXID": "SPDXRef-File-usr-lib-libcrypto.so.3-a36b11ef91e748da", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/libssl.so.3", + "SPDXID": "SPDXRef-File-usr-lib-libssl.so.3-69cc85b55482eba1", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/libz.so.1.3.1", + "SPDXID": "SPDXRef-File-usr-lib-libz.so.1.3.1-014a43ace0388eab", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/os-release", + "SPDXID": "SPDXRef-File-usr-lib-os-release-c36f73b65f306d73", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/ossl-modules/legacy.so", + "SPDXID": "SPDXRef-File-usr-lib-ossl-modules-legacy.so-ff43617ec381da18", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/lib/sysctl.d/00-alpine.conf", + "SPDXID": "SPDXRef-File-usr-lib-sysctl.d-00-alpine.conf-d70650009c2bb036", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-4a6a0840.rsa.pub-0c253a006a51a055", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5243ef4b.rsa.pub-7430793c9843ac71", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-524d27bb.rsa.pub-9174b40bd64a4afa", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5261cecb.rsa.pub-41bcca0722df483b", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-58199dcc.rsa.pub-33dbeb08a729965e", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-58cbb476.rsa.pub-ee960619cbfa52da", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-58e4f17d.rsa.pub-3104c985f7217a91", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-5e69ca50.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5e69ca50.rsa.pub-1e36d9959ceb1540", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-60ac2099.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-60ac2099.rsa.pub-abac01a20548e31f", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-6165ee59.rsa.pub-60f3660667c882d5", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-61666e3f.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-61666e3f.rsa.pub-20c139d549b80964", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616a9724.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616a9724.rsa.pub-9ee7f6bfafbd04f5", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616abc23.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616abc23.rsa.pub-c0f8875bed907f16", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ac3bc.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616ac3bc.rsa.pub-5d2bce7c30d4842e", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616adfeb.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616adfeb.rsa.pub-6277d3a7f9f78fa0", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616ae350.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616ae350.rsa.pub-e8e728f7eb9ac206", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-616db30d.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616db30d.rsa.pub-58dfe972c27db41d", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/apk/keys/alpine-devel@lists.alpinelinux.org-66ba20fe.rsa.pub", + "SPDXID": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-66ba20fe.rsa.pub-9d58e602c9201b03", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + }, + { + "fileName": "/usr/share/udhcpc/default.script", + "SPDXID": "SPDXRef-File-usr-share-udhcpc-default.script-431ddc0b26fa5372", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "", + "comment": "layerID: sha256:3e01818d79cd3467f1d60e54224f3f6ce5170eceb54e265d96bb82344b8c24e7" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-scanelf-5732e2d080a0a28c", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-File-lib-ld-musl-x86-64.so.1-6e7e3004534b86d1", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relatedSpdxElement": "SPDXRef-Package-apk-zlib-cd1596a99504840a", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relatedSpdxElement": "SPDXRef-File-usr-bin-ldd-485cab4248b11e3f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relatedSpdxElement": "SPDXRef-File-usr-bin-iconv-b33e7a49b30565e7", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relatedSpdxElement": "SPDXRef-File-usr-bin-getconf-cb10741d9c217d07", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relatedSpdxElement": "SPDXRef-File-usr-bin-getent-d36d4602a8c0ad50", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relatedSpdxElement": "SPDXRef-File-sbin-ldconfig-f058d85fa2770b4f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-etc-udhcpc-udhcpc.conf-378d389540b30911", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-etc-busybox-paths.d-busybox-3cbfe16761873575", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-usr-share-udhcpc-default.script-431ddc0b26fa5372", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-bin-busybox-468f4889734362f5", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-Package-apk-busybox-binsh-79dd77b653445aa8", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-etc-network-if-up.d-dad-ae53711b4c2ae508", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-etc-logrotate.d-acpid-e680a539c74e700d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relatedSpdxElement": "SPDXRef-File-etc-securetty-f5ddc6e8bf552536", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relatedSpdxElement": "SPDXRef-File-etc-secfixes.d-alpine-31f4ab357226cd60", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relatedSpdxElement": "SPDXRef-File-etc-issue-b413295f2e1636b9", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relatedSpdxElement": "SPDXRef-File-usr-lib-os-release-c36f73b65f306d73", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relatedSpdxElement": "SPDXRef-File-etc-alpine-release-fd2f197c9c548c66", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-profile.d-README-22834df769d2095e", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-motd-4eb653645b7f13ee", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-crontabs-root-86d9785f3abff156", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-profile.d-color-prompt.sh.disabled-96f77289dc1c6105", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-modprobe.d-aliases.conf-b29978812ba855fa", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-modprobe.d-blacklist.conf-b58b1a80d90b95b5", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-modprobe.d-kms.conf-bae009a41fd01a1d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-usr-lib-sysctl.d-00-alpine.conf-d70650009c2bb036", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-modprobe.d-i386.conf-f54933ed0cb86bd2", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relatedSpdxElement": "SPDXRef-File-etc-profile.d-20locale.sh-ff541b2c108c7e57", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-scanelf-5732e2d080a0a28c", + "relatedSpdxElement": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-scanelf-5732e2d080a0a28c", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-scanelf-5732e2d080a0a28c", + "relatedSpdxElement": "SPDXRef-File-usr-bin-scanelf-97db8aae69dc1643", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-4a6a0840.rsa.pub-0c253a006a51a055", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5e69ca50.rsa.pub-1e36d9959ceb1540", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-61666e3f.rsa.pub-20c139d549b80964", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-58e4f17d.rsa.pub-3104c985f7217a91", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-58199dcc.rsa.pub-33dbeb08a729965e", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5261cecb.rsa.pub-41bcca0722df483b", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616db30d.rsa.pub-58dfe972c27db41d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616ac3bc.rsa.pub-5d2bce7c30d4842e", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-6165ee59.rsa.pub-60f3660667c882d5", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616adfeb.rsa.pub-6277d3a7f9f78fa0", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5243ef4b.rsa.pub-7430793c9843ac71", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5261cecb.rsa.pub-8503eca0329855ac", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-524d27bb.rsa.pub-9174b40bd64a4afa", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-66ba20fe.rsa.pub-9d58e602c9201b03", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616a9724.rsa.pub-9ee7f6bfafbd04f5", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-6165ee59.rsa.pub-9fc2b76378071420", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-60ac2099.rsa.pub-abac01a20548e31f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616abc23.rsa.pub-c0f8875bed907f16", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-5243ef4b.rsa.pub-dd82cc7a531a4f8a", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-61666e3f.rsa.pub-e174ab22f2beeb60", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-4a6a0840.rsa.pub-e31ca5e7df53db52", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-616ae350.rsa.pub-e8e728f7eb9ac206", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relatedSpdxElement": "SPDXRef-File-...alpine-devel-lists.alpinelinux.org-58cbb476.rsa.pub-ee960619cbfa52da", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-engines-3-padlock.so-2230852154a764a9", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-engines-3-capi.so-2cac453818c05153", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-engines-3-afalg.so-51e3b429173363b7", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-etc-ssl-openssl.cnf.dist-70e74fc481bbd7b7", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-etc-ssl-ct-log-list.cnf.dist-7386b7ea31c8254f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-etc-ssl-ct-log-list.cnf-97ee0f53ad135fdd", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-etc-ssl-openssl.cnf-9afb2be8c7429f0d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-libcrypto.so.3-a36b11ef91e748da", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-engines-3-loader-attic.so-b2bd5535e09327d5", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-ossl-modules-legacy.so-ff43617ec381da18", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-ca-certificates-bundle-67dec00a55941450", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-ca-certificates-bundle-67dec00a55941450", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-ca-certificates-bundle-67dec00a55941450", + "relatedSpdxElement": "SPDXRef-File-etc-ssl-certs-ca-certificates.crt-860b9d53645feb47", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relatedSpdxElement": "SPDXRef-File-usr-lib-libssl.so.3-69cc85b55482eba1", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relatedSpdxElement": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-protocols-2ebaf2d5275a87c3", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-inittab-4d8d5f0587154018", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-passwd-52c8226245401a54", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-services-5e64bee7d0512520", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-shells-5f409797065e8a24", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-nsswitch.conf-668425e791126652", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-hosts-6ac577ecad2ec5b0", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-profile-773e11b623fa2206", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-group-7db3625241f6856d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-modules-a3196be7d01a0aec", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-fstab-b80c3cb56c980b08", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-sysctl.conf-c068f4c2abc469f7", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-shadow-e52a006d3d1244db", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relatedSpdxElement": "SPDXRef-File-etc-hostname-e7d0d8adb0625ce4", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relatedSpdxElement": "SPDXRef-File-sbin-apk-0c90571c31cc24d4", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relatedSpdxElement": "SPDXRef-File-usr-lib-libapk.so.2.14.0-4e2deeb47f39a10e", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-binsh-79dd77b653445aa8", + "relatedSpdxElement": "SPDXRef-File-bin-busybox-468f4889734362f5", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-binsh-79dd77b653445aa8", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-busybox-binsh-79dd77b653445aa8", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "relatedSpdxElement": "SPDXRef-File-usr-bin-ssl-client-b4bab8e79035429e", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-zlib-cd1596a99504840a", + "relatedSpdxElement": "SPDXRef-File-usr-lib-libz.so.1.3.1-014a43ace0388eab", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-apk-zlib-cd1596a99504840a", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "DEPENDENCY_OF" + }, + { + "spdxElementId": "SPDXRef-Package-apk-zlib-cd1596a99504840a", + "relatedSpdxElement": "SPDXRef-File-lib-apk-db-installed-847731c531b13bae", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-baselayout-51b252cbcc64365d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-baselayout-data-6f47cd381e7d99ac", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-keys-5dd53a4dcb46e28e", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-alpine-release-51163b3e15318574", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-apk-tools-71bc4cfa3d7edaba", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-busybox-4f44d11c6a9d8f9c", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-busybox-binsh-79dd77b653445aa8", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-ca-certificates-bundle-67dec00a55941450", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-libcrypto3-60435cc06ed5c73a", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-libssl3-6ea5aa937ae77a59", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-musl-17abef0c23a9509f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-musl-utils-472b480bd2fe3378", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-scanelf-5732e2d080a0a28c", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-ssl-client-9894cb492b9e703b", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Image-alpine", + "relatedSpdxElement": "SPDXRef-Package-apk-zlib-cd1596a99504840a", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DocumentRoot-Image-alpine", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/tests/unit/data/sboms/cachi2.bom.spdx.json b/tests/unit/data/sboms/cachi2.bom.spdx.json new file mode 100644 index 000000000..a8d5f93c3 --- /dev/null +++ b/tests/unit/data/sboms/cachi2.bom.spdx.json @@ -0,0 +1,195 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "unknown", + "documentNamespace": "NOASSERTION", + "creationInfo": { + "licenseListVersion": "3.24", + "creators": [ + "Organization: Red Hat, Inc", + "Tool: cachi2-" + ], + "created": "2024-08-21T11:19:04Z" + }, + "packages": [ + { + "name": "PyYAML", + "SPDXID": "SPDXRef-Package-python-PyYAML-696696f5e92f1b5e", + "versionInfo": "6.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/pyyaml@6.0" + } + ] + }, + { + "name": "aiowsgi", + "SPDXID": "SPDXRef-Package-python-aiowsgi-78716bdabf6daae1", + "versionInfo": "0.8", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/aiowsgi@0.8" + } + ] + }, + { + "name": "appr", + "SPDXID": "SPDXRef-Package-python-appr-d869da81f0adbece", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/appr?checksum=sha256:ee6a0a38bed8cff46a562ed3620bc453141a02262ab0c8dd055824af2829ee5c&download_url=https://github.com/quay/appr/archive/37ff9a487a54ad41b59855ecd76ee092fe206a84.zip" + } + ] + }, + { + "name": "archive/tar", + "SPDXID": "SPDXRef-Package-go-module-archive-tar-1ce4dbb5cf96f1c7", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/archive/tar?type=package" + } + ] + }, + { + "name": "cachi2", + "SPDXID": "SPDXRef-Package-python-cachi2-865cdb2c6f0ff5c5", + "versionInfo": "0.0.1", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/cachi2@0.0.1?vcs_url=git%2Bssh://git%40github.com/containerbuildsystem/cachi2%40fc0d6079c2dc9b2a491c0848e550ad3509986110" + } + ] + }, + { + "name": "cachito-npm-without-deps", + "SPDXID": "SPDXRef-Package-npm-cachito-npm-without-deps-563e3658e3eb288e", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/cachito-npm-without-deps?vcs_url=git%2Bhttps://github.com/cachito-testing/cachito-npm-without-deps.git%402f0ce1d7b1f8b35572d919428b965285a69583f6" + } + ] + }, + { + "name": "code.gitea.io/sdk/gitea", + "SPDXID": "SPDXRef-Package-go-module-code.gitea.io-sdk-gitea-cdc94d3a9074a69b", + "versionInfo": "v0.15.1", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/code.gitea.io/sdk/gitea@v0.15.1?type=module" + } + ] + }, + { + "name": "fecha", + "SPDXID": "SPDXRef-Package-npm-fecha-874399c7dda48850", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/fecha?checksum=sha512:8ae71e98d68e38e1f6e4c629187684dd85e4dc96647c7219b1dd189598ea52865e947f0ad94a7001fa8fb5eccf58467fe34ad10066e831af3374120134604bd5&download_url=https://github.com/taylorhakes/fecha/archive/91680e4db1415fea33eac878cfd889c80a7b55c7.tar.gz" + } + ] + }, + { + "name": "github.com/cachito-testing/gomod-pandemonium/terminaltor", + "SPDXID": "SPDXRef-Package-go-module-github.com-cachito-testing-gomod-pandemonium-terminaltor-d85aa69f7b0304e3", + "versionInfo": "v1.0.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/cachito-testing/gomod-pandemonium/terminaltor@v1.0.0?type=module" + } + ] + }, + { + "name": "github.com/docker/cli", + "SPDXID": "SPDXRef-Package-go-module-github.com-docker-cli-ea403731821a081e", + "versionInfo": "v23.0.0-rc.3+incompatible", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/docker/cli@v23.0.0-rc.3%2Bincompatible?type=module" + } + ] + }, + { + "name": "github.com/docker/cli/cli/config", + "SPDXID": "SPDXRef-Package-go-module-github.com-docker-cli-cli-config-73cc4b7b8f510817", + "versionInfo": "v23.0.0-rc.3+incompatible", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/docker/cli/cli/config@v23.0.0-rc.3%2Bincompatible?type=package" + } + ] + }, + { + "name": "github.com/redhat-appstudio/build-service", + "SPDXID": "SPDXRef-Package-go-module-github.com-redhat-appstudio-build-service-574d786c89acf613", + "versionInfo": "v0.0.0-20230503110830-d1a9e858489d", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/redhat-appstudio/build-service@v0.0.0-20230503110830-d1a9e858489d?type=module" + } + ] + }, + { + "name": "knative.dev/pkg", + "SPDXID": "SPDXRef-Package-go-module-knative.dev-pkg-0a00bf33a820e7f1", + "versionInfo": "v0.0.0-20230125083639-408ad0773f47", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/knative.dev/pkg@v0.0.0-20230125083639-408ad0773f47?type=module" + } + ] + }, + { + "name": "knative.dev/pkg/metrics", + "SPDXID": "SPDXRef-Package-go-module-knative.dev-pkg-metrics-c613be23287c5dc4", + "versionInfo": "v0.0.0-20230125083639-408ad0773f47", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/knative.dev/pkg/metrics@v0.0.0-20230125083639-408ad0773f47?type=package" + } + ] + }, + { + "name": "test_package_cachi2", + "SPDXID": "SPDXRef-Package-python-test-package-cachi2-bdec7caf7aac75f3", + "versionInfo": "1.0.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/test-package-cachi2@1.0.0?vcs_url=git%2Bssh://git%40github.com/brunoapimentel/pip-e2e-test.git%40294df352deed835cf703ae8a799926418ae5fd3b" + } + ] + } + ], + "relationships": [ + ] +} diff --git a/tests/unit/data/sboms/merged.bom.spdx.json b/tests/unit/data/sboms/merged.bom.spdx.json new file mode 100644 index 000000000..36b626b58 --- /dev/null +++ b/tests/unit/data/sboms/merged.bom.spdx.json @@ -0,0 +1,488 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "/var/lib/containers/storage/vfs/dir/517aef0ffe20db360d19aa475dbbfbe03f452f53403881a31f9a475c83af788b", + "documentNamespace": "https://anchore.com/syft/file/var/lib/containers/storage/vfs/dir/517aef0ffe20db360d19aa475dbbfbe03f452f53403881a31f9a475c83af788b-fd7936f6-323c-4de7-9be8-0fe2584f97fd", + "creationInfo": { + "licenseListVersion": "3.24", + "creators": [ + "Organization: Anchore, Inc", + "Tool: syft-0.100.0", + "Organization: Red Hat, Inc", + "Tool: cachi2-" + ], + "created": "2024-08-21T11:19:30Z" + }, + "packages": [ + { + "name": "appr", + "SPDXID": "SPDXRef-Package-python-appr-93a64d044490691c", + "versionInfo": "0.7.4", + "supplier": "Person: Antoine Legrand (2t.antoine@gmail.com)", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from installed python package manifest file: opt/app-root/lib/python3.9/site-packages/appr-0.7.4-py3.9.egg-info/PKG-INFO, opt/app-root/lib/python3.9/site-packages/appr-0.7.4-py3.9.egg-info/top_level.txt", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "originator": "Person: Antoine Legrand (2t.antoine@gmail.com)", + "externalRefs": [ + { + "referenceCategory":"PACKAGE-MANAGER", + "referenceLocator":"pkg:pypi/appr@0.7.4", + "referenceType":"purl" + } + ] + }, + { + "name": "bash", + "SPDXID": "SPDXRef-Package-rpm-bash-1a6619bdab5f8a2d", + "versionInfo": "4.4.20-4.el8_6", + "supplier": "Organization: Red Hat, Inc.", + "originator": "Organization: Red Hat, Inc.", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from RPM DB: var/lib/rpm/Packages", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:rpm/rhel/bash@4.4.20-4.el8_6?arch=x86_64&upstream=bash-4.4.20-4.el8_6.src.rpm&distro=rhel-8.7" + } + ] + }, + { + "SPDXID": "SPDXRef-Package-python-cachi2-71a99443e114c112", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:pypi/cachi2@0.0.post1+gdfd2180.d20230704", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "cachi2", + "sourceInfo": "acquired package info from installed python package manifest file: src/cachi2.egg-info/PKG-INFO, src/cachi2.egg-info/top_level.txt", + "supplier": "NOASSERTION", + "versionInfo": "0.0.post1+gdfd2180.d20230704" + }, + { + "SPDXID": "SPDXRef-Package-npm-cachito-npm-without-deps-72138119b55a065d", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:npm/cachito-npm-without-deps@git+https://github.com/cachito-testing/cachito-npm-without-deps.git%232f0ce1d7b1f8b35572d919428b965285a69583f6", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "cachito-npm-without-deps", + "sourceInfo": "acquired package info from installed node module manifest file: opt/app-root/src/package-lock.json", + "supplier": "NOASSERTION", + "versionInfo": "git+https://github.com/cachito-testing/cachito-npm-without-deps.git#2f0ce1d7b1f8b35572d919428b965285a69583f6" + }, + { + "SPDXID": "SPDXRef-Package-npm-fecha-ff4ad17b28d08441", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:npm/fecha@https://github.com/taylorhakes/fecha/archive/91680e4db1415fea33eac878cfd889c80a7b55c7.tar.gz", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "fecha", + "sourceInfo": "acquired package info from installed node module manifest file: opt/app-root/src/package-lock.json", + "supplier": "NOASSERTION", + "versionInfo": "https://github.com/taylorhakes/fecha/archive/91680e4db1415fea33eac878cfd889c80a7b55c7.tar.gz" + }, + { + "SPDXID": "SPDXRef-Package-go-module-github.com-redhat-appstudio-build-service-5719506d15c0a3dd", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/redhat-appstudio/build-service@(devel)", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "github.com/redhat-appstudio/build-service", + "sourceInfo": "acquired package info from go module information: manager", + "supplier": "NOASSERTION", + "versionInfo": "(devel)" + }, + { + "SPDXID": "SPDXRef-Package-python-PyYAML-696696f5e92f1b5e", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:pypi/pyyaml@6.0", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "PyYAML", + "sourceInfo": "acquired package info from installed python package manifest file: ", + "supplier": "NOASSERTION", + "versionInfo": "6.0" + }, + { + "SPDXID": "SPDXRef-Package-python-aiowsgi-78716bdabf6daae1", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:pypi/aiowsgi@0.8", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "aiowsgi", + "sourceInfo": "acquired package info from installed python package manifest file: ", + "supplier": "NOASSERTION", + "versionInfo": "0.8" + }, + { + "SPDXID": "SPDXRef-Package-python-appr-d869da81f0adbece", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:pypi/appr?checksum=sha256:ee6a0a38bed8cff46a562ed3620bc453141a02262ab0c8dd055824af2829ee5c&download_url=https://github.com/quay/appr/archive/37ff9a487a54ad41b59855ecd76ee092fe206a84.zip", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "appr", + "sourceInfo": "acquired package info from installed python package manifest file: ", + "supplier": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-Package-go-module-archive-tar-1ce4dbb5cf96f1c7", + "copyrightText": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/archive/tar?type=package", + "referenceType": "purl" + } + ], + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "name": "archive/tar", + "sourceInfo": "acquired package info from go module information: ", + "supplier": "NOASSERTION" + }, + { + "name": "cachi2", + "SPDXID": "SPDXRef-Package-python-cachi2-865cdb2c6f0ff5c5", + "versionInfo": "0.0.1", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from installed python package manifest file: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/cachi2@0.0.1?vcs_url=git%2Bssh://git%40github.com/containerbuildsystem/cachi2%40fc0d6079c2dc9b2a491c0848e550ad3509986110" + } + ] + }, + { + "name": "cachito-npm-without-deps", + "SPDXID": "SPDXRef-Package-npm-cachito-npm-without-deps-563e3658e3eb288e", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from installed node module manifest file: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/cachito-npm-without-deps?vcs_url=git%2Bhttps://github.com/cachito-testing/cachito-npm-without-deps.git%402f0ce1d7b1f8b35572d919428b965285a69583f6" + } + ] + }, + { + "name": "code.gitea.io/sdk/gitea", + "SPDXID": "SPDXRef-Package-go-module-code.gitea.io-sdk-gitea-cdc94d3a9074a69b", + "versionInfo": "v0.15.1", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/code.gitea.io/sdk/gitea@v0.15.1?type=module" + } + ] + }, + { + "name": "fecha", + "SPDXID": "SPDXRef-Package-npm-fecha-874399c7dda48850", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from installed node module manifest file: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/fecha?checksum=sha512:8ae71e98d68e38e1f6e4c629187684dd85e4dc96647c7219b1dd189598ea52865e947f0ad94a7001fa8fb5eccf58467fe34ad10066e831af3374120134604bd5&download_url=https://github.com/taylorhakes/fecha/archive/91680e4db1415fea33eac878cfd889c80a7b55c7.tar.gz" + } + ] + }, + { + "name": "github.com/cachito-testing/gomod-pandemonium/terminaltor", + "SPDXID": "SPDXRef-Package-go-module-github.com-cachito-testing-gomod-pandemonium-terminaltor-d85aa69f7b0304e3", + "versionInfo": "v1.0.0", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/cachito-testing/gomod-pandemonium/terminaltor@v1.0.0?type=module" + } + ] + }, + { + "name": "github.com/docker/cli", + "SPDXID": "SPDXRef-Package-go-module-github.com-docker-cli-ea403731821a081e", + "versionInfo": "v23.0.0-rc.3+incompatible", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/docker/cli@v23.0.0-rc.3%2Bincompatible?type=module" + } + ] + }, + { + "name": "github.com/docker/cli/cli/config", + "SPDXID": "SPDXRef-Package-go-module-github.com-docker-cli-cli-config-73cc4b7b8f510817", + "versionInfo": "v23.0.0-rc.3+incompatible", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/docker/cli/cli/config@v23.0.0-rc.3%2Bincompatible?type=package" + } + ] + }, + { + "name": "github.com/redhat-appstudio/build-service", + "SPDXID": "SPDXRef-Package-go-module-github.com-redhat-appstudio-build-service-574d786c89acf613", + "versionInfo": "v0.0.0-20230503110830-d1a9e858489d", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/redhat-appstudio/build-service@v0.0.0-20230503110830-d1a9e858489d?type=module" + } + ] + }, + { + "name": "knative.dev/pkg", + "SPDXID": "SPDXRef-Package-go-module-knative.dev-pkg-0a00bf33a820e7f1", + "versionInfo": "v0.0.0-20230125083639-408ad0773f47", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/knative.dev/pkg@v0.0.0-20230125083639-408ad0773f47?type=module" + } + ] + }, + { + "name": "knative.dev/pkg/metrics", + "SPDXID": "SPDXRef-Package-go-module-knative.dev-pkg-metrics-c613be23287c5dc4", + "versionInfo": "v0.0.0-20230125083639-408ad0773f47", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from go module information: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/knative.dev/pkg/metrics@v0.0.0-20230125083639-408ad0773f47?type=package" + } + ] + }, + { + "name": "test_package_cachi2", + "SPDXID": "SPDXRef-Package-python-test-package-cachi2-bdec7caf7aac75f3", + "versionInfo": "1.0.0", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from installed python package manifest file: ", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/test-package-cachi2@1.0.0?vcs_url=git%2Bssh://git%40github.com/brunoapimentel/pip-e2e-test.git%40294df352deed835cf703ae8a799926418ae5fd3b" + } + ] + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-.-terminaltor-1b79094a8c283d88", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-.-terminaltor-9c8431f4d44b5c65", + "relationshipType": "CONTAINS" + }, + { + "relatedSpdxElement": "SPDXRef-Package-python-PyYAML-0172906cb007d3b6", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-DocumentRoot-File-" + }, + { + "relatedSpdxElement": "SPDXRef-Package-python-aiowsgi-b32dee5d93047994", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-DocumentRoot-File-" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-python-appr-93a64d044490691c", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-rpm-bash-1a6619bdab5f8a2d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-python-cachi2-71a99443e114c112", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-npm-cachito-npm-without-deps-72138119b55a065d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-code.gitea.io-sdk-gitea-3172f131171fcbf8", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-npm-fecha-ff4ad17b28d08441", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-github.com-docker-cli-1671a7feec4073fe", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-github.com-redhat-appstudio-build-service-5719506d15c0a3dd", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-knative.dev-pkg-8ce424e944b2a02f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DocumentRoot-File-", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/tests/unit/data/sboms/syft.bom.spdx.json b/tests/unit/data/sboms/syft.bom.spdx.json new file mode 100644 index 000000000..eae0d3d7a --- /dev/null +++ b/tests/unit/data/sboms/syft.bom.spdx.json @@ -0,0 +1,244 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "/var/lib/containers/storage/vfs/dir/517aef0ffe20db360d19aa475dbbfbe03f452f53403881a31f9a475c83af788b", + "documentNamespace": "NOASSERTION", + "creationInfo": { + "licenseListVersion": "3.24", + "creators": [ + "Organization: Anchore, Inc", + "Tool: syft-0.100.0" + ], + "created": "2024-08-21T11:19:30Z" + }, + "packages": [ + { + "name": "./terminaltor", + "SPDXID": "SPDXRef-Package-go-module-.-terminaltor-1b79094a8c283d88", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/./terminaltor" + } + ] + }, + { + "name": "./terminaltor", + "SPDXID": "SPDXRef-Package-go-module-.-terminaltor-9c8431f4d44b5c65", + "versionInfo": "(devel)", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/./terminaltor@(devel)" + } + ] + }, + { + "name": "PyYAML", + "SPDXID": "SPDXRef-Package-python-PyYAML-0172906cb007d3b6", + "versionInfo": "6.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/PyYAML@6.0" + } + ] + }, + { + "name": "aiowsgi", + "SPDXID": "SPDXRef-Package-python-aiowsgi-b32dee5d93047994", + "versionInfo": "0.8", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/aiowsgi@0.8" + } + ] + }, + { + "name": "appr", + "SPDXID": "SPDXRef-Package-python-appr-93a64d044490691c", + "versionInfo": "0.7.4", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/appr@0.7.4" + } + ] + }, + { + "name": "bash", + "SPDXID": "SPDXRef-Package-rpm-bash-1a6619bdab5f8a2d", + "versionInfo": "4.4.20-4.el8_6", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:rpm/rhel/bash@4.4.20-4.el8_6?arch=x86_64&upstream=bash-4.4.20-4.el8_6.src.rpm&distro=rhel-8.7" + } + ] + }, + { + "name": "cachi2", + "SPDXID": "SPDXRef-Package-python-cachi2-71a99443e114c112", + "versionInfo": "0.0.post1+gdfd2180.d20230704", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:pypi/cachi2@0.0.post1+gdfd2180.d20230704" + } + ] + }, + { + "name": "cachito-npm-without-deps", + "SPDXID": "SPDXRef-Package-npm-cachito-npm-without-deps-72138119b55a065d", + "versionInfo": "git+https://github.com/cachito-testing/cachito-npm-without-deps.git#2f0ce1d7b1f8b35572d919428b965285a69583f6", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/cachito-npm-without-deps@git+https://github.com/cachito-testing/cachito-npm-without-deps.git%232f0ce1d7b1f8b35572d919428b965285a69583f6" + } + ] + }, + { + "name": "code.gitea.io/sdk/gitea", + "SPDXID": "SPDXRef-Package-go-module-code.gitea.io-sdk-gitea-3172f131171fcbf8", + "versionInfo": "v0.15.1", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/code.gitea.io/sdk/gitea@v0.15.1" + } + ] + }, + { + "name": "fecha", + "SPDXID": "SPDXRef-Package-npm-fecha-ff4ad17b28d08441", + "versionInfo": "https://github.com/taylorhakes/fecha/archive/91680e4db1415fea33eac878cfd889c80a7b55c7.tar.gz", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/fecha@https://github.com/taylorhakes/fecha/archive/91680e4db1415fea33eac878cfd889c80a7b55c7.tar.gz" + } + ] + }, + { + "name": "github.com/docker/cli", + "SPDXID": "SPDXRef-Package-go-module-github.com-docker-cli-1671a7feec4073fe", + "versionInfo": "v23.0.0-rc.3+incompatible", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/docker/cli@v23.0.0-rc.3+incompatible" + } + ] + }, + { + "name": "github.com/redhat-appstudio/build-service", + "SPDXID": "SPDXRef-Package-go-module-github.com-redhat-appstudio-build-service-5719506d15c0a3dd", + "versionInfo": "(devel)", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/redhat-appstudio/build-service@(devel)" + } + ] + }, + { + "name": "knative.dev/pkg", + "SPDXID": "SPDXRef-Package-go-module-knative.dev-pkg-8ce424e944b2a02f", + "versionInfo": "v0.0.0-20230125083639-408ad0773f47", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/knative.dev/pkg@v0.0.0-20230125083639-408ad0773f47" + } + ] + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-.-terminaltor-1b79094a8c283d88", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-.-terminaltor-9c8431f4d44b5c65", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-python-PyYAML-0172906cb007d3b6", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-python-aiowsgi-b32dee5d93047994", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-python-appr-93a64d044490691c", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-rpm-bash-1a6619bdab5f8a2d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-python-cachi2-71a99443e114c112", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-npm-cachito-npm-without-deps-72138119b55a065d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-code.gitea.io-sdk-gitea-3172f131171fcbf8", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-npm-fecha-ff4ad17b28d08441", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-github.com-docker-cli-1671a7feec4073fe", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-github.com-redhat-appstudio-build-service-5719506d15c0a3dd", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "relatedSpdxElement": "SPDXRef-Package-go-module-knative.dev-pkg-8ce424e944b2a02f", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DocumentRoot-File-", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json b/tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json new file mode 100644 index 000000000..1dbb0b2d7 --- /dev/null +++ b/tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json @@ -0,0 +1,112 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "more_experiments/", + "documentNamespace": "https://anchore.com/syft/dir/more_experiments-22e50414-3115-4435-aa19-4e87afa03d09", + "creationInfo": { + "licenseListVersion": "3.22", + "creators": [ + "Organization: Anchore, Inc", + "Tool: syft-0.100.0" + ], + "created": "2024-12-09T23:44:30Z" + }, + "packages": [ + { + "name": "i2c-devel", + "SPDXID": "SPDXRef-Package-rpm-i2c-devel-137ed927c09bd14d", + "versionInfo": "0:2.10.5-8.el3", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from RPM DB: /i2c-devel-2.10.5-8.el3.x86_64.rpm", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "LicenseRef-GPL", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:i2c-devel:i2c-devel:0\\:2.10.5-8.el3:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:i2c-devel:i2c_devel:0\\:2.10.5-8.el3:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:i2c_devel:i2c-devel:0\\:2.10.5-8.el3:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:i2c_devel:i2c_devel:0\\:2.10.5-8.el3:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:i2c:i2c-devel:0\\:2.10.5-8.el3:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:i2c:i2c_devel:0\\:2.10.5-8.el3:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:rpm/i2c-devel@2.10.5-8.el3?arch=x86_64&epoch=0&upstream=i2c-2.10.5-8.el3.src.rpm" + } + ] + }, + { + "name": "more_experiments/", + "SPDXID": "SPDXRef-DocumentRoot-Directory-more-experiments-", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "primaryPackagePurpose": "FILE" + } + ], + "files": [ + { + "fileName": "/i2c-devel-2.10.5-8.el3.x86_64.rpm", + "SPDXID": "SPDXRef-File-i2c-devel-2.10.5-8.el3.x86-64.rpm-0992d87b036af481", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "" + } + ], + "hasExtractedLicensingInfos": [ + { + "licenseId": "LicenseRef-GPL", + "extractedText": "GPL" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-Package-rpm-i2c-devel-137ed927c09bd14d", + "relatedSpdxElement": "SPDXRef-File-i2c-devel-2.10.5-8.el3.x86-64.rpm-0992d87b036af481", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Directory-more-experiments-", + "relatedSpdxElement": "SPDXRef-Package-rpm-i2c-devel-137ed927c09bd14d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DocumentRoot-Directory-more-experiments-", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/tests/unit/data/something.simple0.100.0.spdx.pretty.json b/tests/unit/data/something.simple0.100.0.spdx.pretty.json new file mode 100644 index 000000000..8f053e98c --- /dev/null +++ b/tests/unit/data/something.simple0.100.0.spdx.pretty.json @@ -0,0 +1,87 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "experiments/", + "documentNamespace": "https://anchore.com/syft/dir/experiments-d6d4a378-508e-4f7b-b478-22f0d8e0978c", + "creationInfo": { + "licenseListVersion": "3.22", + "creators": [ + "Organization: Anchore, Inc", + "Tool: syft-0.100.0" + ], + "created": "2024-12-09T19:00:27Z" + }, + "packages": [ + { + "name": "ash", + "SPDXID": "SPDXRef-Package-rpm-ash-b5c592006a5ddade", + "versionInfo": "0:0.3.8-20.el4_7.1", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "sourceInfo": "acquired package info from RPM DB: /ash-0.3.8-20.el4_7.1.x86_64.rpm", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "LicenseRef-BSD", + "copyrightText": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:ash:ash:0\\:0.3.8-20.el4_7.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:rpm/ash@0.3.8-20.el4_7.1?arch=x86_64&epoch=0&upstream=ash-0.3.8-20.el4_7.1.src.rpm" + } + ] + }, + { + "name": "experiments/", + "SPDXID": "SPDXRef-DocumentRoot-Directory-experiments-", + "supplier": "NOASSERTION", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "primaryPackagePurpose": "FILE" + } + ], + "files": [ + { + "fileName": "/ash-0.3.8-20.el4_7.1.x86_64.rpm", + "SPDXID": "SPDXRef-File-ash-0.3.8-20.el4-7.1.x86-64.rpm-39422426cc3490ed", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000000" + } + ], + "licenseConcluded": "NOASSERTION", + "copyrightText": "" + } + ], + "hasExtractedLicensingInfos": [ + { + "licenseId": "LicenseRef-BSD", + "extractedText": "BSD" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-Package-rpm-ash-b5c592006a5ddade", + "relatedSpdxElement": "SPDXRef-File-ash-0.3.8-20.el4-7.1.x86-64.rpm-39422426cc3490ed", + "relationshipType": "OTHER", + "comment": "evident-by: indicates the package's existence is evident by the given file" + }, + { + "spdxElementId": "SPDXRef-DocumentRoot-Directory-experiments-", + "relatedSpdxElement": "SPDXRef-Package-rpm-ash-b5c592006a5ddade", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-DocumentRoot-Directory-experiments-", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/tests/unit/models/test_sbom.py b/tests/unit/models/test_sbom.py index 8cb7dc454..740396558 100644 --- a/tests/unit/models/test_sbom.py +++ b/tests/unit/models/test_sbom.py @@ -1,7 +1,23 @@ +import datetime +import json + import pydantic import pytest -from cachi2.core.models.sbom import FOUND_BY_CACHI2_PROPERTY, Component, Property, Sbom +from cachi2.core.models.sbom import ( + FOUND_BY_CACHI2_PROPERTY, + Component, + Metadata, + Property, + Sbom, + SPDXPackage, + SPDXPackageAnnotation, + SPDXPackageExternalRefPackageManagerPURL, + SPDXPackageExternalRefType, + SPDXRelation, + SPDXSbom, + Tool, +) class TestComponent: @@ -93,6 +109,168 @@ def test_default_property( ) +class TestSPDXPackage: + @pytest.mark.parametrize( + "input_data, expected_data", + [ + ( + { + "SPDXID": "SPDXRef-Package-mypkg--4035f88e9e6be21e9717c7170c40cbdce83f591dc862c5a8e4ac21b5636fa875", + "name": "mypkg", + "externalRefs": [ + { + "referenceLocator": "pkg:generic/mypkg", + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + } + ], + }, + SPDXPackage( + SPDXID="SPDXRef-Package-mypkg--4035f88e9e6be21e9717c7170c40cbdce83f591dc862c5a8e4ac21b5636fa875", + name="mypkg", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceLocator="pkg:generic/mypkg", + referenceCategory="PACKAGE-MANAGER", + referenceType="purl", + ), + ], + ), + ), + ( + { + "name": "mypkg", + "versionInfo": "1.0.0", + "externalRefs": [ + { + "referenceLocator": "pkg:generic/mypkg@1.0.0", + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + } + ], + }, + SPDXPackage( + SPDXID="SPDXRef-Package-mypkg-1.0.0-ded235cb82fb6d084178a362048a549edb6586fd5cd5f84c7afbd919789b801d", + name="mypkg", + versionInfo="1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceLocator="pkg:generic/mypkg@1.0.0", + referenceCategory="PACKAGE-MANAGER", + referenceType="purl", + ), + ], + ), + ), + ( + { + "name": "mypkg", + "versionInfo": "random-version-string", + "externalRefs": [ + { + "referenceLocator": "pkg:generic/mypkg", + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + } + ], + }, + SPDXPackage( + SPDXID="SPDXRef-Package-mypkg-random-version-string-5c73e2936cdb76c672fbee4c7a357695b052c97bcddacc5fb886f82bc78098d4", + name="mypkg", + versionInfo="random-version-string", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceLocator="pkg:generic/mypkg", + referenceCategory="PACKAGE-MANAGER", + referenceType="purl", + ) + ], + ), + ), + ( + { + "name": "mypkg", + "externalRefs": [ + { + "referenceLocator": "pkg:generic/mypkg@1.0.0", + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + } + ], + "versionInfo": "1.0.0", + "path": ".", + "dependencies": [], + }, + SPDXPackage( + SPDXID="SPDXRef-Package-mypkg-1.0.0-ded235cb82fb6d084178a362048a549edb6586fd5cd5f84c7afbd919789b801d", + name="mypkg", + versionInfo="1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceLocator="pkg:generic/mypkg@1.0.0", + referenceCategory="PACKAGE-MANAGER", + referenceType="purl", + ) + ], + ), + ), + ], + ) + def test_construct_from_package_dict( + self, input_data: dict[str, str], expected_data: SPDXPackage + ) -> None: + spdx_package = SPDXPackage.from_package_dict(input_data) + assert spdx_package == expected_data + + @pytest.mark.parametrize( + "input_data, expect_error", + [ + ( + {"versionInfo": "some-version"}, + "1 validation error for SPDXPackage\nname\n Field required", + ) + ], + ) + def test_invalid_packages(self, input_data: dict[str, str], expect_error: str) -> None: + with pytest.raises(pydantic.ValidationError, match=expect_error): + SPDXPackage(**input_data) + + @pytest.mark.parametrize( + "category,type,locator,valid", + [ + ("PACKAGE-MANAGER", "maven-central", "org.apache.tomcat:tomcat:9.0.0.M4", False), + ("PACKAGE-MANAGER", "npm", "http-server@0.3.0", False), + ("PACKAGE-MANAGER", "nuget", "Microsoft.AspNet.MVC/5.0.0", False), + ("PACKAGE-MANAGER", "bower", "modernizr#2.6.2", False), + ("PERSISTENT-ID", "swh", "swh:1:cnt:94a9ed024d3859793618152ea559a168bbcbb5e2", False), + ( + "PERSISTENT-ID", + "gitoid", + "gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", + False, + ), + ( + "OTHER", + "some-id", + "anythingcangohere", + False, + ), + ], + ) + def test_package_unsupported_external_ref( + self, category: str, type: str, locator: str, valid: str + ) -> None: + """Fails on unsupported category and type combinations. + + Only PACKAGE-MANAGER and SECURITY categories with type purl is supported. + """ + adapter: pydantic.TypeAdapter = pydantic.TypeAdapter(SPDXPackageExternalRefType) + with pytest.raises(pydantic.ValidationError): + adapter.validate_python( + dict(referenceCategory=category, referenceLocator=locator, referenceType=type) + ) + + class TestSbom: def test_sort_and_dedupe_components(self) -> None: sbom = Sbom( @@ -127,7 +305,6 @@ def test_sort_and_dedupe_components(self) -> None: {"name": "bytes", "version": None, "purl": "pkg:golang/bytes"}, ], ) - print(sbom.components) assert sbom.components == [ Component(name="bytes", purl="pkg:golang/bytes"), Component(name="fmt", purl="pkg:golang/fmt"), @@ -141,3 +318,995 @@ def test_sort_and_dedupe_components(self) -> None: name="github.com/org/B", purl="pkg:golang/github.com/org/B@v1.0.0", version="v1.0.0" ), ] + + def test_to_spdx(self, isodate: datetime.datetime) -> None: + sbom = Sbom( + components=[ + { + "name": "spdx-expression-parse", + "version": "v1.0.0", + "purl": "pkg:npm/spdx-expression-parse@1.0.0", + }, + { + "name": "github.com/org/A", + "version": "v1.0.0", + "purl": "pkg:golang/github.com/org/A@v1.0.0", + }, + { + "name": "github.com/org/A", + "version": "v1.1.0", + "purl": "pkg:golang/github.com/org/A@v1.1.0", + }, + ], + ) + sbom.components[0].properties.extend( + [ + Property(name="cdx:npm:package:bundled", value="true"), + ] + ) + spdx_sbom = sbom.to_spdx("NOASSERTION") + + assert spdx_sbom.packages == [ + SPDXPackage( + SPDXID="SPDXRef-DocumentRoot-File-", + name="", + versionInfo="", + externalRefs=[], + annotations=[], + ), + SPDXPackage( + SPDXID="SPDXRef-Package-github.com/org/A-v1.0.0-8090f86e9eb851549de5f8391948c1df6a2c8976bfa33c3cbd82e917564ac94f", + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0", + referenceType="purl", + ) + ], + annotations=[ + SPDXPackageAnnotation( + annotator="Tool: cachi2:jsonencoded", + annotationDate="2021-07-01T00:00:00Z", + annotationType="OTHER", + comment=json.dumps({"name": "cachi2:found_by", "value": "cachi2"}), + ), + SPDXPackageAnnotation( + annotator="Tool: cachi2:jsonencoded", + annotationDate="2021-07-01T00:00:00Z", + annotationType="OTHER", + comment=json.dumps({"name": "cdx:npm:package:bundled", "value": "true"}), + ), + ], + ), + SPDXPackage( + SPDXID="SPDXRef-Package-github.com/org/A-v1.1.0-898f4d436d82296d12247741855acc48a1f80639d2418e556268f30ae2336303", + name="github.com/org/A", + versionInfo="v1.1.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.1.0", + referenceType="purl", + ) + ], + annotations=[ + SPDXPackageAnnotation( + annotator="Tool: cachi2:jsonencoded", + annotationDate="2021-07-01T00:00:00Z", + annotationType="OTHER", + comment=json.dumps({"name": "cachi2:found_by", "value": "cachi2"}), + ), + ], + ), + SPDXPackage( + SPDXID="SPDXRef-Package-spdx-expression-parse-v1.0.0-2d5c537d20208409089cf9c7ae9398b7105beef1f883cfc4c0b1f804bca86b02", + name="spdx-expression-parse", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:npm/spdx-expression-parse@1.0.0", + referenceType="purl", + ) + ], + annotations=[ + SPDXPackageAnnotation( + annotator="Tool: cachi2:jsonencoded", + annotationDate="2021-07-01T00:00:00Z", + annotationType="OTHER", + comment=json.dumps({"name": "cachi2:found_by", "value": "cachi2"}), + ), + ], + ), + ] + assert spdx_sbom.relationships == [ + SPDXRelation( + spdxElementId="SPDXRef-DOCUMENT", + comment="", + relatedSpdxElement="SPDXRef-DocumentRoot-File-", + relationshipType="DESCRIBES", + ), + SPDXRelation( + spdxElementId="SPDXRef-DocumentRoot-File-", + comment="", + relatedSpdxElement="SPDXRef-Package-github.com/org/A-v1.0.0-8090f86e9eb851549de5f8391948c1df6a2c8976bfa33c3cbd82e917564ac94f", + relationshipType="CONTAINS", + ), + SPDXRelation( + spdxElementId="SPDXRef-DocumentRoot-File-", + comment="", + relatedSpdxElement="SPDXRef-Package-github.com/org/A-v1.1.0-898f4d436d82296d12247741855acc48a1f80639d2418e556268f30ae2336303", + relationshipType="CONTAINS", + ), + SPDXRelation( + spdxElementId="SPDXRef-DocumentRoot-File-", + comment="", + relatedSpdxElement="SPDXRef-Package-spdx-expression-parse-v1.0.0-2d5c537d20208409089cf9c7ae9398b7105beef1f883cfc4c0b1f804bca86b02", + relationshipType="CONTAINS", + ), + ] + + def test_cyclonedx_sbom_can_be_converted_to_spdx_and_back_without_loosing_any_data( + self, isodate: datetime.datetime + ) -> None: + sbom = Sbom( + components=[ + { + "name": "spdx-expression-parse", + "version": "v1.0.0", + "purl": "pkg:npm/spdx-expression-parse@1.0.0", + }, + { + "name": "github.com/org/A", + "version": "v1.0.0", + "purl": "pkg:golang/github.com/org/A@v1.0.0", + }, + { + "name": "github.com/org/A", + "version": "v1.1.0", + "purl": "pkg:golang/github.com/org/A@v1.1.0", + }, + ], + ) + sbom.components[0].properties.extend( + [ + Property(name="cdx:npm:package:bundled", value="true"), + ] + ) + cyclonedx_sbom = sbom.to_spdx("NOASSERTION").to_cyclonedx() + assert cyclonedx_sbom == sbom + + +# Some partially constructed objects to streamline test cases definitions. +STOCK_ANNOTATION = { + "annotator": "Tool: cachi2:jsonencoded", + "annotationDate": "2021-07-01T00:00:00Z", + "annotationType": "OTHER", + "comment": '{"name": "cachi2:found_by", "value": "cachi2"}', +} + +BLANK_SPDX_SBOM = SPDXSbom( + SPDXID="SPDXRef-DOCUMENT", + documentNamespace="NOASSERTION", + creationInfo={ + "creators": ["Tool: cachi2", "Organization: cachi2"], + "created": "2021-07-01T00:00:00Z", + }, +) + +DEFAULT_ROOT_PACKAGE = SPDXPackage( + **{ + "SPDXID": "SPDXRef-DocumentRoot-File-", + "name": "", + "versionInfo": "", + "externalRefs": [], + "annotations": [], + } +) + +DEFAULT_ROOT_RELATION = { + "spdxElementId": "SPDXRef-DOCUMENT", + "comment": "", + "relatedSpdxElement": "SPDXRef-DocumentRoot-File-", + "relationshipType": "DESCRIBES", +} + + +def _gen_ref(locator: str) -> dict: + return { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": locator, + "referenceType": "purl", + } + + +def _root_contains(spdxid: str) -> SPDXRelation: + return SPDXRelation( + **{ + "spdxElementId": "SPDXRef-DocumentRoot-File-", + "comment": "", + "relatedSpdxElement": spdxid, + "relationshipType": "CONTAINS", + } + ) + + +class TestSPDXSbom: + def test_sort_and_dedupe_packages(self) -> None: + sbom = SPDXSbom( + creationInfo={"creators": [], "created": "2021-07-01T00:00:00Z"}, + documentNamespace="NOASSERTION", + packages=[ + { + "name": "github.com/org/B", + "versionInfo": "v1.0.0", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/B@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.1.0", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.1.0?repository_id=R1", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.1.0", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.1.0?repository_id=R2", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.0.0", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.0.0", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/B", + "versionInfo": "v1.0.0", + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/B@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "fmt", + "versionInfo": None, + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/fmt", + "referenceType": "purl", + } + ], + }, + { + "name": "bytes", + "versionInfo": None, + "downloadLocation": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/bytes", + "referenceType": "purl", + } + ], + }, + ], + ) + expected_packages = [ + SPDXPackage( + name="bytes", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/bytes", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="fmt", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/fmt", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.1.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R1", + referenceType="purl", + ), + ], + ), + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.1.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R2", + referenceType="purl", + ), + ], + ), + SPDXPackage( + name="github.com/org/B", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0", + referenceType="purl", + ) + ], + ), + ] + assert len(sbom.packages) == len(expected_packages) + assert sbom.packages == expected_packages + + def test_package_external_ref_invalid_reference_type_for_category(self) -> None: + adapter: pydantic.TypeAdapter = pydantic.TypeAdapter(SPDXPackageExternalRefType) + + with pytest.raises(pydantic.ValidationError): + adapter.validate_python( + dict( + referenceCategory="SECURITY", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0", + referenceType="purl", + ) + ) + with pytest.raises(pydantic.ValidationError): + adapter.validate_python( + dict( + referenceCategory="PERSISTENT-ID", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0", + referenceType="purl", + ) + ) + with pytest.raises(pydantic.ValidationError): + adapter.validate_python( + dict( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="gitoid:blob:sha1:261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64", + referenceType="gitbom", + ) + ) + + def test_package_external_ref_invalid_reference(self) -> None: + adapter: pydantic.TypeAdapter = pydantic.TypeAdapter(SPDXPackageExternalRefType) + with pytest.raises( + pydantic.ValidationError, + ): + adapter.validate_python( + dict( + referenceCategory="INVALID", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0", + referenceType="purl", + ) + ) + + def test_to_cyclonedx(self) -> None: + sbom = SPDXSbom( + documentNamespace="NOASSERTION", + creationInfo={ + "creators": ["Tool: cachi2", "Organization: cachi2"], + "created": "2021-07-01T00:00:00Z", + }, + packages=[ + { + "name": "github.com/org/B", + "versionInfo": "v1.0.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/B@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.1.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.1.0?repository_id=R1", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.1.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.1.0?repository_id=R2", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.0.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/A", + "versionInfo": "v1.0.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/A@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "github.com/org/B", + "versionInfo": "v1.0.0", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/github.com/org/B@v1.0.0", + "referenceType": "purl", + } + ], + }, + { + "name": "fmt", + "versionInfo": None, + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/fmt", + "referenceType": "purl", + } + ], + }, + { + "name": "bytes", + "versionInfo": None, + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:golang/bytes", + "referenceType": "purl", + } + ], + }, + ], + ) + cyclonedx_sbom = sbom.to_cyclonedx() + assert cyclonedx_sbom == Sbom( + components=[ + Component(name="bytes", purl="pkg:golang/bytes", version=None), + Component(name="fmt", purl="pkg:golang/fmt", version=None), + Component( + name="github.com/org/A", + purl="pkg:golang/github.com/org/A@v1.0.0", + version="v1.0.0", + ), + Component( + name="github.com/org/A", + purl="pkg:golang/github.com/org/A@v1.1.0?repository_id=R1", + version="v1.1.0", + ), + Component( + name="github.com/org/A", + purl="pkg:golang/github.com/org/A@v1.1.0?repository_id=R2", + version="v1.1.0", + ), + Component( + name="github.com/org/B", + purl="pkg:golang/github.com/org/B@v1.0.0", + version="v1.0.0", + ), + ], + metadata=Metadata( + tools=[Tool(vendor="cachi2", name="cachi2")], + ), + ) + + # SPDX SBOM objects are very verbose and it is rather hard to tell the + # difference between them at a glance. It is unavoidable when a SBOM is + # produced, but it is possible to short-cut during construction time. This + # test case attempts just that. Visual markers are also added to assist in + # locating individual test parameters. Test cases contain progressively + # more complex input SBOMs from the simplest possible to full original + # example. Having smaller examples helps with narrowing down issues when a + # need to update equivalence definition arises. + @pytest.mark.parametrize( + "original_sbom", + ( + # Test case 1. + BLANK_SPDX_SBOM.model_copy( + update={ + "packages": [ + DEFAULT_ROOT_PACKAGE, + # from_package_dict handles hash creation, however the has is also + # needed when defining relationships. + SPDXPackage( + **{ + "name": "github.com/org/B", + "SPDXID": "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c", + "versionInfo": "v1.0.0", + "externalRefs": [ + _gen_ref(locator="pkg:golang/github.com/org/B@v1.0.0") + ], + "annotations": [STOCK_ANNOTATION], + } + ), + ], + "relationships": [ + DEFAULT_ROOT_RELATION, + _root_contains( + "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c" + ), + ], + } + ), + # Test case 2. + BLANK_SPDX_SBOM.model_copy( + update={ + "packages": [ + DEFAULT_ROOT_PACKAGE, + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R1" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/B", + "SPDXID": "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c", + "versionInfo": "v1.0.0", + "externalRefs": [ + _gen_ref(locator="pkg:golang/github.com/org/B@v1.0.0") + ], + "annotations": [STOCK_ANNOTATION], + } + ), + ], + "relationships": [ + DEFAULT_ROOT_RELATION, + # NOTE: for the time being test object has to be constructed in order. + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3" + ), + _root_contains( + "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c" + ), + ], + } + ), + # Test case 3. + BLANK_SPDX_SBOM.model_copy( + update={ + "packages": [ + DEFAULT_ROOT_PACKAGE, + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R1" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/A", + # NOTE: now a full purl goes into package hash + # computation procedure: + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e92e3a95e71ca3b2ef7bd075547593856dec87255626aa3db90a05dcde1b05ec", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R2" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/B", + "SPDXID": "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c", + "versionInfo": "v1.0.0", + "externalRefs": [ + _gen_ref(locator="pkg:golang/github.com/org/B@v1.0.0") + ], + "annotations": [STOCK_ANNOTATION], + } + ), + ], + "relationships": [ + DEFAULT_ROOT_RELATION, + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3" + ), + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e92e3a95e71ca3b2ef7bd075547593856dec87255626aa3db90a05dcde1b05ec" + ), + _root_contains( + "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c" + ), + ], + } + ), + # Test case 4. + BLANK_SPDX_SBOM.model_copy( + update={ + "packages": [ + DEFAULT_ROOT_PACKAGE, + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.0.0-8090f86e9eb851549de5f8391948c1df6a2c8976bfa33c3cbd82e917564ac94f", + "versionInfo": "v1.0.0", + "externalRefs": [_gen_ref("pkg:golang/github.com/org/A@v1.0.0")], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R1" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e92e3a95e71ca3b2ef7bd075547593856dec87255626aa3db90a05dcde1b05ec", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R2" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/B", + "SPDXID": "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c", + "versionInfo": "v1.0.0", + "externalRefs": [ + _gen_ref(locator="pkg:golang/github.com/org/B@v1.0.0") + ], + "annotations": [STOCK_ANNOTATION], + } + ), + ], + "relationships": [ + DEFAULT_ROOT_RELATION, + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.0.0-8090f86e9eb851549de5f8391948c1df6a2c8976bfa33c3cbd82e917564ac94f" + ), + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3" + ), + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e92e3a95e71ca3b2ef7bd075547593856dec87255626aa3db90a05dcde1b05ec" + ), + _root_contains( + "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c" + ), + ], + } + ), + # Test case 5. + BLANK_SPDX_SBOM.model_copy( + # NOTE: model_copy **will not** deduplicate data for you (by design). + update={ + "packages": [ + DEFAULT_ROOT_PACKAGE, + SPDXPackage( + **{ + "name": "bytes", + "SPDXID": "SPDXRef-Package-bytes--159a73f12ce40d92d01ba213c0ec5b442a301c842533acb3487aed9454ae17e7", + "versionInfo": "", + "externalRefs": [_gen_ref("pkg:golang/bytes")], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "fmt", + "SPDXID": "SPDXRef-Package-fmt--7e4d2ed76d4ea914ece19cdfb657d52dfe5c22193e31c8141497806571490439", + "versionInfo": "", + "externalRefs": [_gen_ref("pkg:golang/fmt")], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.0.0-8090f86e9eb851549de5f8391948c1df6a2c8976bfa33c3cbd82e917564ac94f", + "versionInfo": "v1.0.0", + "externalRefs": [_gen_ref("pkg:golang/github.com/org/A@v1.0.0")], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R1" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/A", + "SPDXID": "SPDXRef-Package-github.com/org/A-v1.1.0-e92e3a95e71ca3b2ef7bd075547593856dec87255626aa3db90a05dcde1b05ec", + "versionInfo": "v1.1.0", + "externalRefs": [ + _gen_ref( + locator="pkg:golang/github.com/org/A@v1.1.0?repository_id=R2" + ) + ], + "annotations": [STOCK_ANNOTATION], + } + ), + SPDXPackage( + **{ + "name": "github.com/org/B", + "SPDXID": "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c", + "versionInfo": "v1.0.0", + "externalRefs": [ + _gen_ref(locator="pkg:golang/github.com/org/B@v1.0.0") + ], + "annotations": [STOCK_ANNOTATION], + } + ), + ], + "relationships": [ + DEFAULT_ROOT_RELATION, + _root_contains( + "SPDXRef-Package-bytes--159a73f12ce40d92d01ba213c0ec5b442a301c842533acb3487aed9454ae17e7" + ), + _root_contains( + "SPDXRef-Package-fmt--7e4d2ed76d4ea914ece19cdfb657d52dfe5c22193e31c8141497806571490439" + ), + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.0.0-8090f86e9eb851549de5f8391948c1df6a2c8976bfa33c3cbd82e917564ac94f" + ), + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e4e45c4dc4bfb505f298188b3156fcee718b13e618a73a270401f5a3b77e49b3" + ), + _root_contains( + "SPDXRef-Package-github.com/org/A-v1.1.0-e92e3a95e71ca3b2ef7bd075547593856dec87255626aa3db90a05dcde1b05ec" + ), + _root_contains( + "SPDXRef-Package-github.com/org/B-v1.0.0-f75a590094f92d64111235b9ae298c34b9acd126f8fc6263b7924810bfe6470c" + ), + ], + }, + ), + ), + ) + def test_spdx_sbom_can_be_converted_to_cyclonedx_and_back_without_loosing_any_data( + self, original_sbom: SPDXSbom, isodate: datetime.datetime + ) -> None: + + converted_sbom = original_sbom.to_cyclonedx().to_spdx("NOASSERTION") + + assert json.dumps(original_sbom.model_dump(), sort_keys=True, indent=4) == json.dumps( + converted_sbom.model_dump(), sort_keys=True, indent=4 + ) + + +def test_deduplicate_spdx_packages() -> None: + packages = [ + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R1", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R1", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R1", + referenceType="purl", + ), + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R2", + referenceType="purl", + ), + ], + ), + SPDXPackage( + name="github.com/org/B", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/B", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0?repository_id=R1", + referenceType="purl", + ) + ], + ), + ] + expected_packages = [ + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R1", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/A", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R1", + referenceType="purl", + ), + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/A@v1.0.0?repository_id=R2", + referenceType="purl", + ), + ], + ), + SPDXPackage( + name="github.com/org/B", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0", + referenceType="purl", + ) + ], + ), + SPDXPackage( + name="github.com/org/B", + versionInfo="v1.0.0", + externalRefs=[ + SPDXPackageExternalRefPackageManagerPURL( + referenceCategory="PACKAGE-MANAGER", + referenceLocator="pkg:golang/github.com/org/B@v1.0.0?repository_id=R1", + referenceType="purl", + ) + ], + ), + ] + + deduped_packages = SPDXSbom.deduplicate_spdx_packages(packages) + + assert len(deduped_packages) == len(expected_packages) + assert deduped_packages == expected_packages diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index f43572fbd..a8b3d34df 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -1,3 +1,4 @@ +import datetime import importlib.metadata import logging import os @@ -22,6 +23,7 @@ RequestOutput, Sbom, ) +from cachi2.core.models.sbom import SPDXSbom from cachi2.interface.cli import DEFAULT_OUTPUT, DEFAULT_SOURCE, app runner = typer.testing.CliRunner() @@ -596,7 +598,11 @@ def test_invalid_flags(self, cli_args: list[str], expect_error: str) -> None: ), ], ) - def test_write_json_output(self, request_output: RequestOutput, tmp_cwd: Path) -> None: + def test_write_json_output( + self, + request_output: RequestOutput, + tmp_cwd: Path, + ) -> None: with mock_fetch_deps(output=request_output): invoke_expecting_sucess(app, ["fetch-deps", "gomod"]) @@ -609,6 +615,42 @@ def test_write_json_output(self, request_output: RequestOutput, tmp_cwd: Path) - assert written_build_config == request_output.build_config assert written_sbom == request_output.generate_sbom() + @pytest.mark.parametrize( + "request_output", + [ + RequestOutput.empty(), + RequestOutput.from_obj_list( + components=[ + Component( + name="cool-package", + version="v1.0.0", + purl="pkg:generic/cool-package@v1.0.0", + type="library", + ) + ], + environment_variables=[ + EnvironmentVariable(name="GOMOD_SOMETHING", value="yes"), + ], + project_files=[], + ), + ], + ) + def test_write_json_output_spdx( + self, request_output: RequestOutput, tmp_cwd: Path, isodate: datetime.datetime + ) -> None: + with mock_fetch_deps(output=request_output): + invoke_expecting_sucess(app, ["fetch-deps", "--sbom-output-type", "spdx", "gomod"]) + + build_config_path = tmp_cwd / DEFAULT_OUTPUT / ".build-config.json" + sbom_path = tmp_cwd / DEFAULT_OUTPUT / "bom.json" + + written_build_config = BuildConfig.model_validate_json(build_config_path.read_text()) + sbom_text = sbom_path.read_text() + written_sbom = SPDXSbom.model_validate_json(sbom_text) + + assert written_build_config == request_output.build_config + assert written_sbom == request_output.generate_sbom().to_spdx("NOASSERTION") + def test_delete_existing_deps_dir(self, tmp_cwd: Path) -> None: ouput_dir = tmp_cwd / DEFAULT_OUTPUT pip_deps_dir = ouput_dir / "deps" / "pip" @@ -926,3 +968,45 @@ def test_a_user_can_successfully_save_sboms_merge_results_to_a_file( fp.close() invoke_expecting_sucess(app, ["merge-sboms", "-o", fp.name, *sbom_files_to_merge]) assert Path(fp.name).lstat().st_size > 0, "SBOM failed to be written to output file!" + + @pytest.mark.parametrize( + "sbom_files_to_merge", + [ + [ + "./tests/unit/data/sboms/cachi2.bom.spdx.json", + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + ], + ], + ) + def test_a_user_can_successfully_save_sboms_merge_results_to_a_file_in_spdx_format( + self, + sbom_files_to_merge: list[str], + ) -> None: + with tempfile.NamedTemporaryFile(delete=False) as fp: + fp.close() + invoke_expecting_sucess( + app, + ["merge-sboms", "-o", fp.name, "--sbom-output-type", "spdx", *sbom_files_to_merge], + ) + assert Path(fp.name).lstat().st_size > 0, "SBOM failed to be written to output file!" + + @pytest.mark.parametrize( + "sbom_files_to_merge", + [ + [ + "./tests/unit/data/sboms/cachi2.bom.json", + "./tests/unit/data/sboms/syft.bom.spdx.json", + ], + ], + ) + def test_a_user_can_successfully_save_mixed_sboms_merge_results_to_a_file_in_spdx_format( + self, + sbom_files_to_merge: list[str], + ) -> None: + with tempfile.NamedTemporaryFile(delete=False) as fp: + fp.close() + invoke_expecting_sucess( + app, + ["merge-sboms", "-o", fp.name, "--sbom-output-type", "spdx", *sbom_files_to_merge], + ) + assert Path(fp.name).lstat().st_size > 0, "SBOM failed to be written to output file!" diff --git a/tests/unit/test_merge_spdx.py b/tests/unit/test_merge_spdx.py new file mode 100644 index 000000000..7abe0342b --- /dev/null +++ b/tests/unit/test_merge_spdx.py @@ -0,0 +1,353 @@ +from collections import defaultdict +from functools import reduce +from operator import add +from pathlib import Path +from typing import Any + +import pytest + +from cachi2.core.models.sbom import SPDXSbom + + +def _find_roots(sbom: SPDXSbom) -> list[str]: + direct_rel_map = defaultdict(list) + inverse_map = dict() + for rel in sbom.relationships: + spdx_id, related_spdx = rel.spdxElementId, rel.relatedSpdxElement + direct_rel_map[spdx_id].append(related_spdx) + inverse_map[related_spdx] = spdx_id + unidirectionally_related_package = lambda p: inverse_map.get(p) == sbom.SPDXID # noqa: E731 + roots = list(filter(unidirectionally_related_package, direct_rel_map)) + return roots + + +def _assert_merging_sboms_produces_correct_number_of_packages( + merged_sbom: SPDXSbom, + *sources: SPDXSbom, +) -> None: + # Ignoring mypy because it is cheaper to rebind the name in a local utility. + sources = set(sources) # type: ignore + unqie_packages_across_all_sources = set(sum([s.packages for s in sources], [])) + # Drop one root package per source, add 1 to account for one eventual root: + expected_num_of_packages = len(unqie_packages_across_all_sources) - len(sources) + 1 + actual_num_of_packages = len(merged_sbom.packages) + difference = expected_num_of_packages - actual_num_of_packages + msg = ( + f"""Number of packages in input SBOMs and resulting SBOM do not add up: + The new SBOM contains {abs(difference)} package{'s' if abs(difference) != 1 else ''} """ + f"""{"more" if difference < 0 else "less"} than the both input SBOMs.""" + ) + assert expected_num_of_packages == actual_num_of_packages, msg + + assert len(merged_sbom.relationships) == len( + set(merged_sbom.relationships) + ), "Relationships length mismatch" + + +def _assert_merging_two_distinct_sboms_produces_correct_number_of_packages( + sbom_left: SPDXSbom, + sbom_right: SPDXSbom, + merged_sbom: SPDXSbom, +) -> None: + # Note the 'distinct' part: sbom_left and sbom_right _do not_ intersect. + # You should never verify intersecting SBOMs with this function. + # -1 for one of the roots that must go. + expected_num_of_packages = len(sbom_left.packages) + len(sbom_right.packages) - 1 + actual_num_of_packages = len(merged_sbom.packages) + difference = expected_num_of_packages - actual_num_of_packages + msg = ( + f""" + Number of packages in input SBOMs and resulting SBOM do not add up: + The new SBOM contains {abs(difference)} package{'s' if abs(difference) != 1 else ''} """ + f"""{"more" if difference < 0 else "less"} than the both input SBOMs.""" + ) + assert expected_num_of_packages == actual_num_of_packages, msg + + +def _assert_merging_two_sboms_produces_correct_number_of_files( + sbom_left: SPDXSbom, + sbom_right: SPDXSbom, + merged_sbom: SPDXSbom, +) -> None: + assert len(sbom_left.files) + len(sbom_right.files) == len(merged_sbom.files) + + +def _assert_root_was_inherited_from_left_sbom( + merged_sbom: SPDXSbom, + sbom_left: SPDXSbom, +) -> None: + msg = f"""Root mismatch! + Expected: {sbom_left.root_id} + Got: {merged_sbom.root_id} + """ + assert merged_sbom.root_id == sbom_left.root_id, msg + + +def _assert_there_is_only_one_root(merged_sbom: SPDXSbom) -> None: + assert len(_find_roots(merged_sbom)) == 1, f"Found several roots in {merged_sbom}" + + +def _assert_all_relationships_are_within_the_document(for_sbom: SPDXSbom) -> None: + external_rels = [] + package_names = [p.SPDXID for p in for_sbom.packages] + file_names = [f.SPDXID for f in for_sbom.files] + known_entities = package_names + file_names + + for r in for_sbom.relationships: + if r.relatedSpdxElement not in known_entities: + external_rels.append(r) + if external_rels: + assert False, f"Found relations that lead outside of a document: {external_rels}" + + +def _assert_root_is_present(sbom: SPDXSbom) -> None: + # DocumentRoot can be File or Directory: + fail_msg = "Document root is missing" + root_pfx = "SPDXRef-DocumentRoot-" + # The inline would be too long otherwise, thus lambda, thus noqa. + is_root = lambda p: p.SPDXID is not None and p.SPDXID.startswith(root_pfx) # noqa: E731 + assert any(is_root(p) for p in sbom.packages), fail_msg + + +def _assert_no_relation_is_missing(sbom: SPDXSbom) -> None: + # I.e. ther are at least as many relations as packages _and_ files + # (each package will relate to at least one other package and that would be + # thr root document). + assert len(sbom.relationships) == len( + set(sbom.relationships) + ), "There are duplicate relationships" + fail_msg = ( + "Some packages are not having relationships: not enough relationships for all packages" + ) + assert len(sbom.packages) + len(sbom.files) <= len(sbom.relationships), fail_msg + + +def _assert_no_relationship_points_out(sbom: SPDXSbom) -> None: + packages = set(p.SPDXID for p in sbom.packages) + files = set(f.SPDXID for f in sbom.files) + assert all( + (rel := r.relatedSpdxElement) in packages or rel in files for r in sbom.relationships + ), "Found stray relationship(s)" + + +def _assert_no_unrelated_packages(sbom: SPDXSbom) -> None: + ids_related_to = set(r.relatedSpdxElement for r in sbom.relationships) + assert all(p.SPDXID in ids_related_to for p in sbom.packages), "Unrelated packages detecetd" + + +def _assert_no_unrelated_files(sbom: SPDXSbom) -> None: + ids_related_to = set(r.relatedSpdxElement for r in sbom.relationships) + assert all(f.SPDXID in ids_related_to for f in sbom.files), "Unrelated files detected" + + +def _assert_sbom_is_well_formed(sbom: SPDXSbom) -> None: + _assert_root_is_present(sbom) + _assert_no_relation_is_missing(sbom) + _assert_no_relationship_points_out(sbom) + _assert_no_unrelated_packages(sbom) + _assert_no_unrelated_files(sbom) + + +def _assert_no_relationship_is_duplicated(sbom: SPDXSbom) -> None: + have_relationships = len(sbom.relationships) + should_have_relationships = len(set(sbom.relationships)) + fail_msg = ( + "Relationships duplication detected: have " + f"{have_relationships - should_have_relationships} relationships more than expected." + ) + assert have_relationships == should_have_relationships, fail_msg + + +# Data for this test was generated with syft like this: +# For a directory: +# $ ./syft dir:experiments/ -o spdx-json > experiments.json +# $ jq < experiments.json > experiments.pretty.json +# $ ls experiments +# $ ash-0.3.8-20.el4_7.1.x86_64.rpm +# For a container image (sha256:4048db5d36726e313ab8f7ffccf2362a34cba69e4cdd49119713483a68641fce): +# $ ./syft alpine -o spdx-json > alpine.json +# $ jq < alpine.json > alpine.pretty.json +# I used syft v 0.100.0 to be consistent with what I assumed to be correct test data. +@pytest.mark.parametrize( + "sbom_main,sbom_other", + [ + [ + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + "./tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json", + ], + [ + "./tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json", + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + ], + [ + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + ], + [ + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + "./tests/unit/data/alpine.pretty.json", + ], + ], +) +# NOTE: additional tests should be: +# * verifying rules for mixed merging of SPDX and CycloneDX +# * verifying merging of CycloneDX with a conversion to SPDX +# * ensure no change for SPDX + same SPDX, but pre-converted into CDX +# Also it is likely that conversion willl need to be tested as well +# TODO: +# SPDXSbom.from_cyclonedx(SBOM) +# Sbom.from_spdx(SPDXSbom) +# or even beter: (can be both actually) +# Sbom.from_sbom(other): +# if same_class: return other +# else: return convert_to(cls) +# TODO: merging with self +# deduplication must be accounted for (should happen automatically with model_copy) +def test_merging_two_spdx_sboms_works_in_general_independent_of_order( + sbom_main: Any, # 'Any' is used to prevent mypy from having a fit over re-binding + sbom_other: Any, # 'Any' is used to prevent mypy from having a fit over re-binding +) -> None: + # Re-using the names withing a short scope => mypy sad => must distract mypy. + # Simple ignore won't work here so the need for a cast. + sbom_main = SPDXSbom.from_file(Path(sbom_main)) + sbom_other = SPDXSbom.from_file(Path(sbom_other)) + + merged_sbom = sbom_main + sbom_other + + _assert_all_relationships_are_within_the_document(for_sbom=merged_sbom) + _assert_merging_two_distinct_sboms_produces_correct_number_of_packages( + sbom_main, sbom_other, merged_sbom + ) + _assert_merging_sboms_produces_correct_number_of_packages(merged_sbom, sbom_other, sbom_main) + _assert_merging_two_sboms_produces_correct_number_of_files(sbom_main, sbom_other, merged_sbom) + _assert_root_was_inherited_from_left_sbom(merged_sbom, sbom_main) + _assert_there_is_only_one_root(merged_sbom) + + +@pytest.mark.parametrize( + "sboms_to_merge", + [ + pytest.param( + ( + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + "./tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json", + ), + id="three unique SBOMs", + ), + ], +) +def test_merging_several_spdx_sboms_works_in_general_independent_of_order( + sboms_to_merge: list[Any], # 'Any' is used to prevent mypy from having a fit over re-binding +) -> None: + sboms_to_merge = [SPDXSbom.from_file(Path(s)) for s in sboms_to_merge] + + merged_sbom = reduce(add, sboms_to_merge) + + _assert_all_relationships_are_within_the_document(for_sbom=merged_sbom) + _assert_merging_sboms_produces_correct_number_of_packages(merged_sbom, *sboms_to_merge) + _assert_root_was_inherited_from_left_sbom(merged_sbom, sboms_to_merge[0]) + _assert_there_is_only_one_root(merged_sbom) + _assert_sbom_is_well_formed(merged_sbom) + + +@pytest.mark.parametrize( + "sboms_to_merge", + [ + pytest.param( + ( + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + "./tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json", + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + ), + id="three unique SBOMs and a duplicate", + ), + pytest.param( + ( + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/alpine.pretty.json", + ), + id="merging with self", + ), + ], +) +def test_merging_same_spdx_sbom_multiple_times_does_not_increase_the_number_of_packages( + sboms_to_merge: list[Any], # 'Any' is used to prevent mypy from having a fit over re-binding +) -> None: + sboms_to_merge = [SPDXSbom.from_file(Path(s)) for s in sboms_to_merge] + + merged_sbom = reduce(add, sboms_to_merge) + + _assert_all_relationships_are_within_the_document(for_sbom=merged_sbom) + _assert_merging_sboms_produces_correct_number_of_packages(merged_sbom, *sboms_to_merge) + _assert_root_was_inherited_from_left_sbom(merged_sbom, sboms_to_merge[0]) + _assert_there_is_only_one_root(merged_sbom) + _assert_sbom_is_well_formed(merged_sbom) + + +@pytest.mark.parametrize( + "sboms_to_merge", + [ + pytest.param( + ( + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/alpine.pretty.json", + "./tests/unit/data/alpine.pretty.json", + ), + id="merging with self", + ), + ], +) +def test_merging_spdx_on_self_does_not_modify_the_sbom( + sboms_to_merge: list[Any], # 'Any' is used to prevent mypy from having a fit over re-binding +) -> None: + sboms_to_merge = [SPDXSbom.from_file(Path(s)) for s in sboms_to_merge] + + merged_sbom = reduce(add, sboms_to_merge) + + _assert_all_relationships_are_within_the_document(for_sbom=merged_sbom) + _assert_no_relationship_is_duplicated(merged_sbom) + _assert_merging_sboms_produces_correct_number_of_packages(merged_sbom, *sboms_to_merge) + _assert_root_was_inherited_from_left_sbom(merged_sbom, sboms_to_merge[0]) + _assert_there_is_only_one_root(merged_sbom) + _assert_sbom_is_well_formed(merged_sbom) + + +def _same_relationship_order(sbom1: SPDXSbom, sbom2: SPDXSbom) -> bool: + for r1, r2 in zip(sbom1.relationships, sbom2.relationships): + if r1 != r2: + return False + return True + + +@pytest.mark.parametrize( + "sboms_to_merge", + [ + pytest.param( + ( + "./tests/unit/data/something.simple0.100.0.spdx.pretty.json", + "./tests/unit/data/something.more.simple.0.100.0.spdx.pretty.json", + ), + id="two sboms", + ), + ], +) +def test_merging_spdx_sboms_produces_consistent_relationships_ordering( + sboms_to_merge: list[Any], # 'Any' is used to prevent mypy from having a fit over re-binding +) -> None: + # TODO: this must be moved to integration tests due to hash seed dependency. + # This might require some rework to ITs. Keeping this code here as a reminder. + sboms_to_merge = [SPDXSbom.from_file(Path(s)) for s in sboms_to_merge] + + merged_sbom1 = reduce(add, sboms_to_merge) + merged_sbom2 = reduce(add, sboms_to_merge) + merged_sbom3 = reduce(add, sboms_to_merge) + + assert _same_relationship_order(merged_sbom1, merged_sbom2), "Order mismatch!" + assert _same_relationship_order(merged_sbom2, merged_sbom3), "Order mismatch!" + + _assert_sbom_is_well_formed(merged_sbom1)