Files
ITWorx_MCP_Hub-Public/tests/test_mcp_hub_release_artifact.py
T

365 lines
12 KiB
Python

"""Behavioral tests for the exact protected-run release artifact payload."""
from __future__ import annotations
import copy
import hashlib
import json
from pathlib import Path
import pytest
from scripts import package_mcp_hub_release_artifact as packager
SERVICES = (
"connector-appops",
"connector-gitea",
"connector-ingestion",
"connector-knowledge",
"connector-mobilityops",
"connector-projectbrain",
"connector-stitch",
"connector-unraid",
"contextforge",
"control-api",
"dataplane-router",
"docker-read-broker",
"ingestion-clamd",
"postgres",
"redis",
"web",
"worker",
)
REPORT_NAMES = {
"trivy-db-metadata.json",
*(
name
for service in SERVICES
for name in (f"vulnerability-{service}.json", f"secret-{service}.json")
),
}
REVISION = "a" * 40
TREE = "b" * 40
RUN_CREATED_AT = "2026-10-03T10:11:12.123Z"
def _json_bytes(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode()
def _fixture(tmp_path: Path) -> tuple[dict[str, object], dict[str, dict[str, str]], Path, Path]:
image_tar = tmp_path / "inputs" / "images.tar"
report_dir = tmp_path / "inputs" / "reports"
report_dir.mkdir(parents=True)
image_tar.parent.mkdir(parents=True, exist_ok=True)
image_tar.write_bytes(b"fixture-docker-save-archive\n")
images: dict[str, dict[str, str]] = {}
report_time = "2026-10-03T10:12:00Z"
for index, service in enumerate(SERVICES, start=1):
image_id = f"sha256:{index:064x}"
image_ref = f"registry.example/itworx/{service}:protected-main@sha256:{index:064x}"
images[service] = {
"imageRef": image_ref,
"imageId": image_id,
"archiveRef": f"registry.example/itworx/{service}:archive-{REVISION[:12]}",
}
vulnerability = {
"SchemaVersion": 2,
"CreatedAt": report_time,
"Trivy": {"Version": "0.73.0"},
"Metadata": {
"ImageID": image_id,
"ImageConfig": {"os": "linux", "architecture": "amd64"},
},
"Results": [
{
"Packages": [{"Name": "fixture-package", "Version": "1.0"}],
"Vulnerabilities": [],
}
],
}
secret = {
"ImageID": image_id,
"CreatedAt": report_time,
"TrivyVersion": "0.73.0",
"FindingCount": 0,
"SensitiveFindingDetailsPersisted": False,
"Results": [{"FindingCount": 0}],
}
(report_dir / f"vulnerability-{service}.json").write_bytes(_json_bytes(vulnerability))
(report_dir / f"secret-{service}.json").write_bytes(_json_bytes(secret))
database = {
"UpdatedAt": report_time,
"DownloadedAt": report_time,
"Version": 2,
"NextUpdate": "2026-10-04T10:12:00Z",
}
(report_dir / "trivy-db-metadata.json").write_bytes(_json_bytes(database))
build = {
"id": REVISION[:12],
"repository": "jens/itworx_mcp_hub",
"revision": REVISION,
"tree": TREE,
"treeDigest": hashlib.sha256(f"git-tree:{TREE}".encode()).hexdigest(),
"builtAt": RUN_CREATED_AT,
}
context: dict[str, object] = {
"repository": "jens/itworx_mcp_hub",
"revision": REVISION,
"tree": TREE,
"workflowPath": ".gitea/workflows/managed-validation.yml",
"workflowRunId": 2468,
"workflowRunCreatedAt": RUN_CREATED_AT,
"artifactName": f"hub-release-candidate-{REVISION}",
"build": build,
"sourceDateEpoch": "1791022272",
}
return context, images, image_tar, report_dir
def _package(
output_dir: Path,
context: dict[str, object],
images: dict[str, dict[str, str]],
image_tar: Path,
report_dir: Path,
) -> dict[str, object]:
return packager.package_candidate_artifact(
output_dir=output_dir,
manifest_context=context,
images=images,
image_tar_path=image_tar,
report_dir=report_dir,
)
@pytest.fixture(autouse=True)
def _bind_fixture_to_test_git(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
packager,
"_git_source_identity",
lambda: (REVISION, TREE, "1791022272"),
)
def test_package_contains_exact_importer_payload_and_binds_original_bytes(tmp_path: Path) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
output = tmp_path / "artifact"
returned_manifest = _package(output, context, images, image_tar, report_dir)
expected_names = {"candidate-manifest.json", "images.tar", *REPORT_NAMES}
assert {path.name for path in output.iterdir()} == expected_names
assert all(path.is_file() and not path.is_symlink() for path in output.iterdir())
manifest_bytes = (output / "candidate-manifest.json").read_bytes()
manifest = json.loads(manifest_bytes)
tar_bytes = image_tar.read_bytes()
assert manifest == returned_manifest
assert set(manifest) == {
"schema",
"repository",
"sourceRevision",
"sourceTree",
"workflowPath",
"workflowRunId",
"artifactName",
"build",
"sourceDateEpoch",
"dockerTarBytes",
"dockerTarSha256",
"images",
"reports",
}
assert manifest["schema"] == "itworx.mcp-hub-image-artifact/v1"
assert manifest["repository"] == context["repository"]
assert manifest["sourceRevision"] == context["revision"]
assert manifest["sourceTree"] == context["tree"]
assert manifest["workflowPath"] == context["workflowPath"]
assert manifest["workflowRunId"] == context["workflowRunId"]
assert manifest["artifactName"] == context["artifactName"]
assert manifest["build"] == context["build"]
assert manifest["sourceDateEpoch"] == context["sourceDateEpoch"]
assert manifest["dockerTarBytes"] == len(tar_bytes)
assert manifest["dockerTarSha256"] == hashlib.sha256(tar_bytes).hexdigest()
assert manifest["images"] == images
assert manifest["reports"] == {
name: {
"sha256": hashlib.sha256((report_dir / name).read_bytes()).hexdigest(),
"bytes": (report_dir / name).stat().st_size,
}
for name in sorted(REPORT_NAMES)
}
assert (output / "images.tar").read_bytes() == tar_bytes
for name in REPORT_NAMES:
assert (output / name).read_bytes() == (report_dir / name).read_bytes()
def test_canonicalizes_the_exact_workflow_run_timestamp_to_importer_format(
tmp_path: Path,
) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
context["workflowRunCreatedAt"] = "2026-10-03T10:11:12Z"
build = dict(context["build"])
build["builtAt"] = "2026-10-03T10:11:12.000Z"
context["build"] = build
manifest = _package(tmp_path / "artifact", context, images, image_tar, report_dir)
assert manifest["build"]["builtAt"] == "2026-10-03T10:11:12.000Z"
@pytest.mark.parametrize(
("mutation", "value"),
[
("revision", "c" * 40),
("tree", "d" * 40),
("run_id", 0),
("run_time", "2026-10-03T10:11:12Z"),
("artifact_name", "hub-release-candidate-wrong"),
("epoch", "01791022272"),
],
)
def test_rejects_unbound_frozen_context_without_publishing(
tmp_path: Path, mutation: str, value: object
) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
context = copy.deepcopy(context)
if mutation == "revision":
context["revision"] = value
elif mutation == "tree":
context["tree"] = value
elif mutation == "run_id":
context["workflowRunId"] = value
elif mutation == "run_time":
context["workflowRunCreatedAt"] = value
elif mutation == "artifact_name":
context["artifactName"] = value
else:
context["sourceDateEpoch"] = value
output = tmp_path / "artifact"
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert not output.exists()
assert not list(tmp_path.glob(".hub-release-artifact-*"))
@pytest.mark.parametrize("report_change", ["missing", "extra", "oversized", "wrong_image"])
def test_rejects_invalid_fixed_report_inventory_without_partial_output(
tmp_path: Path, report_change: str
) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
if report_change == "missing":
(report_dir / "secret-worker.json").unlink()
elif report_change == "extra":
(report_dir / "unlisted.json").write_text("{}", encoding="utf-8")
elif report_change == "oversized":
(report_dir / "secret-worker.json").write_bytes(b" " * (2 * 1024**2 + 1))
else:
data = json.loads((report_dir / "vulnerability-worker.json").read_text(encoding="utf-8"))
data["Metadata"]["ImageID"] = "sha256:" + "f" * 64
(report_dir / "vulnerability-worker.json").write_bytes(_json_bytes(data))
output = tmp_path / "artifact"
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert not output.exists()
def test_rejects_secret_findings_instead_of_redacting_or_rewriting_them(tmp_path: Path) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
secret_path = report_dir / "secret-worker.json"
secret = json.loads(secret_path.read_text(encoding="utf-8"))
secret["FindingCount"] = 1
secret["SensitiveFindingDetailsPersisted"] = True
secret_path.write_bytes(_json_bytes(secret))
original = secret_path.read_bytes()
output = tmp_path / "artifact"
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert not output.exists()
assert secret_path.read_bytes() == original
def test_rejects_output_collision_without_overwriting_existing_files(tmp_path: Path) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
output = tmp_path / "artifact"
output.mkdir()
marker = output / "keep.txt"
marker.write_text("preserve", encoding="utf-8")
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert marker.read_text(encoding="utf-8") == "preserve"
assert {path.name for path in output.iterdir()} == {"keep.txt"}
def test_rejects_output_directory_created_at_atomic_publish_boundary(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
output = tmp_path / "artifact"
original_publish = packager._rename_noreplace
def create_collision_then_publish(source: Path, destination: Path) -> None:
destination.mkdir()
original_publish(source, destination)
monkeypatch.setattr(packager, "_rename_noreplace", create_collision_then_publish)
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert output.is_dir()
assert list(output.iterdir()) == []
assert not list(tmp_path.glob(".hub-release-artifact-*"))
def test_rejects_wrong_image_inventory_without_partial_output(tmp_path: Path) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
images.pop("worker")
output = tmp_path / "artifact"
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert not output.exists()
def test_rejects_source_identity_that_does_not_match_checkout(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
monkeypatch.setattr(
packager,
"_git_source_identity",
lambda: ("f" * 40, TREE, "1791022272"),
)
output = tmp_path / "artifact"
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert not output.exists()
def test_rejects_hardlinked_report_without_publishing(tmp_path: Path) -> None:
context, images, image_tar, report_dir = _fixture(tmp_path)
secret_path = report_dir / "secret-worker.json"
secret_path.unlink()
try:
secret_path.hardlink_to(report_dir / "secret-web.json")
except OSError as exc:
pytest.skip(f"filesystem does not support the hard-link fixture: {exc}")
output = tmp_path / "artifact"
with pytest.raises(packager.ArtifactRejected):
_package(output, context, images, image_tar, report_dir)
assert not output.exists()