From ceff7054b167b14cb4e0e1b69e5934dc24fddace Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Tue, 7 Jul 2026 07:42:12 -0300 Subject: [PATCH] fix(security): unbiased crypto digits for the doubao synthetic device id (CodeQL js/biased-cryptographic-random) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit randomNumericId builds a non-secret synthetic device/web id. The v3.8.45 switch to crypto.getRandomValues closed js/insecure-randomness but 'cryptoByte % 10' is biased (256 is not a multiple of 10), tripping js/biased-cryptographic-random. Draw each digit by rejection sampling — discard bytes in the biased tail so the remaining range divides evenly — giving a uniform distribution (verified) while staying crypto-backed. --- open-sse/executors/doubao-web.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/open-sse/executors/doubao-web.ts b/open-sse/executors/doubao-web.ts index c781834042..50449a160a 100644 --- a/open-sse/executors/doubao-web.ts +++ b/open-sse/executors/doubao-web.ts @@ -48,11 +48,23 @@ function parseJsonRecord(raw: string): JsonRecord | null { } function randomNumericId(length = 19): string { - // crypto-backed (CodeQL js/insecure-randomness): a synthetic device/web id, - // not a secret — but crypto.getRandomValues costs the same and closes the alert. - const bytes = globalThis.crypto.getRandomValues(new Uint8Array(length)); - let id = String((bytes[0] % 9) + 1); - for (let i = 1; i < length; i += 1) id += String(bytes[i] % 10); + // crypto-backed, UNBIASED digits (CodeQL js/insecure-randomness + + // js/biased-cryptographic-random): a synthetic device/web id, not a secret. + // A raw `cryptoByte % 10` is biased (256 is not a multiple of 10), so each + // digit is drawn by rejection sampling — discard bytes in the biased tail so + // the remaining range divides evenly, then reduce. + const digit = (max: number): number => { + const limit = 256 - (256 % max); // largest multiple of `max` that fits in a byte + const buf = new Uint8Array(1); + let b: number; + do { + globalThis.crypto.getRandomValues(buf); + b = buf[0]; + } while (b >= limit); + return b % max; + }; + let id = String(digit(9) + 1); + for (let i = 1; i < length; i += 1) id += String(digit(10)); return id; }