fix: resolve CodeQL biased crypto random warning

Use unsigned right shift (>>>) to properly handle 32-bit random values and eliminate statistical bias.
This commit is contained in:
lockbitchat
2025-10-20 00:51:03 -04:00
parent b18e943abd
commit 63a19e6a4c
3 changed files with 78 additions and 39 deletions

50
dist/app-boot.js vendored
View File

@@ -8200,21 +8200,41 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
} }
const range = max - min + 1; const range = max - min + 1;
if (range <= 0) throw new Error("Invalid range"); if (range <= 0) throw new Error("Invalid range");
const bytesNeeded = Math.ceil(Math.log2(range) / 8); if (range <= 256) {
const maxValue = Math.pow(256, bytesNeeded); const threshold2 = 256 - 256 % range;
const threshold = maxValue - maxValue % range; let randomValue2;
do {
randomValue2 = crypto.getRandomValues(new Uint8Array(1))[0];
} while (randomValue2 >= threshold2);
return min + randomValue2 % range;
}
if (range <= 65536) {
const maxValue = 65536;
const threshold2 = maxValue - maxValue % range;
let randomValue2;
do {
const randomBytes = crypto.getRandomValues(new Uint8Array(2));
randomValue2 = randomBytes[0] << 8 | randomBytes[1];
} while (randomValue2 >= threshold2);
return min + randomValue2 % range;
}
const maxUint32 = 4294967296;
const threshold = maxUint32 - maxUint32 % range;
let randomValue; let randomValue;
do { do {
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded)); const randomBytes = crypto.getRandomValues(new Uint8Array(4));
randomValue = 0; randomValue = (randomBytes[0] << 24 | randomBytes[1] << 16 | randomBytes[2] << 8 | randomBytes[3]) >>> 0;
for (let i = 0; i < bytesNeeded; i++) {
randomValue = randomValue << 8 | randomBytes[i];
}
} while (randomValue >= threshold); } while (randomValue >= threshold);
return min + randomValue % range; return min + randomValue % range;
} }
// Safe helper: unbiased float in [minFloat, maxFloat] with scale // Safe helper: unbiased float in [minFloat, maxFloat] with scale
getSafeRandomFloat(minFloat, maxFloat, scale = 100) { getSafeRandomFloat(minFloat, maxFloat, scale = 1e3) {
if (typeof minFloat !== "number" || typeof maxFloat !== "number") {
throw new Error("getSafeRandomFloat requires numeric min and max");
}
if (minFloat >= maxFloat) {
throw new Error("minFloat must be less than maxFloat");
}
const minInt = Math.round(minFloat * scale); const minInt = Math.round(minFloat * scale);
const maxInt = Math.round(maxFloat * scale); const maxInt = Math.round(maxFloat * scale);
const intVal = this.getSafeRandomInt(minInt, maxInt); const intVal = this.getSafeRandomInt(minInt, maxInt);
@@ -8222,12 +8242,10 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
} }
// Generate fingerprint mask // Generate fingerprint mask
generateFingerprintMask() { generateFingerprintMask() {
const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
const mask = { const mask = {
timingOffset: this.getSafeRandomInt(0, 1500), timingOffset: this.getSafeRandomInt(0, 1500),
// 01500ms sizeVariation: this.getSafeRandomFloat(0.75, 1.25, 1e3),
sizeVariation: this.getSafeRandomFloat(0.75, 1.25), // изменен scale
// unbiased float
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
headerVariations: [ headerVariations: [
"X-Client-Version", "X-Client-Version",
@@ -8243,11 +8261,9 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
"X-Private" "X-Private"
], ],
noiseIntensity: this.getSafeRandomInt(50, 150), noiseIntensity: this.getSafeRandomInt(50, 150),
// 50150% sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25, 1e3),
sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25), // изменен scale
// unbiased float
timingVariation: this.getSafeRandomInt(100, 1100) timingVariation: this.getSafeRandomInt(100, 1100)
// 1001100ms
}; };
return mask; return mask;
} }

File diff suppressed because one or more lines are too long

View File

@@ -4489,25 +4489,50 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
const range = max - min + 1; const range = max - min + 1;
if (range <= 0) throw new Error('Invalid range'); if (range <= 0) throw new Error('Invalid range');
// Для больших диапазонов используем несколько байт if (range <= 256) {
const bytesNeeded = Math.ceil(Math.log2(range) / 8); const threshold = 256 - (256 % range);
const maxValue = Math.pow(256, bytesNeeded); let randomValue;
do {
randomValue = crypto.getRandomValues(new Uint8Array(1))[0];
} while (randomValue >= threshold);
return min + (randomValue % range);
}
if (range <= 65536) {
const maxValue = 65536;
const threshold = maxValue - (maxValue % range); const threshold = maxValue - (maxValue % range);
let randomValue;
do {
const randomBytes = crypto.getRandomValues(new Uint8Array(2));
randomValue = (randomBytes[0] << 8) | randomBytes[1];
} while (randomValue >= threshold);
return min + (randomValue % range);
}
const maxUint32 = 0x100000000; // 2^32
const threshold = maxUint32 - (maxUint32 % range);
let randomValue; let randomValue;
do { do {
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded)); const randomBytes = crypto.getRandomValues(new Uint8Array(4));
randomValue = 0; randomValue = ((randomBytes[0] << 24) |
for (let i = 0; i < bytesNeeded; i++) { (randomBytes[1] << 16) |
randomValue = (randomValue << 8) | randomBytes[i]; (randomBytes[2] << 8) |
} randomBytes[3]) >>> 0;
} while (randomValue >= threshold); } while (randomValue >= threshold);
return min + (randomValue % range); return min + (randomValue % range);
} }
// Safe helper: unbiased float in [minFloat, maxFloat] with scale // Safe helper: unbiased float in [minFloat, maxFloat] with scale
getSafeRandomFloat(minFloat, maxFloat, scale = 100) { getSafeRandomFloat(minFloat, maxFloat, scale = 1000) {
if (typeof minFloat !== 'number' || typeof maxFloat !== 'number') {
throw new Error('getSafeRandomFloat requires numeric min and max');
}
if (minFloat >= maxFloat) {
throw new Error('minFloat must be less than maxFloat');
}
const minInt = Math.round(minFloat * scale); const minInt = Math.round(minFloat * scale);
const maxInt = Math.round(maxFloat * scale); const maxInt = Math.round(maxFloat * scale);
const intVal = this.getSafeRandomInt(minInt, maxInt); const intVal = this.getSafeRandomInt(minInt, maxInt);
@@ -4516,19 +4541,17 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Generate fingerprint mask // Generate fingerprint mask
generateFingerprintMask() { generateFingerprintMask() {
const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
const mask = { const mask = {
timingOffset: this.getSafeRandomInt(0, 1500), // 01500ms timingOffset: this.getSafeRandomInt(0, 1500),
sizeVariation: this.getSafeRandomFloat(0.75, 1.25), // unbiased float sizeVariation: this.getSafeRandomFloat(0.75, 1.25, 1000), // изменен scale
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
headerVariations: [ headerVariations: [
'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature', 'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature',
'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private' 'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private'
], ],
noiseIntensity: this.getSafeRandomInt(50, 150), // 50150% noiseIntensity: this.getSafeRandomInt(50, 150),
sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25), // unbiased float sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25, 1000), // изменен scale
timingVariation: this.getSafeRandomInt(100, 1100) // 1001100ms timingVariation: this.getSafeRandomInt(100, 1100)
}; };
return mask; return mask;
} }