Do you want to:
/^(?=.{6,12}$)...
What you do says: find me any character sequence followed by:
- 6-12 characters
- another character sequence followed by 2 digits
- another character sequence followed by two uppercase letters
- another sequence of characters followed by two lowercase letters
And all this is accompanied by another sequence of characters. Therefore, the maximum length does not work, since 30 characters will be followed by 00AAaa and another 30 characters.
And what you do forces two numbers together. To be less strict than this, but requiring at least two numbers anywhere on the line:
/^(?=.{6,12}$)(?=(.*?\d){2})(?=(.*?[AZ]){2})(?=(.*?[az]){2})/
Finally, you'll notice that I'm using non-greedy expressions ( .*? ). This will avoid a lot of return and for this kind of validation is what you usually should use. Difference between:
(.*\d){2}
and
(.*?\d){2}
This is what the first captures all characters with .* , And then searches for a digit. He will not find it, because it will be at the end of the string, so it will return a single character, and then search for a digit. If this is not a number, she will continue to roll back until she finds it. After that, it will correspond to this entire expression for the second time, which will cause an even greater rollback.
What greedy wildcards mean.
The second version will pass null characters to .*? and look for a figure. If it's not a number .*? , it will capture other characters, and then look for a number and so on. In particular, on long search lines this can be an order of magnitude faster. With a short password, this will almost certainly not change, but it is a good habit to understand how the regex assistant works and write the best regex you can.
Saying this, this is probably an example too smart for your own good. If the password is rejected as not satisfying these conditions, how do you determine which one was unsuccessful in order to give the user feedback on what to fix? A software solution in practice is probably preferable.