Menu
BifrOSt
publicLatest change 36972df1d1a942fadabbe03341629b5238357c8a - Reject unprepared installer bootstrap packages by Ólafur Búi Ólafsson
from __future__ import annotations
import copy
import importlib.machinery
import importlib.util
import json
from pathlib import Path
import sys
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
TMP_ROOT = ROOT / ".tmp"
def load_python(name: str, path: Path):
loader = importlib.machinery.SourceFileLoader(name, str(path))
spec = importlib.util.spec_from_loader(name, loader)
assert spec is not None
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
loader.exec_module(module)
return module
bootstrap = load_python(
"bifrost_bootstrap",
ROOT / "profile/airootfs/usr/local/lib/bifrost_bootstrap.py",
)
builder = load_python(
"bifrost_alpm_builder",
ROOT / "packaging/alpm/build-repository.py",
)
VERSION = "0.2.2"
REVISION = "a" * 40
EPOCH = 1_786_113_295
def prepared_release() -> dict[str, object]:
return {
"schema_version": 1,
"provenance_status": "build-input",
"version": VERSION,
"source_revision": REVISION,
"source_date_epoch": EPOCH,
"build_id": f"bifrost-{VERSION}-{REVISION[:12]}-{EPOCH}",
"profile_sha256": "b" * 64,
"profile_digest_excludes": list(bootstrap.PROFILE_DIGEST_EXCLUDES),
"iso": {
"file": f"bifrost-{VERSION}-x86_64.iso",
"bytes": None,
"sha256": None,
},
}
class ReleaseDocumentTests(unittest.TestCase):
def test_prepared_build_input_is_accepted(self) -> None:
bootstrap.validate_release_document(prepared_release(), VERSION)
def test_unsigned_development_payload_is_rejected(self) -> None:
release = prepared_release()
release.update(
{
"provenance_status": "unsigned-development",
"source_revision": None,
"source_date_epoch": 0,
"build_id": None,
"profile_sha256": None,
}
)
with self.assertRaisesRegex(bootstrap.VerificationError, "not a prepared build input"):
bootstrap.validate_release_document(release, VERSION)
def test_mismatched_prepared_fields_are_rejected(self) -> None:
cases = {
"version": "0.2.1",
"source_revision": "a" * 39,
"source_date_epoch": True,
"build_id": "wrong",
"profile_sha256": "b" * 63,
"profile_digest_excludes": [],
"iso": {"file": "wrong.iso", "bytes": None, "sha256": None},
}
for field, value in cases.items():
with self.subTest(field=field):
release = copy.deepcopy(prepared_release())
release[field] = value
with self.assertRaises(bootstrap.VerificationError):
bootstrap.validate_release_document(release, VERSION)
class RepositoryBuildGateTests(unittest.TestCase):
def setUp(self) -> None:
TMP_ROOT.mkdir(exist_ok=True)
self.directory = tempfile.TemporaryDirectory(dir=TMP_ROOT)
self.addCleanup(self.directory.cleanup)
self.root = Path(self.directory.name)
self.release_path = (
self.root
/ "profile/airootfs/usr/share/bifrost/installed-root/usr/share/bifrost/release.json"
)
self.release_path.parent.mkdir(parents=True)
def write_release(self, release: dict[str, object]) -> None:
self.release_path.write_text(json.dumps(release) + "\n", encoding="utf-8")
def test_builder_accepts_prepared_provenance(self) -> None:
self.write_release(prepared_release())
builder.validate_source_provenance(self.root, VERSION)
def test_builder_refuses_to_sign_unsigned_development_payload(self) -> None:
release = prepared_release()
release["provenance_status"] = "unsigned-development"
self.write_release(release)
with self.assertRaisesRegex(builder.BuildError, "run generate-release-metadata.py"):
builder.validate_source_provenance(self.root, VERSION)
if __name__ == "__main__":
unittest.main()