Authenticate every published installer

This commit is contained in:
pulse-triage[bot] 2026-08-31 04:50:48 +01:00
parent a190703fe5
commit 240adaa5e4
6 changed files with 79 additions and 2 deletions

View file

@ -3429,6 +3429,12 @@ installers consume. macOS notarization is intentionally skipped
for v1: the README documents the Gatekeeper bypass and the
install-script flow downloads the same unsigned binary, with the
audit trail of signed-manifest and SHA256 verification preserved.
Because the shell installer is itself an executable bootstrap trust boundary,
release assembly must include `install-mcp.sh` in the authenticated checksum
manifest and emit its checksum and detached-signature sidecars before upload;
validating only the binary that an unauthenticated installer chooses is not a
complete install chain. Local and post-publication validation require every
published installer entry exactly once in that signed manifest.
The adapter's complete request/response tool-list projection, manifest
projection, capability and governance metadata formatting, request/response
tool filtering, typed input-schema projection, and API route/body call
@ -4416,7 +4422,8 @@ release packet, and make post-publication validation authenticate
`checksums.txt` and every listed artifact's `.sshsig` against the configured
`PULSE_UPDATE_SIGNING_PUBLIC_KEY`, not merely test that sidecars are present.
Validation must fail if the trust root is unavailable, if any signature is
invalid, if any published artifact or `checksums.txt` is missing its
invalid, if any published installer is absent from the authenticated checksum
manifest, if any published artifact or `checksums.txt` is missing its
`.sshsig` sidecar, if the authenticated checksum manifest contains duplicate
asset filenames or trailing fields, or if the canonical
release-packet SBOM is absent so published RC/stable downloads can keep the

View file

@ -1363,6 +1363,9 @@ func TestReleaseValidationRequiresSignedSidecars(t *testing.T) {
}
localValidator := string(localValidatorBytes)
localRequired := []string{
`"install-mcp.sh"`,
`for installer in install.sh install-docker.sh install-mcp.sh install-mcp.ps1 install.ps1 pulse-auto-update.sh; do`,
`checksums.txt must contain exactly one entry for release installer ${installer}`,
`"pulse-v${PULSE_VERSION}-release.sbom.spdx.json"`,
`release_sbom="pulse-${PULSE_TAG}-release.sbom.spdx.json"`,
`error "checksums.txt is missing ${release_sbom}"`,
@ -1460,6 +1463,9 @@ func TestReleaseValidationRequiresSignedSidecars(t *testing.T) {
}
publishedValidator := string(publishedValidatorBytes)
publishedRequired := []string{
`REQUIRED_SIGNED_INSTALLERS=(`,
`install-mcp.sh`,
`Authenticated checksums.txt must contain exactly one valid entry for published installer ${installer}.`,
`RELEASE_SBOM="pulse-${TAG}-release.sbom.spdx.json"`,
`PULSE_UPDATE_SIGNING_PUBLIC_KEY is required to authenticate published release assets.`,
`go -C "$REPO_ROOT" run ./scripts/release_update_key.go public-key-ssh`,
@ -1503,6 +1509,7 @@ func TestReleaseValidationRequiresSignedSidecars(t *testing.T) {
"make post-publication validation authenticate",
"every listed artifact's `.sshsig` against the configured",
"Validation must fail if the trust root is unavailable",
"any published installer is absent from the authenticated checksum",
"release-packet SBOM is absent",
"download endpoints must return checksum and signature headers",
"must disable Go's automatic VCS stamping",
@ -2805,6 +2812,7 @@ func TestBuildReleasePackagesPulseMcpForAllPlatforms(t *testing.T) {
`checksum_files+=( pulse-mcp-linux-* )`,
`checksum_files+=( pulse-mcp-darwin-* )`,
`checksum_files+=( pulse-mcp-freebsd-* )`,
`checksum_files+=( install-mcp.sh )`,
} {
if !strings.Contains(string(commonContent), needle) {
t.Fatalf("release checksum collection missing bare MCP assets: %s", needle)
@ -2816,6 +2824,7 @@ func TestBuildReleasePackagesPulseMcpForAllPlatforms(t *testing.T) {
"pulse-mcp-linux-amd64",
"pulse-mcp-darwin-arm64",
"pulse-mcp-freebsd-amd64",
"install-mcp.sh",
} {
if err := os.WriteFile(filepath.Join(releaseDir, asset), []byte(asset), 0o755); err != nil {
t.Fatalf("write MCP checksum fixture: %v", err)
@ -2826,7 +2835,7 @@ func TestBuildReleasePackagesPulseMcpForAllPlatforms(t *testing.T) {
if err != nil {
t.Fatalf("collect MCP release checksum files: %v\n%s", err, checksumOutput)
}
for _, asset := range []string{"pulse-mcp-linux-amd64", "pulse-mcp-darwin-arm64", "pulse-mcp-freebsd-amd64"} {
for _, asset := range []string{"pulse-mcp-linux-amd64", "pulse-mcp-darwin-arm64", "pulse-mcp-freebsd-amd64", "install-mcp.sh"} {
if !strings.Contains(string(checksumOutput), asset) {
t.Fatalf("bare MCP release asset %s missing from checksum/signature input:\n%s", asset, checksumOutput)
}

View file

@ -287,6 +287,9 @@ pulse_release_collect_checksum_files() {
if [[ -f "install-docker.sh" ]]; then
checksum_files+=( install-docker.sh )
fi
if [[ -f "install-mcp.sh" ]]; then
checksum_files+=( install-mcp.sh )
fi
if [[ -f "pulse-auto-update.sh" ]]; then
checksum_files+=( pulse-auto-update.sh )
fi

View file

@ -47,7 +47,12 @@ class ValidatePublishedReleaseTest(unittest.TestCase):
payloads = {
SBOM: b'{"spdxVersion":"SPDX-2.3"}\n',
"install-docker.sh": b"#!/bin/sh\necho docker installer\n",
"install-mcp.sh": b"#!/bin/sh\necho mcp installer\n",
"install-mcp.ps1": b"Write-Output 'mcp installer'\r\n",
"install.ps1": b"Write-Output 'agent installer'\r\n",
"install.sh": b"#!/bin/sh\necho pulse\n",
"pulse-auto-update.sh": b"#!/bin/sh\necho update\n",
}
checksum_lines: list[str] = []
for name, content in payloads.items():
@ -154,6 +159,23 @@ printf '%s\n' "${FAKE_SSH_PUBLIC_KEY}"
self.assertNotEqual(result.returncode, 0)
self.assertIn("SSH signature verification failed for install.sh", result.stderr)
def test_rejects_unsigned_published_mcp_installer(self) -> None:
checksums = (self.assets / "checksums.txt").read_text(encoding="utf-8")
filtered = "".join(
line
for line in checksums.splitlines(keepends=True)
if not line.endswith(" install-mcp.sh\n")
)
self.replace_signed_checksums(filtered)
result = self.run_validator()
self.assertNotEqual(result.returncode, 0)
self.assertIn(
"Authenticated checksums.txt must contain exactly one valid entry for published installer install-mcp.sh.",
result.stderr,
)
def test_rejects_forged_checksum_manifest_before_using_it(self) -> None:
with (self.assets / "checksums.txt").open("a", encoding="utf-8") as handle:
handle.write(f"{'0' * 64} attacker-payload\n")

View file

@ -85,6 +85,21 @@ if ! verify_signature "$CHECKSUMS_PATH" "$CHECKSUMS_SIG_PATH" "checksums.txt for
exit 1
fi
REQUIRED_SIGNED_INSTALLERS=(
install.sh
install-docker.sh
install-mcp.sh
install-mcp.ps1
install.ps1
pulse-auto-update.sh
)
for installer in "${REQUIRED_SIGNED_INSTALLERS[@]}"; do
if ! awk -v name="$installer" '$2 == name { found = 1 } END { exit !found }' "$CHECKSUMS_PATH"; then
echo "Authenticated checksums.txt must contain exactly one valid entry for published installer ${installer}." >&2
exit 1
fi
done
RELEASE_SBOM="pulse-${TAG}-release.sbom.spdx.json"
if ! awk '{print $2}' "$CHECKSUMS_PATH" | grep -Fx "$RELEASE_SBOM" >/dev/null 2>&1; then
echo "checksums.txt does not list ${RELEASE_SBOM} for ${TAG}" >&2
@ -191,4 +206,11 @@ if [[ "$status" -ne 0 ]]; then
exit 1
fi
for installer in "${REQUIRED_SIGNED_INSTALLERS[@]}"; do
if ! grep -Fxq -- "$installer" "$SEEN_FILENAMES_PATH"; then
echo "Authenticated checksums.txt must contain exactly one valid entry for published installer ${installer}." >&2
exit 1
fi
done
echo "Published release assets for ${TAG} match authenticated checksums.txt, *.sha256 files, and verified *.sshsig sidecars."

View file

@ -449,6 +449,11 @@ info "Checking required release assets..."
# They are only included in Docker images for /download/ endpoints
required_assets=(
"install.sh"
"install-docker.sh"
"install-mcp.sh"
"install-mcp.ps1"
"install.ps1"
"pulse-auto-update.sh"
"checksums.txt"
"pulse-v${PULSE_VERSION}-release.sbom.spdx.json"
"pulse-v${PULSE_VERSION}.tar.gz"
@ -490,6 +495,15 @@ if [ $missing_count -gt 0 ]; then
fi
success "All ${#required_assets[@]} required release assets present"
for installer in install.sh install-docker.sh install-mcp.sh install-mcp.ps1 install.ps1 pulse-auto-update.sh; do
manifest_matches="$(awk -v name="${installer}" '$2 == name && NF == 2 { count++ } END { print count + 0 }' checksums.txt)"
if [ "${manifest_matches}" -ne 1 ]; then
error "checksums.txt must contain exactly one entry for release installer ${installer}"
exit 1
fi
done
success "All published release installers are uniquely listed in checksums.txt"
# Validate published install.sh is the Pulse SERVER installer.
# Across v6 rc.1 → rc.5 the rendered AGENT installer was published here by
# mistake, silently breaking the LXC quickstart, the in-product Update Pulse