mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user