fix(release): reject unsafe Sparkle build floors

This commit is contained in:
Vincent Koc 2026-06-17 00:51:08 +02:00
parent 2b92fbc2ee
commit 8db66b416b
No known key found for this signature in database
2 changed files with 27 additions and 7 deletions

View file

@ -54,13 +54,13 @@ export function sparkleBuildFloorsFromShortVersion(
return null;
}
const year = Number.parseInt(match[1], 10);
const month = Number.parseInt(match[2], 10);
const patch = Number.parseInt(match[3], 10);
const year = Number(match[1]);
const month = Number(match[2]);
const patch = Number(match[3]);
if (
!Number.isInteger(year) ||
!Number.isInteger(month) ||
!Number.isInteger(patch) ||
!Number.isSafeInteger(year) ||
!Number.isSafeInteger(month) ||
!Number.isSafeInteger(patch) ||
month < 1 ||
month > 12 ||
patch < 1
@ -87,10 +87,14 @@ export function sparkleBuildFloorsFromShortVersion(
// Keep old appcast entries byte-stable, then switch to YYMMPPPPLL so
// monthly patches beyond 31 stay monotonic without pretending to be dates.
const releaseKey = monthlyPatchReleaseKey(year, month, patch);
const laneFloor = releaseKey + lane;
if (!isSafeSparkleFloor(releaseKey) || !isSafeSparkleFloor(laneFloor)) {
return null;
}
return {
releaseKey,
legacyFloor: releaseKey,
laneFloor: releaseKey + lane,
laneFloor,
lane,
};
}
@ -98,6 +102,13 @@ export function sparkleBuildFloorsFromShortVersion(
const releaseKey = legacyDateReleaseKey(year, month, patch);
const legacyFloor = Number(`${releaseKey}0`);
const laneFloor = Number(`${releaseKey}${String(lane).padStart(2, "0")}`);
if (
!isSafeSparkleFloor(releaseKey) ||
!isSafeSparkleFloor(legacyFloor) ||
!isSafeSparkleFloor(laneFloor)
) {
return null;
}
return { releaseKey, legacyFloor, laneFloor, lane };
}
@ -105,6 +116,10 @@ export function canonicalSparkleBuildFromVersion(shortVersion: string): number |
return sparkleBuildFloorsFromShortVersion(shortVersion)?.laneFloor ?? null;
}
function isSafeSparkleFloor(value: number): boolean {
return Number.isSafeInteger(value) && value > 0;
}
function runCli(args: string[]): number {
const [command, version] = args;
if (command !== "canonical-build" || !version) {

View file

@ -26,6 +26,11 @@ describe("canonicalSparkleBuildFromVersion", () => {
expect(canonicalSparkleBuildFromVersion("2026.6.5-beta.0")).toBeNull();
expect(canonicalSparkleBuildFromVersion("2026.6.5-beta.9007199254740993")).toBeNull();
});
it("rejects unsafe numeric release parts and build floors", () => {
expect(canonicalSparkleBuildFromVersion("2026.6.9007199254740993")).toBeNull();
expect(canonicalSparkleBuildFromVersion("2026.6.90071992547410")).toBeNull();
});
});
function parseItems(appcast: string): AppcastItem[] {