Created
July 1, 2020 18:27
-
-
Save jamiebuilds/3aa8e0ca21da63b8845e588f27fdc2a0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function validatePassword(password: string): string[] { | |
let errors = [] | |
// 1. Don't regex for things you can trivially express in code | |
// ----------------------------------------------------------- | |
// For example, we could have written this as `/^.{0,7}$/` but that's not | |
// nearly as clear as checking the length of the string. | |
if (password.length < 8) { | |
errors.push("Password must be at least 8 characters long") | |
} | |
// 2. Split your regex into smaller and more easily understood chunks whereever possible | |
// ------------------------------------------------------------------------------------- | |
// For example, we could have written this as `/(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])/` | |
// but we've only made the problem more complex for ourselves by trying to do everything | |
// at once. | |
if (/[a-z]/.test(password)) { errors.push("Password must contain at least one lowercase letter") } | |
if (/[A-Z]/.test(password)) { errors.push("Password must contain at least one uppercase letter") } | |
if (/[0-9]/.test(password)) { errors.push("Password must contain at least one number") } | |
// 3. Move logical operators out of regex whereever possible | |
// --------------------------------------------------------- | |
// For example, we could have written this as `/^\s|\s$/` but again, regexes are | |
// way easier to understand when they are broken down into smaller chunks that accomplish | |
// one things. | |
if (/^\s/.test(password) || /\s$/.test(password)) { | |
errors.push("Password must not start or end with a whitespace character") | |
} | |
// 4. Learn how to selectively apply the more complex regex operators | |
// ------------------------------------------------------------------ | |
// For example, we could have written something like `/[!@#$%^&*]/` but we definitely | |
// can't list off every possible special character. So instead we're testing the string | |
// contains at least one character that is something _other_ than a-z, A-Z, or 0-9. | |
// Explainer: | |
// Match a character `[^except]` `a-z`, `A-Z`, or `0-9` | |
if (/[^a-zA-Z0-9]/.test(password)) { | |
errors.push("Password must contain at least one special character") | |
} | |
// 5. When using complex regex operators, be sure to keep them isolated and well explained | |
// --------------------------------------------------------------------------------------- | |
// For example, a couple more complex regex operators can replace a fair bit of stateful code | |
// logic here. If these complex operators were part of a massive regex, it would make for a | |
// very hard to read regex. But keeping it small allows you to document what is happening and | |
// keeps it easy to understand. | |
// Explainer: | |
// Match any `(.)` character, then check if that character `\1` is repeated 3 more times `{3,}` | |
if (/(.)\1{3,}/.test(password)) { | |
errors.push("Password must not repeat the same character in a row 4 or more times") | |
} | |
// 6. Regular Expressions can't solve everything, don't use it in place of what's most important | |
// --------------------------------------------------------------------------------------------- | |
// For example, we've written all of the above checks to make sure the user isn't | |
// shooting themselves in the foot with their password. But that's in service of | |
// the bigger problem that we want to make sure someone isn't going to break into | |
// their account. Regex alone is not enough to solve this problem. | |
if (await checkHaveIBeenPwned(password)) { | |
errors.push("Password has been previously exposed by data breach according to haveibeenpwned.com") | |
} | |
return errors | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment