2024-08-07 12:22:36 +00:00
|
|
|
import commonPasswords from '../data/commonPasswords';
|
|
|
|
import Trie from './Trie';
|
|
|
|
|
|
|
|
const checkStrength = (pass: string) => {
|
|
|
|
const score = scorePassword(pass);
|
2025-01-08 10:45:03 +00:00
|
|
|
const scoreLabel = mapScoreToLabel(score);
|
|
|
|
const hints = getImprovementHints(pass);
|
2024-08-07 12:22:36 +00:00
|
|
|
return {
|
|
|
|
score,
|
2025-01-08 10:45:03 +00:00
|
|
|
scoreLabel,
|
|
|
|
hints,
|
|
|
|
};
|
2024-08-07 12:22:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default checkStrength;
|
|
|
|
|
|
|
|
// Function to score the password based on different criteria
|
|
|
|
const scorePassword = (password: string): number => {
|
2025-01-08 10:45:03 +00:00
|
|
|
if (password.length <= 8 || isCommonPassword(password)) return 0;
|
2024-08-07 12:22:36 +00:00
|
|
|
|
|
|
|
let score = 0;
|
2025-01-08 10:45:03 +00:00
|
|
|
score += getLengthScore(password); //3
|
|
|
|
score += getSpecialCharScore(password); //1
|
|
|
|
score += getCaseMixScore(password); //1
|
|
|
|
score += getNumberMixScore(password); //1
|
2024-08-07 12:22:36 +00:00
|
|
|
|
2025-01-08 10:45:03 +00:00
|
|
|
return Math.min(score, 6); // Maximum score is 6
|
2024-08-07 12:22:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Initialize the Trie with common passwords
|
|
|
|
const trie = new Trie();
|
2025-01-08 10:45:03 +00:00
|
|
|
commonPasswords.forEach((password) => trie.insert(password));
|
2024-08-07 12:22:36 +00:00
|
|
|
const isCommonPassword = (password: string): boolean => {
|
|
|
|
// return commonPasswords.includes(password);
|
|
|
|
return trie.search(password);
|
|
|
|
};
|
|
|
|
|
2025-01-08 10:45:03 +00:00
|
|
|
// Length-based scoring
|
2024-08-07 12:22:36 +00:00
|
|
|
const getLengthScore = (password: string): number => {
|
|
|
|
if (password.length > 20 && !hasRepeatChars(password)) return 3;
|
|
|
|
if (password.length > 12 && !hasRepeatChars(password)) return 2;
|
|
|
|
if (password.length > 8) return 1;
|
|
|
|
return 0;
|
|
|
|
};
|
2025-01-08 10:45:03 +00:00
|
|
|
|
|
|
|
// const hasRepeatChars = (password: string): boolean => {
|
|
|
|
// const repeatCharRegex = /(\w)(\1+\1+\1+\1+)/g;
|
|
|
|
// return repeatCharRegex.test(password);
|
|
|
|
// };
|
|
|
|
// Check for repeated characters
|
|
|
|
const hasRepeatChars = (password: string): boolean => /(\w)\1{3,}/.test(password);
|
2024-08-07 12:22:36 +00:00
|
|
|
|
|
|
|
// Function to get the score based on the presence of special characters
|
|
|
|
const getSpecialCharScore = (password: string): number => {
|
|
|
|
const specialCharRegex = /[^A-Za-z0-9]/g;
|
|
|
|
return specialCharRegex.test(password) ? 1 : 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Function to get the score based on the mix of uppercase and lowercase letters
|
|
|
|
const getCaseMixScore = (password: string): number => {
|
|
|
|
const hasUpperCase = /[A-Z]/.test(password);
|
|
|
|
const hasLowerCase = /[a-z]/.test(password);
|
|
|
|
return hasUpperCase && hasLowerCase ? 1 : 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Function to get the score based on the mix of letters and numbers
|
|
|
|
const getNumberMixScore = (password: string): number => {
|
|
|
|
const hasLetter = /[A-Za-z]/.test(password);
|
|
|
|
const hasNumber = /[0-9]/.test(password);
|
|
|
|
return hasLetter && hasNumber ? 1 : 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Function to map the score to a corresponding label
|
|
|
|
const mapScoreToLabel = (score: number): string => {
|
2025-01-08 10:45:03 +00:00
|
|
|
const labels = ['risky', 'guessable', 'weak', 'safe', 'secure', 'safe-secure', 'optimal'];
|
2024-08-07 12:22:36 +00:00
|
|
|
return labels[score] || '';
|
|
|
|
};
|
|
|
|
|
2025-01-08 10:45:03 +00:00
|
|
|
// Function to get improvement hints
|
|
|
|
const getImprovementHints = (password: string): string[] => {
|
|
|
|
const hints = [];
|
|
|
|
|
|
|
|
if (password.length <= 8) {
|
|
|
|
hints.push('Increase your password length to more than 8 characters for 1 scoring point.');
|
|
|
|
} else {
|
|
|
|
if (password.length > 8 && password.length <= 12) {
|
|
|
|
hints.push('Increase your password length to more than 12 characters for additional scoring point.');
|
|
|
|
} else if (password.length > 12 && password.length <= 20) {
|
|
|
|
hints.push('Increase your password length to more than 20 characters for additional scoring point.');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for special character score
|
|
|
|
if (!getSpecialCharScore(password)) {
|
|
|
|
hints.push('Include at least one special character for 1 point.');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for case mix score
|
|
|
|
if (!getCaseMixScore(password)) {
|
|
|
|
hints.push('Mix uppercase and lowercase letters for 1 point.');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for number mix score
|
|
|
|
if (!getNumberMixScore(password)) {
|
|
|
|
hints.push('Add numbers to your password for 1 point.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasRepeatChars(password) && password.length < 20) {
|
|
|
|
hints.push('Remove repeated characters for 1 point.');
|
|
|
|
}
|
|
|
|
if (hasRepeatChars(password) && password.length > 20) {
|
|
|
|
hints.push('Remove repeated characters for 2 points.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hints;
|
|
|
|
};
|