From 595f9e04919b4481da0b5bbed735efb3abc7273c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 08:10:45 +0000 Subject: [PATCH 1/2] Keep seeded PRNG output strictly below 1 makeRng's rand() divided by 0x7fffffff (2^31-1), returning exactly 1 when seed === 0x7fffffff, which would let pick index past the array and randInt return hi + 1. Divide by 0x80000000 (2^31) so the result is always in [0, 1). Removes the corresponding follow-up from TODO.md. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RJV4Bsf2owkANcSfn5sZgm --- TODO.md | 12 ------------ test/lib/checkout-pricing-consistency.test.ts | 5 ++++- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index 31af3a8763..0592e157c8 100644 --- a/TODO.md +++ b/TODO.md @@ -509,15 +509,3 @@ Fix direction: in `applyArg`, when `VALUE_FLAGS[arg]` exists but `next` is `undefined`, raise a clear "missing value for " usage error rather than pushing the flag to `positional`; keep consuming/returning true when a value is present. - -### 5. Seeded PRNG `rand()` can return exactly 1 - -`test/lib/checkout-pricing-consistency.test.ts` — `makeRng`'s `rand` returns -`seed / 0x7fffffff`, which is `1` when `seed === 0x7fffffff`. That lets `pick` -index one past the array and `randInt` return `hi + 1`. Astronomically unlikely -for the fixed seeds in use (and identical to `main`), but incorrect. - -Fix direction: normalize with `seed / 0x80000000` (or clamp below 1). Note this -changes the seeded sequence, so re-baseline any hardcoded expectations and -confirm the property test still passes; best landed on its own so the corpus -shift is reviewed deliberately. diff --git a/test/lib/checkout-pricing-consistency.test.ts b/test/lib/checkout-pricing-consistency.test.ts index 4f94e0bfc1..b088186405 100644 --- a/test/lib/checkout-pricing-consistency.test.ts +++ b/test/lib/checkout-pricing-consistency.test.ts @@ -118,7 +118,10 @@ const makeRng = (initialSeed: number): Rng => { let seed = initialSeed; const rand = () => { seed = (seed * 1103515245 + 12345) & 0x7fffffff; - return seed / 0x7fffffff; + // Divide by 2^31 (not 2^31-1) so the result is always in [0, 1): at + // seed === 0x7fffffff the old divisor returned exactly 1, letting `pick` + // index past the array and `randInt` return hi + 1. + return seed / 0x80000000; }; const pick = (xs: readonly T[]): T => xs[Math.floor(rand() * xs.length)]!; const randInt = (lo: number, hi: number) => From 7b45d35f40432a31bb5dba3508ca5566a55791f6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 08:13:27 +0000 Subject: [PATCH 2/2] Apply the same PRNG strictly-below-1 fix to payment-signature test The identical LCG in payment-signature.test.ts also divided by 0x7fffffff, so its rand() could return exactly 1. Divide by 0x80000000 for consistency. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RJV4Bsf2owkANcSfn5sZgm --- test/shared/payment-signature.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/shared/payment-signature.test.ts b/test/shared/payment-signature.test.ts index 75536b0d43..1fc69db78e 100644 --- a/test/shared/payment-signature.test.ts +++ b/test/shared/payment-signature.test.ts @@ -58,7 +58,8 @@ const excludedKeys: [string, Record][] = [ /** Tiny deterministic PRNG so the fuzz loop is repeatable. */ const lcg = (seed: number) => () => { seed = (seed * 1103515245 + 12345) & 0x7fffffff; - return seed / 0x7fffffff; + // Divide by 2^31 (not 2^31-1) so the result stays in [0, 1). + return seed / 0x80000000; }; describeWithEnv("payment price signature", { encryptionKey: true }, () => {