Skip to content

Commit a490e37

Browse files
authored
fix(release): sign updater compatibility metadata without a trailing newline (#40)
## Summary **Category:** fix **User Impact:** Future desktop releases can be installed from older Berd versions without a false compatibility-signature failure. **Problem:** The release script signed compact compatibility JSON with a trailing newline, while the updater verifies the same JSON serialized without that newline. The one-byte mismatch caused valid release metadata to fail closed during update checks. **Solution:** Emit raw compact JSON with `jq -cjn` so the producer signs the updater's canonical bytes, and cover the exact payload passed to the signer with a regression test. <details> <summary>File changes</summary> **scripts/release/sign-compatibility-descriptor.sh** Emit the compatibility descriptor without a trailing newline so its signed bytes match updater verification. **scripts/release/tests/release-scripts.test.mjs** Capture the signer's input and assert it is the exact compact, newline-free canonical payload expected by the updater. </details> ### Related issue None found. ### Testing - `pnpm test:release-scripts` - `just ci`
1 parent 6c71c55 commit a490e37

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

scripts/release/sign-compatibility-descriptor.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fi
2222
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/berd-compatibility.XXXXXX")"
2323
trap 'rm -rf "$WORK_DIR"' EXIT
2424
PAYLOAD="$WORK_DIR/compatibility.json"
25-
jq -cn \
25+
jq -cjn \
2626
--arg channelId "$CHANNEL_ID" \
2727
--arg version "$VERSION" \
2828
--arg artifactSha256 "$(printf '%s' "$ARTIFACT_SHA256" | tr '[:upper:]' '[:lower:]')" \

scripts/release/tests/release-scripts.test.mjs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,59 @@ describe("desktop release workflow platform gate", () => {
976976
});
977977
});
978978

979+
describe("sign-compatibility-descriptor", () => {
980+
it("signs the updater's exact newline-free canonical payload", async () => {
981+
const dir = await tempDir();
982+
const fakeBin = join(dir, "bin");
983+
const capturedPayload = join(dir, "compatibility.json");
984+
const artifactSha256 = "AB".repeat(32);
985+
await mkdir(fakeBin);
986+
await writeFile(
987+
join(fakeBin, "pnpm"),
988+
`#!/usr/bin/env bash
989+
set -euo pipefail
990+
[[ "$*" == "exec tauri signer sign "* ]]
991+
payload="\${@: -1}"
992+
cp "$payload" "$CAPTURED_PAYLOAD"
993+
printf 'fake-signature\r\n' > "$payload.sig"
994+
`,
995+
{ mode: 0o755 },
996+
);
997+
998+
const result = run(
999+
"scripts/release/sign-compatibility-descriptor.sh",
1000+
["1.2.3-rc.4", "main", artifactSha256],
1001+
{
1002+
PATH: `${fakeBin}:${process.env.PATH}`,
1003+
CAPTURED_PAYLOAD: capturedPayload,
1004+
TAURI_SIGNING_PRIVATE_KEY: "test-key",
1005+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: "test-password",
1006+
BERD_STORE_CONTRACT_VERSION: "1",
1007+
BERD_WRITES_DATA_EPOCH: "2",
1008+
BERD_MIN_READABLE_DATA_EPOCH: "1",
1009+
BERD_MAX_READABLE_DATA_EPOCH: "3",
1010+
},
1011+
);
1012+
1013+
expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0);
1014+
expect(result.stdout).toBe("fake-signature");
1015+
expect(await readFile(capturedPayload, "utf8")).toBe(
1016+
JSON.stringify({
1017+
schemaVersion: 1,
1018+
channelId: "main",
1019+
version: "1.2.3-rc.4",
1020+
artifactSha256: artifactSha256.toLowerCase(),
1021+
compatibility: {
1022+
storeContractVersion: 1,
1023+
writesDataEpoch: 2,
1024+
minReadableDataEpoch: 1,
1025+
maxReadableDataEpoch: 3,
1026+
},
1027+
}),
1028+
);
1029+
});
1030+
});
1031+
9791032
describe("package-signed-updater", () => {
9801033
it("uses the version/platform-qualified filename and keeps Berd.app at archive root", async () => {
9811034
const dir = await tempDir();

0 commit comments

Comments
 (0)