127 lines
4.8 KiB
Python
127 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
from datetime import UTC, datetime
|
|
import hashlib
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
META_ROOT = Path(__file__).resolve().parents[1]
|
|
RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release"
|
|
if str(RELEASE_TOOLS_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(RELEASE_TOOLS_ROOT))
|
|
|
|
from govoplan_release.sbom import build_release_sbom, render_sbom, sbom_sha256, validate_sbom # noqa: E402
|
|
|
|
|
|
class ReleaseSbomTests(unittest.TestCase):
|
|
@staticmethod
|
|
def _build_at(timestamp: datetime) -> dict:
|
|
return build_release_sbom(
|
|
product_version="1.2.3",
|
|
pip_inspect={
|
|
"version": "1",
|
|
"installed": [{"metadata": {"name": "FastAPI", "version": "0.100.0"}}],
|
|
},
|
|
npm_lock={"packages": {"": {"name": "govoplan", "version": "1.2.3"}}},
|
|
timestamp=timestamp,
|
|
)
|
|
|
|
def test_builds_valid_deduplicated_python_and_npm_components(self) -> None:
|
|
integrity = base64.b64encode(b"example digest").decode("ascii")
|
|
payload = build_release_sbom(
|
|
product_version="1.2.3",
|
|
pip_inspect={
|
|
"version": "1",
|
|
"installed": [
|
|
{"metadata": {"name": "FastAPI", "version": "0.100.0"}},
|
|
{"metadata": {"name": "FastAPI", "version": "0.100.0"}},
|
|
],
|
|
},
|
|
npm_lock={
|
|
"packages": {
|
|
"": {"name": "govoplan", "version": "1.2.3"},
|
|
"node_modules/@scope/example": {
|
|
"name": "@scope/example",
|
|
"version": "4.5.6",
|
|
"integrity": f"sha512-{integrity}",
|
|
},
|
|
}
|
|
},
|
|
)
|
|
|
|
validate_sbom(payload)
|
|
|
|
self.assertEqual(2, len(payload["components"]))
|
|
npm = next(item for item in payload["components"] if item["name"] == "@scope/example")
|
|
self.assertEqual("SHA-512", npm["hashes"][0]["alg"])
|
|
self.assertIn("pkg:npm/%40scope/example@4.5.6", npm["purl"])
|
|
self.assertEqual(
|
|
hashlib.sha256(render_sbom(payload).encode("utf-8")).hexdigest(),
|
|
sbom_sha256(payload),
|
|
)
|
|
|
|
def test_rejects_empty_component_inventory(self) -> None:
|
|
payload = build_release_sbom(
|
|
product_version="1.2.3",
|
|
pip_inspect={"version": "1", "installed": []},
|
|
npm_lock={"packages": {"": {"version": "1.2.3"}}},
|
|
)
|
|
|
|
with self.assertRaisesRegex(ValueError, "no dependency components"):
|
|
validate_sbom(payload)
|
|
|
|
def test_fixed_timestamp_produces_identical_bytes_and_digest(self) -> None:
|
|
timestamp = datetime(2026, 7, 21, 8, 30, tzinfo=UTC)
|
|
|
|
first = self._build_at(timestamp)
|
|
second = self._build_at(timestamp)
|
|
|
|
self.assertEqual(render_sbom(first), render_sbom(second))
|
|
self.assertEqual(sbom_sha256(first), sbom_sha256(second))
|
|
|
|
def test_timestamp_changes_serial_and_digest(self) -> None:
|
|
first = self._build_at(datetime(2026, 7, 21, 8, 30, tzinfo=UTC))
|
|
second = self._build_at(datetime(2026, 7, 21, 8, 31, tzinfo=UTC))
|
|
|
|
self.assertNotEqual(first["serialNumber"], second["serialNumber"])
|
|
self.assertNotEqual(sbom_sha256(first), sbom_sha256(second))
|
|
|
|
def test_local_and_credential_bearing_distribution_urls_are_omitted(self) -> None:
|
|
payload = build_release_sbom(
|
|
product_version="1.2.3",
|
|
pip_inspect={
|
|
"version": "1",
|
|
"installed": [
|
|
{
|
|
"metadata": {"name": "LocalPackage", "version": "1.0.0"},
|
|
"direct_url": {"url": "file:///private/build/govoplan-core"},
|
|
},
|
|
{
|
|
"metadata": {"name": "PrivatePackage", "version": "1.0.0"},
|
|
"direct_url": {"url": "https://user:secret@example.test/private.whl"},
|
|
},
|
|
{
|
|
"metadata": {"name": "PublicPackage", "version": "1.0.0"},
|
|
"direct_url": {"url": "https://example.test/public.whl"},
|
|
},
|
|
],
|
|
},
|
|
npm_lock={"packages": {"": {"name": "govoplan", "version": "1.2.3"}}},
|
|
timestamp=datetime(2026, 7, 21, 8, 30, tzinfo=UTC),
|
|
)
|
|
|
|
by_name = {component["name"]: component for component in payload["components"]}
|
|
self.assertNotIn("externalReferences", by_name["LocalPackage"])
|
|
self.assertNotIn("externalReferences", by_name["PrivatePackage"])
|
|
self.assertEqual(
|
|
[{"type": "distribution", "url": "https://example.test/public.whl"}],
|
|
by_name["PublicPackage"]["externalReferences"],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|