David Isla David Isla - 7 months ago 9
Javascript Question

Is there a way to merge these two regex?

I need check out a password string with these rules:


  • Only lower-case letters, upper-case letters, numbers and some special characters (see below) are allowed.

  • Minimun of 8 characters and maximum of 16.

  • Password must contains at least one character from three of the four following groups:


    • Lower-case letter

    • Upper-case letter

    • Numeric character

    • Special character (!@#$%&/=?_.,:;-)




For accomplish this goal, I made two validations. The easy one is the first step, check out the allowed characters and lenght:

^[a-zA-Z0-9!@\#$%&/=?_.,:;\-]{8,16}$


The second one is a little bit more complex, but I could resolve it thanks to this Stackoverflow answer:

^((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@\#$%&/=?_.,:;\-])|(?=.*[a-z])(?=.*[0-9])(?=.*[!@\#$%&/=?_.,:;\-])|(?=.*[A-Z])(?=.*[0-9])(?=.*[!@\#$%&/=?_.,:;\-])).{8,16}$


I think that probably, a 2 steps solution is clear and easy .. but... Is there a way to merge both regex?

I would like to apply the solution on Java, JavaScript and Lua.

Answer

Let me start out by emphasising that I dislike your password policy. Why do you ban other symbols, or passwords over 16 characters? Don't include those rules, if at all possible.

I've written this regex in extended form (i.e. ignores white-space), for clarity:

/\A 
  (?=.{8,16}\z)                     # Must contain 8-16 characters
  (?!.*[^a-zA-Z\d!@#$%&\/=?_.,:;-]) # Must not contain other characters
  (?=.*\d)                          # Must contain a digit
  (?=.*[a-z])                       # Must contain a lower case character
  (?=.*[A-Z])                       # Must contain an upper case character
  (?=.*[!@#$%&\/=?_.,:;-])          # Must contain a symbol from this list
/x

The above pattern solves the problem for enforcing all four of your rules. You could, in theory, wrap the last 4 conditions into four "or" groups - like your original example has done.

However, again, I would not recommend it. Your code would be much more legible, useful and maintainable if you do this as a series of separate checks inside a function. (For example, this means you can display more helpful error messages rather than just "regex failed!".

But since you asked.... Here is the regexp monster:

/\A 
  (?=.{8,16}\z)
  (?!.*[^a-zA-Z\d!@#$%&\/=?_.,:;-])
  (
    (?=.*\d)
    (?=.*[a-z])
    (?=.*[A-Z])
  |
    (?=.*\d)
    (?=.*[a-z])
    (?=.*[!@#$%&\/=?_.,:;-])
  |
    (?=.*\d)
    (?=.*[A-Z])
    (?=.*[!@#$%&\/=?_.,:;-])
  |
    (?=.*[a-z])
    (?=.*[A-Z])
    (?=.*[!@#$%&\/=?_.,:;-])
  )
/x

Remove all whitespace, if you don't want/have access to the /x modifier.