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:
50
dist/app-boot.js
vendored
50
dist/app-boot.js
vendored
@@ -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),
|
||||||
// 0–1500ms
|
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),
|
||||||
// 50–150%
|
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)
|
||||||
// 100–1100ms
|
|
||||||
};
|
};
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|||||||
6
dist/app-boot.js.map
vendored
6
dist/app-boot.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -4482,32 +4482,57 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function: unbiased integer in [min, max]
|
// Helper function: unbiased integer in [min, max]
|
||||||
getSafeRandomInt(min, max) {
|
getSafeRandomInt(min, max) {
|
||||||
if (!Number.isInteger(min) || !Number.isInteger(max)) {
|
if (!Number.isInteger(min) || !Number.isInteger(max)) {
|
||||||
throw new Error('getSafeRandomInt requires integer min and max');
|
throw new Error('getSafeRandomInt requires integer min and max');
|
||||||
}
|
}
|
||||||
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;
|
||||||
const threshold = maxValue - (maxValue % range);
|
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);
|
||||||
|
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), // 0–1500ms
|
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), // 50–150%
|
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) // 100–1100ms
|
timingVariation: this.getSafeRandomInt(100, 1100)
|
||||||
};
|
};
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user