fix: require package v2 for install readiness
This commit is contained in:
@@ -35,7 +35,10 @@ RESTORE_TEST_SCHEMA = "yuqei.first-release-restore-test.v1"
|
||||
PLAN_SCHEMA = "yuqei.first-release-snapshot-restore-plan.v1"
|
||||
READINESS_RECEIPT_SCHEMA = "yuqei.first-release-recovery-readiness-receipt.v1"
|
||||
IMAGE_IDENTITY_SCHEMA = "yuqei.release-image-identity.v2"
|
||||
PACKAGE_SCHEMA = "yuqei.release-package.v1"
|
||||
PACKAGE_SCHEMA = "yuqei.release-package.v2"
|
||||
READ_ONLY_PACKAGE_SCHEMAS = frozenset(
|
||||
{"yuqei.release-package.v1", PACKAGE_SCHEMA}
|
||||
)
|
||||
SOURCE_MANIFEST_SCHEMA = "yuqei.release-source-manifest.v1"
|
||||
CHECKSUM_MANIFEST_SCHEMA = "yuqei.release-package-checksums.v1"
|
||||
INSTALLABLE_MANIFEST_SCHEMA = "yuqei.installable-release-manifest.v1"
|
||||
@@ -416,6 +419,7 @@ def _validate_package(
|
||||
*,
|
||||
repo_root: Path,
|
||||
package_validator: Callable[..., Any] | None = None,
|
||||
require_installable_package_v2: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
root, package, actual, bootstrapped_release, release_sha = _bootstrap_release_package(
|
||||
package_dir,
|
||||
@@ -438,7 +442,12 @@ def _validate_package(
|
||||
if getattr(validated, "package_sha256", None) != actual:
|
||||
raise SnapshotRestorePlanError("official validator package hash drifted")
|
||||
validated_package = getattr(validated, "package", None)
|
||||
if validated_package != package or package.get("schema_version") != PACKAGE_SCHEMA:
|
||||
package_schema = package.get("schema_version")
|
||||
if (
|
||||
validated_package != package
|
||||
or package_schema not in READ_ONLY_PACKAGE_SCHEMAS
|
||||
or (require_installable_package_v2 and package_schema != PACKAGE_SCHEMA)
|
||||
):
|
||||
raise SnapshotRestorePlanError("official validator package payload/schema drifted")
|
||||
release_manifest = getattr(validated, "release_manifest", None)
|
||||
if release_manifest != bootstrapped_release:
|
||||
@@ -581,6 +590,7 @@ def _validate_package(
|
||||
raise SnapshotRestorePlanError("official validator trusted build receipt drifted")
|
||||
return {
|
||||
"release_id": release_id,
|
||||
"package_schema_version": package_schema,
|
||||
"package_sha256": actual,
|
||||
"source_commit": source_commit,
|
||||
"release_manifest_sha256": release_sha,
|
||||
@@ -1400,12 +1410,14 @@ def _verify_raw_inputs(
|
||||
trusted_root_public_key_file: Path,
|
||||
now: datetime,
|
||||
package_validator_for_tests: Callable[..., Any] | None = None,
|
||||
require_installable_package_v2: bool = False,
|
||||
) -> tuple[dict[str, Any], dict[str, Any], str]:
|
||||
package_binding = _validate_package(
|
||||
package_dir,
|
||||
expected_package_sha256,
|
||||
repo_root=repo_root,
|
||||
package_validator=package_validator_for_tests,
|
||||
require_installable_package_v2=require_installable_package_v2,
|
||||
)
|
||||
evidence = load_json(evidence_file, label="first-release recovery evidence")
|
||||
keyring = load_json(keyring_file, label="recovery keyring")
|
||||
@@ -1458,6 +1470,7 @@ def verify_and_build_output(
|
||||
trusted_root_public_key_file=trusted_root_public_key_file,
|
||||
now=now,
|
||||
package_validator_for_tests=package_validator_for_tests,
|
||||
require_installable_package_v2=(output_kind == "install-readiness-receipt"),
|
||||
)
|
||||
if output_kind == "plan":
|
||||
return build_plan(
|
||||
@@ -1527,6 +1540,7 @@ def validate_install_readiness(
|
||||
keyring_file=keyring_file,
|
||||
trusted_root_public_key_file=trusted_root_public_key_file,
|
||||
now=now,
|
||||
require_installable_package_v2=True,
|
||||
)
|
||||
rebuilt = build_install_readiness_receipt(
|
||||
package_binding=package_binding,
|
||||
|
||||
@@ -594,6 +594,19 @@ def _build_output(case: dict[str, Any], output_kind: str) -> dict[str, Any]:
|
||||
)
|
||||
|
||||
|
||||
def _rewrite_package_schema(case: dict[str, Any], schema: str) -> None:
|
||||
package = json.loads(case["package_path"].read_text(encoding="utf-8"))
|
||||
package["schema_version"] = schema
|
||||
package = _embedded(
|
||||
{key: value for key, value in package.items() if key != "package_payload_sha256"},
|
||||
"package_payload_sha256",
|
||||
)
|
||||
_write_json(case["package_path"], package)
|
||||
case["package_sha256"] = _sha(case["package_path"])
|
||||
case["validated_package"].package = package
|
||||
case["validated_package"].package_sha256 = case["package_sha256"]
|
||||
|
||||
|
||||
def test_valid_first_release_evidence_emits_only_a_non_writing_plan(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
@@ -674,6 +687,29 @@ def test_install_readiness_receipt_binds_every_gate_input_without_allowing_resto
|
||||
)
|
||||
|
||||
|
||||
def test_v1_package_remains_read_only_and_cannot_mint_install_readiness(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
case = _case(tmp_path)
|
||||
_rewrite_package_schema(case, "yuqei.release-package.v1")
|
||||
binding = planner._validate_package(
|
||||
case["package_dir"],
|
||||
case["package_sha256"],
|
||||
repo_root=case["repo_root"],
|
||||
package_validator=case["package_validator"],
|
||||
)
|
||||
assert binding["package_schema_version"] == "yuqei.release-package.v1"
|
||||
|
||||
with pytest.raises(planner.SnapshotRestorePlanError, match="payload/schema drifted"):
|
||||
planner._validate_package(
|
||||
case["package_dir"],
|
||||
case["package_sha256"],
|
||||
repo_root=case["repo_root"],
|
||||
package_validator=case["package_validator"],
|
||||
require_installable_package_v2=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("which", ("application", "infrastructure"))
|
||||
def test_official_package_result_must_cover_all_eleven_images(
|
||||
tmp_path: Path,
|
||||
|
||||
Reference in New Issue
Block a user