fix: use crypto.getRandomValues() for password generation

Math.random() is not cryptographically secure. Replace with
crypto.getRandomValues() in both generatePassword() functions
to resolve CodeQL js/insecure-randomness alerts.
This commit is contained in:
SnapOtter
2026-05-20 16:04:57 +08:00
parent 8706d8555d
commit 59bbe2b5e0
2 changed files with 22 additions and 12 deletions
@@ -751,20 +751,26 @@ function SecuritySection() {
/* ────────────────────── People ────────────────────── */
function secureRandom(max: number): number {
const array = new Uint32Array(1);
crypto.getRandomValues(array);
return array[0] % max;
}
function generatePassword(): string {
const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const lower = "abcdefghijklmnopqrstuvwxyz";
const digits = "0123456789";
const all = upper + lower + digits;
const required = [
upper[Math.floor(Math.random() * upper.length)],
lower[Math.floor(Math.random() * lower.length)],
digits[Math.floor(Math.random() * digits.length)],
upper[secureRandom(upper.length)],
lower[secureRandom(lower.length)],
digits[secureRandom(digits.length)],
];
const rest = Array.from({ length: 13 }, () => all[Math.floor(Math.random() * all.length)]);
const rest = Array.from({ length: 13 }, () => all[secureRandom(all.length)]);
const chars = [...required, ...rest];
for (let i = chars.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const j = secureRandom(i + 1);
[chars[i], chars[j]] = [chars[j], chars[i]];
}
return chars.join("");
+11 -7
View File
@@ -41,22 +41,26 @@ function triggerBrowserPasswordSave(username: string, password: string) {
// The form.submit() causes a full page navigation to "/", so no cleanup needed.
}
function secureRandom(max: number): number {
const array = new Uint32Array(1);
crypto.getRandomValues(array);
return array[0] % max;
}
function generatePassword(): string {
const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const lower = "abcdefghijklmnopqrstuvwxyz";
const digits = "0123456789";
const all = upper + lower + digits;
// Guarantee at least one of each required character class
const required = [
upper[Math.floor(Math.random() * upper.length)],
lower[Math.floor(Math.random() * lower.length)],
digits[Math.floor(Math.random() * digits.length)],
upper[secureRandom(upper.length)],
lower[secureRandom(lower.length)],
digits[secureRandom(digits.length)],
];
const rest = Array.from({ length: 13 }, () => all[Math.floor(Math.random() * all.length)]);
// Shuffle so the required chars aren't always at the start
const rest = Array.from({ length: 13 }, () => all[secureRandom(all.length)]);
const chars = [...required, ...rest];
for (let i = chars.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const j = secureRandom(i + 1);
[chars[i], chars[j]] = [chars[j], chars[i]];
}
return chars.join("");