First commit
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const COMMON_PASSWORDS = new Set([
|
||||
"password",
|
||||
"password123",
|
||||
"qwerty",
|
||||
"letmein",
|
||||
"welcome",
|
||||
"admin",
|
||||
"iloveyou",
|
||||
"monkey",
|
||||
"dragon",
|
||||
"abc123",
|
||||
]);
|
||||
|
||||
const DISPOSABLE_EMAIL_DOMAINS = new Set([
|
||||
"10minutemail.com",
|
||||
"dispostable.com",
|
||||
"emailondeck.com",
|
||||
"fakemail.net",
|
||||
"getnada.com",
|
||||
"guerrillamail.com",
|
||||
"maildrop.cc",
|
||||
"mailinator.com",
|
||||
"sharklasers.com",
|
||||
"temp-mail.org",
|
||||
"tempmail.com",
|
||||
"throwawaymail.com",
|
||||
"trashmail.com",
|
||||
"yopmail.com",
|
||||
]);
|
||||
|
||||
function hasSequentialCharacters(value) {
|
||||
const normalizedValue = value.toLowerCase();
|
||||
|
||||
for (let index = 0; index <= normalizedValue.length - 4; index += 1) {
|
||||
const characters = normalizedValue.slice(index, index + 4);
|
||||
const codes = [...characters].map((character) => character.charCodeAt(0));
|
||||
const ascending = codes.every((code, characterIndex) => characterIndex === 0 || code === codes[characterIndex - 1] + 1);
|
||||
const descending = codes.every((code, characterIndex) => characterIndex === 0 || code === codes[characterIndex - 1] - 1);
|
||||
|
||||
if ((ascending || descending) && /^[a-z0-9]+$/.test(characters)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getEmailParts(email) {
|
||||
const atIndex = email.lastIndexOf("@");
|
||||
return {
|
||||
localPart: email.slice(0, atIndex),
|
||||
domain: email.slice(atIndex + 1),
|
||||
};
|
||||
}
|
||||
|
||||
function isDisposableEmail(email) {
|
||||
const { domain } = getEmailParts(email);
|
||||
return [...DISPOSABLE_EMAIL_DOMAINS].some(
|
||||
(disposableDomain) => domain === disposableDomain || domain.endsWith(`.${disposableDomain}`),
|
||||
);
|
||||
}
|
||||
|
||||
export function validateRegistration({ name, username, email, password }) {
|
||||
const trimmedName = name.trim();
|
||||
const trimmedUsername = username.trim();
|
||||
const normalizedEmail = email.trim().toLowerCase();
|
||||
|
||||
if (trimmedName.length < 1 || trimmedName.length > 100) {
|
||||
return "Name must be between 1 and 100 characters.";
|
||||
}
|
||||
|
||||
if (trimmedUsername.length < 1 || trimmedUsername.length > 50 || /\s/.test(trimmedUsername)) {
|
||||
return "Username must be between 1 and 50 characters and cannot contain whitespace.";
|
||||
}
|
||||
|
||||
if (
|
||||
normalizedEmail.length < 1 ||
|
||||
normalizedEmail.length > 254 ||
|
||||
/\s/.test(normalizedEmail) ||
|
||||
!EMAIL_PATTERN.test(normalizedEmail)
|
||||
) {
|
||||
return "Enter a valid email address without whitespace.";
|
||||
}
|
||||
|
||||
if (password.length < 1 || password.length > 128) {
|
||||
return "Password must be between 1 and 128 characters.";
|
||||
}
|
||||
|
||||
if (password.length < 12) {
|
||||
return "Password must be at least 12 characters long.";
|
||||
}
|
||||
|
||||
if (!/[a-z]/.test(password) || !/[A-Z]/.test(password) || !/\d/.test(password) || !/[^A-Za-z0-9\s]/.test(password)) {
|
||||
return "Password must contain a lowercase letter, uppercase letter, number, and special character.";
|
||||
}
|
||||
|
||||
const passwordForComparison = password.toLowerCase();
|
||||
const { localPart } = getEmailParts(normalizedEmail);
|
||||
|
||||
if (passwordForComparison.includes(trimmedUsername.toLowerCase()) || passwordForComparison.includes(localPart.toLowerCase())) {
|
||||
return "Password cannot contain your username or email address.";
|
||||
}
|
||||
|
||||
if (COMMON_PASSWORDS.has(passwordForComparison)) {
|
||||
return "Choose a less common password.";
|
||||
}
|
||||
|
||||
if (/(.)\1\1/.test(password) || hasSequentialCharacters(password)) {
|
||||
return "Password cannot contain repeated or sequential character patterns.";
|
||||
}
|
||||
|
||||
if (isDisposableEmail(normalizedEmail)) {
|
||||
return "Disposable or temporary email addresses are not allowed.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function validateLogin({ email, password }) {
|
||||
const normalizedEmail = email.trim().toLowerCase();
|
||||
|
||||
if (normalizedEmail.length < 1 || normalizedEmail.length > 254 || /\s/.test(normalizedEmail) || !EMAIL_PATTERN.test(normalizedEmail)) {
|
||||
return "Enter a valid email address without whitespace.";
|
||||
}
|
||||
|
||||
if (password.length < 1 || password.length > 128) {
|
||||
return "Password must be between 1 and 128 characters.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeRegistrationData({ name, username, email, password }) {
|
||||
return {
|
||||
name: name.trim(),
|
||||
username: username.trim(),
|
||||
email: email.trim().toLowerCase(),
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeLoginData({ email, password }) {
|
||||
return {
|
||||
email: email.trim().toLowerCase(),
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
export function isControlCharacterPresent(value) {
|
||||
return [...value].some((character) => {
|
||||
const code = character.charCodeAt(0);
|
||||
return code <= 0x1f || code === 0x7f;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user