Import source: app/backend/src/lib/password-strength
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
const COMMON_PASSWORDS = new Set([
|
||||||
|
'password', '12345678', 'qwerty12', 'abc12345', 'password1',
|
||||||
|
'123456789', '1234567890', 'iloveyou', 'sunshine1', 'princess',
|
||||||
|
'football', 'charlie1', 'access12', 'master12', 'michael1',
|
||||||
|
'shadow12', 'monkey12', ' letmein1', 'dragon12', 'trustno1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export interface PasswordStrength {
|
||||||
|
score: number;
|
||||||
|
issues: string[];
|
||||||
|
entropy: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validatePasswordStrength(password: string): PasswordStrength {
|
||||||
|
const issues: string[] = [];
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
if (password.length >= 8) score += 1;
|
||||||
|
if (password.length >= 12) score += 1;
|
||||||
|
if (password.length >= 16) score += 1;
|
||||||
|
|
||||||
|
const hasLower = /[a-z]/.test(password);
|
||||||
|
const hasUpper = /[A-Z]/.test(password);
|
||||||
|
const hasDigit = /[0-9]/.test(password);
|
||||||
|
const hasSpecial = /[^a-zA-Z0-9]/.test(password);
|
||||||
|
const hasPersian = /[\u0600-\u06FF]/.test(password);
|
||||||
|
|
||||||
|
const charsetSize =
|
||||||
|
(hasLower ? 26 : 0) +
|
||||||
|
(hasUpper ? 26 : 0) +
|
||||||
|
(hasDigit ? 10 : 0) +
|
||||||
|
(hasSpecial ? 33 : 0) +
|
||||||
|
(hasPersian ? 33 : 0);
|
||||||
|
|
||||||
|
const entropy = Math.log2(Math.max(charsetSize, 1)) * password.length;
|
||||||
|
|
||||||
|
if (hasLower) score += 1;
|
||||||
|
if (hasUpper) score += 1;
|
||||||
|
if (hasDigit) score += 1;
|
||||||
|
if (hasSpecial) score += 1;
|
||||||
|
|
||||||
|
const repeats = /(.)\1{2,}/.test(password);
|
||||||
|
const sequential = /(?:abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz|012|123|234|345|456|567|678|789)/i.test(password);
|
||||||
|
const keyboardRows = /(?:qwerty|asdf|zxcv|qaz|wsx|edc)/i.test(password);
|
||||||
|
|
||||||
|
if (repeats) { score -= 1; issues.push('repeating_characters'); }
|
||||||
|
if (sequential) { score -= 1; issues.push('sequential_characters'); }
|
||||||
|
if (keyboardRows) { score -= 1; issues.push('keyboard_pattern'); }
|
||||||
|
|
||||||
|
if (charsetSize < 2) issues.push('needs_variety');
|
||||||
|
if (entropy < 30) issues.push('low_entropy');
|
||||||
|
if (COMMON_PASSWORDS.has(password.toLowerCase())) issues.push('common_password');
|
||||||
|
|
||||||
|
if (password.length < 8) issues.push('too_short');
|
||||||
|
if (password.length > 256) issues.push('too_long');
|
||||||
|
|
||||||
|
return {
|
||||||
|
score: Math.max(0, Math.min(10, score)),
|
||||||
|
issues,
|
||||||
|
entropy: Math.round(entropy * 10) / 10,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPasswordAcceptable(password: string): { ok: boolean; issues: string[] } {
|
||||||
|
const result = validatePasswordStrength(password);
|
||||||
|
const ok = result.issues.length === 0 && result.score >= 4;
|
||||||
|
return { ok, issues: result.issues };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user