"""Signing and checking netOrk's images with cosign (CIS Docker 4.5, NetOrk/netork#1390). Docker Content Trust is deprecated; like Docker's Hardened Images, every image of NetOrk is signed with cosign instead and carries its CIS result and SBOM as attestations. The registry's names stay inside: nothing goes to a public transparency log. Each link of the chain checks the signature of the image it builds on before it builds. The scripts run here against a fake ``cosign`` and ``docker`` that record what they are asked. """ from __future__ import annotations import json import os import re import subprocess from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] SIGN = ROOT / "bin/sign.sh" VERIFY = ROOT / "bin/verify-signature.sh" VERIFY_BASE = ROOT / "bin/verify-base.sh" INSTALL = ROOT / "bin/install-cosign.sh" IMAGE = "registry.netork.io/netork/engine" AMD = "sha256:" + "a" * 64 ARM = "sha256:" + "b" * 64 INDEX = "sha256:" + "e" * 64 _FAKE_COSIGN = """#!/bin/sh echo "cosign $*" >> "$LOG" case "$*" in *"{fail}"*) echo "no matching signatures" >&2; exit 1 ;; esac exit 0 """ _FAKE_DOCKER = """#!/bin/sh case "$*" in *--raw*) cat "$RAW" ;; *"{{json .Image}}"*) cat "$CONFIG" ;; *) echo "docker $*" >> "$LOG" ;; esac """ def _bin(tmp_path: Path, fail: str = "@@never@@") -> dict[str, str]: bin_dir = tmp_path / "bin" bin_dir.mkdir() (bin_dir / "cosign").write_text(_FAKE_COSIGN.replace("{fail}", fail)) (bin_dir / "docker").write_text(_FAKE_DOCKER) for tool in bin_dir.iterdir(): tool.chmod(0o755) return {"PATH": f"{bin_dir}:{os.environ['PATH']}", "LOG": str(tmp_path / "log")} def _log(tmp_path: Path) -> list[str]: log = tmp_path / "log" return log.read_text().splitlines() if log.exists() else [] def _run(script: Path, *args: str, env: dict[str, str]) -> subprocess.CompletedProcess[str]: return subprocess.run( ["bash", str(script), *args], env=env, capture_output=True, text=True, timeout=30 ) class TestSign: def _attest_dir(self, tmp_path: Path, *arches: str) -> Path: out = tmp_path / "attest" out.mkdir() for arch in arches: (out / f"cis-{arch}.json").write_text("{}") (out / f"sbom-{arch}.cdx.json").write_text("{}") return out def _env(self, tmp_path: Path, raw: dict, config: dict, **extra: str) -> dict[str, str]: (tmp_path / "raw.json").write_text(json.dumps(raw)) (tmp_path / "config.json").write_text(json.dumps(config)) env = _bin(tmp_path) env.update( RAW=str(tmp_path / "raw.json"), CONFIG=str(tmp_path / "config.json"), COSIGN_PRIVATE_KEY_B64="a2V5Cg==", COSIGN_PASSWORD="pw", **extra, ) return env def test_a_single_manifest_is_signed_and_attested(self, tmp_path): """A netork dev image: one amd64 manifest, no index.""" env = self._env(tmp_path, {"mediaType": "x", "layers": []}, {"architecture": "amd64"}) attest = self._attest_dir(tmp_path, "amd64") result = _run(SIGN, f"{IMAGE}:main-abc@{AMD}", str(attest), env=env) assert result.returncode == 0, result.stderr log = _log(tmp_path) assert any("sign --yes --recursive" in ln and f"@{AMD}" in ln for ln in log) attests = [ln for ln in log if " attest " in ln] assert len(attests) == 2 and all(f"{IMAGE}@{AMD}" in ln for ln in attests) assert f"signed and attested: {IMAGE}@{AMD} (amd64)" in result.stdout def test_an_index_attests_every_platform(self, tmp_path): raw = { "mediaType": "application/vnd.oci.image.index.v1+json", "manifests": [ {"digest": AMD, "platform": {"os": "linux", "architecture": "amd64"}}, {"digest": ARM, "platform": {"os": "linux", "architecture": "arm64"}}, ], } env = self._env(tmp_path, raw, {}, EXPECT_PLATFORMS="amd64 arm64") attest = self._attest_dir(tmp_path, "amd64", "arm64") result = _run(SIGN, f"{IMAGE}:0.41.0@{INDEX}", str(attest), env=env) assert result.returncode == 0, result.stderr verified = [ln for ln in _log(tmp_path) if "verify-attestation" in ln] assert sum(f"@{AMD}" in ln for ln in verified) == 2 assert sum(f"@{ARM}" in ln for ln in verified) == 2 def test_a_missing_expected_platform_stops_it(self, tmp_path): env = self._env( tmp_path, {"mediaType": "x"}, {"architecture": "amd64"}, EXPECT_PLATFORMS="amd64 arm64" ) result = _run(SIGN, f"{IMAGE}@{AMD}", str(self._attest_dir(tmp_path, "amd64")), env=env) assert result.returncode != 0 assert "expected amd64 arm64" in result.stderr assert not any(" attest " in ln for ln in _log(tmp_path)) def test_a_reference_without_a_digest_is_refused(self, tmp_path): env = self._env(tmp_path, {}, {}) result = _run(SIGN, f"{IMAGE}:latest", str(self._attest_dir(tmp_path)), env=env) assert result.returncode != 0 and "not pinned by digest" in result.stderr def test_without_the_secrets_it_says_so(self, tmp_path): env = _bin(tmp_path) result = _run(SIGN, f"{IMAGE}@{AMD}", str(tmp_path), env=env) assert result.returncode != 0 and "secret is missing" in result.stderr class TestVerify: BASE = f"registry.netork.io/netork/base-python:3.12@{AMD}" OS = f"registry.netork.io/netork/base-os:13@{ARM}" def _dockerfile(self, tmp_path: Path) -> Path: path = tmp_path / "Dockerfile" path.write_text( f"FROM {self.BASE} AS builder\nFROM builder AS drivers\nFROM {self.OS} AS runtime\n" ) return path def test_every_base_of_the_dockerfile_is_verified_against_the_public_key(self, tmp_path): result = _run(VERIFY_BASE, str(self._dockerfile(tmp_path)), env=_bin(tmp_path)) assert result.returncode == 0, result.stderr log = _log(tmp_path) assert log == [ f"cosign verify --key cosign.pub --insecure-ignore-tlog {self.BASE}", f"cosign verify --key cosign.pub --insecure-ignore-tlog {self.OS}", ] def test_an_unsigned_base_stops_the_build(self, tmp_path): env = _bin(tmp_path, fail="base-os") result = _run(VERIFY_BASE, str(self._dockerfile(tmp_path)), env=env) assert result.returncode != 0 assert f"not signed with netOrk's key: {self.OS}" in result.stderr def test_a_base_without_a_digest_stops_the_build(self, tmp_path): path = tmp_path / "Dockerfile" path.write_text("FROM registry.netork.io/netork/base-os:pinned\n") result = _run(VERIFY_BASE, str(path), env=_bin(tmp_path)) assert result.returncode != 0 and "not pinned by digest" in result.stderr assert _log(tmp_path) == [] def test_a_reference_without_a_digest_is_never_checked(self, tmp_path): result = _run(VERIFY, "registry.netork.io/netork/base-os:pinned", env=_bin(tmp_path)) assert result.returncode != 0 and _log(tmp_path) == [] def test_nothing_goes_to_a_public_transparency_log(): config = json.loads((ROOT / "cosign-signing.json").read_text()) text = SIGN.read_text() for services in ("rekorTlogUrls", "caUrls", "oidcUrls", "tsaUrls"): assert config[services] == [] signing = [ ln for ln in text.replace("\\\n", " ").splitlines() if re.search(r"cosign (sign|attest) ", ln) ] assert len(signing) == 2 assert all("--signing-config cosign-signing.json" in ln for ln in signing) def test_the_public_key_is_in_the_repository(): """Whoever pulls an image of NetOrk verifies against it; the private half is the COSIGN_PRIVATE_KEY_B64 secret.""" assert (ROOT / "cosign.pub").read_text().startswith("-----BEGIN PUBLIC KEY-----") def test_the_private_key_arrives_as_one_line(): """The runner masks a secret only where it stands whole; the PEM key's lines did not match, and the key stood in a log (2026-10-22).""" text = SIGN.read_text() assert '"${COSIGN_PRIVATE_KEY_B64:?' in text and "| base64 -d)" in text @pytest.mark.parametrize("arch", ["AMD64", "ARM64"]) def test_cosign_is_pinned_and_checked_for_both_runners(arch): text = INSTALL.read_text() assert re.search(r"^VERSION=v\d+\.\d+\.\d+$", text, re.MULTILINE) assert re.search(rf"^SHA256_{arch}=[0-9a-f]{{64}}$", text, re.MULTILINE) assert "sha256sum -c -" in text