fix(release): reject ambiguous activation asset inventories

Count all activation marker names before validating their metadata so a malformed duplicate cannot pass the immutable packet boundary. Reproduced acceptance with a valid uploaded marker plus a zero-size pending duplicate; both valid and malformed duplicates now fail before attestation or download.

Validation: 13 focused integrity tests and 46 promotion policy tests pass; bash syntax and git diff checks pass. No publication or deployment performed.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-05 15:42:03 +01:00
parent 2356e43000
commit bf6121e41d
3 changed files with 25 additions and 1 deletions
@@ -5019,7 +5019,14 @@ back to a marker-free draft.
`scripts/verify-github-release-integrity.sh` is the shared post-publication
check. It binds the release database ID, tag, exact source SHA, immutable state,
and single digest-bearing activation marker, then requires `gh release verify`
and single digest-bearing activation marker. Uniqueness counts every asset named
`release-activation.json`, including pending, empty, or malformed entries,
before validating the marker's metadata. Duplicate names must fail before
attestation or download; filtering out invalid entries must not make an
ambiguous inventory acceptable. The regression cases in
`scripts/release_control/verify_github_release_integrity_test.py` cover both
valid duplicates and a valid marker accompanied by a malformed duplicate.
The check then requires `gh release verify`
to validate GitHub's signed release attestation. It must then download the
activation marker from that release and require `gh release verify-asset` to
bind the exact consumed bytes to the signed release attestation. Filename,
@@ -230,6 +230,22 @@ class VerifyGitHubReleaseIntegrityTest(unittest.TestCase):
self.assertNotEqual(result.returncode, 0)
self.assertIn("activation marker", result.stderr)
def test_rejects_duplicate_activation_names_before_attestation(self) -> None:
for duplicate in (
{"state": "uploaded", "size": 300, "digest": "sha256:" + "b" * 64},
{"state": "new", "size": 0, "digest": None},
):
with self.subTest(duplicate=duplicate):
release = self.release()
release["assets"].append(
{"name": "release-activation.json", **duplicate}
)
result, calls = self.run_verifier(release)
self.assertNotEqual(result.returncode, 0)
self.assertIn("activation marker", result.stderr)
self.assertNotIn("release verify", calls)
self.assertNotIn("release download", calls)
def test_rejects_failed_release_attestation(self) -> None:
result, _ = self.run_verifier(self.release(), verification_succeeds=False)
self.assertNotEqual(result.returncode, 0)
@@ -97,6 +97,7 @@ if ! jq -e \
.immutable == true and
($expected_release_id == "" or (.id | tostring) == $expected_release_id) and
($expected_source_sha == "" or .target_commitish == $expected_source_sha) and
([.assets[]? | select(.name == "release-activation.json")] | length == 1) and
([.assets[]? | select(
.name == "release-activation.json" and
.state == "uploaded" and