-2

The requirements I've been set are...

MUST match (1 minimum character/number):

  • 1 number (?=.*\d)
  • 1 lower case character (?=.*[a-z])
  • 1 upper case character (?=.*[A-Z])
  • no whitespace (?!.*\s)
  • Between 8 and 40 characters .{8,40}

CAN match, but doesn't have to:

  • Special characters limited to $*%!.,^

This is what I have so far: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,40}/

I'd like to keep it segmented out the way I do for readability - Unless there's a reason not to?! Happy to change if there are any performance benefits, or if I've done something silly/pointless?

The above works for the most part, including my special characters. However, when I type in a "restricted" character, such as @, it still matches.

I'm a bit lost, so any help would be very much appreciated! Thank you!

Examples of what SHOULD match:

  • abcABC123
  • aaBB33!!
  • !a*Bc9!.abBC*4

Examples of what SHOULD NOT match:

  • abc ABC 123
  • abc@ABC?123
  • áááBBB333

Restrictions:

Anything that is NOT a-z A-Z 0-9 or $*%!.,^ is considered a restricted character

Nick
  • 2,261
  • 5
  • 33
  • 65
  • 1
    Do you have some examples of strings that should match the pattern, or are these requirements all we're working with? – Lewis Feb 19 '20 at 14:28
  • What are you defining as "restricted characters"? Your `.{8,40}` doesn't set any restrictions nor do any of your look-aheads... – dvo Feb 19 '20 at 14:29
  • dot '.' matches any character other than \n. That is why you are matching so many things. – Barka Feb 19 '20 at 14:33
  • So for example `abcABC123` would match, as would `abcABC123!!!` but something that doesn't contain any of the characters listed above wouldn't, so for example `abcABC123@@@` – Nick Feb 19 '20 at 14:34
  • @Christian I've updated the post with a few examples – Nick Feb 19 '20 at 14:37
  • @dvo I've updated my post with a couple of examples. My definition of restricted character is anything that's NOT a-z A-Z 0-9 or $*%!.,^ – Nick Feb 19 '20 at 14:38
  • Never understood why you would want to limit the character set used for passwords. When you restrict it, aren't you restricting entropy and underlying security? – PJProudhon Feb 19 '20 at 14:43
  • 1
    @PJProudhon Per the opening sentence "The requirements I've been set are..." I would guess that this is a blind coding assignment whose sole purpose is to get OP to learn regex. – MonkeyZeus Feb 19 '20 at 14:46
  • @anubhava per "Special characters limited to `$*%!.,^`" I would assume not – MonkeyZeus Feb 19 '20 at 14:48
  • This is always the same: `^`, then lookaheads, then comes the consuming pattern where you define what is allowed and what is not, then `$`. If you use `.` in the consuming part you allow anything but line breaks. Just use a character class, as is shown in many already posted answers. – Wiktor Stribiżew Feb 19 '20 at 14:50
  • @MonkeyZeus I guess so. But I find these times appropriate to fight against these long lasting nonsense limits. – PJProudhon Feb 19 '20 at 15:22

1 Answers1

3

You can use:

^(?=\D*\d)(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z])[a-zA-Z\d$*%!.,^]{8,40}$
  • ^(?=\D*\d) - require a digit somewhere
  • (?=[^a-z]*[a-z]) - require a lowercase char somewhere
  • (?=[^A-Z]*[A-Z]) - require a uppercase char somewhere
  • [a-zA-Z\d$*%!.,^]{8,40}$ - from start to finish require 8 to 40 of these whitelisted chars in any order

Test your strings one at a time at https://regex101.com/r/lrABwJ/1

MonkeyZeus
  • 20,375
  • 4
  • 36
  • 77
  • `^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z\d$*%!.,^]{8,40}$` is lot more efficient than `(?=.*\d)(?=.*[a-z])(?=.*[A-Z])^[a-zA-Z\d$*%!.,^]{8,40}$` – anubhava Feb 19 '20 at 14:50
  • 1
    @anubhava Actually, there is no difference if you test a string once with `RegExp#test`. The multiline test at regex101.com with `g` modifier just tricks you up. – Wiktor Stribiżew Feb 19 '20 at 14:51
  • Thanks for this! Exactly what I wanted! Out of interest - You've removed the check I had for spaces, but it still works... What did I have wrong in my previous regex that was matching a space?! – Nick Feb 19 '20 at 14:54
  • @Nick The space requirement just sounds like a [red herring](https://en.wikipedia.org/wiki/Red_herring) to trip you up. I simply omitted the whitespace from my allowed chars list `[a-zA-Z\d$*%!.,^]` – MonkeyZeus Feb 19 '20 at 14:56
  • This all makes a lot more sense than it did 20 minutes ago! Really appreciate your time here! Thank you! – Nick Feb 19 '20 at 14:58
  • 1
    @Nick I saw you put forth a good-faith effort so I'm happy to help you make sense of it :-) – MonkeyZeus Feb 19 '20 at 15:02
  • @WiktorStribiżew: https://regex101.com/r/gAREfT/3 with `g` and `m` turned off. – anubhava Feb 19 '20 at 15:11
  • 1
    @anubhava Nice catch, I've updated my answer. – MonkeyZeus Feb 19 '20 at 15:14
  • 1
    I'd never use a password regex with `.*` in the lookahead anyway. This is bad practice. One should use the principle of contrast in this case. – Wiktor Stribiżew Feb 19 '20 at 15:15
  • @WiktorStribiżew What would be an appropriate alternative? `.*?` seems marginally better. I am not familiar with "principle of contrast" – MonkeyZeus Feb 19 '20 at 15:17
  • 2
    https://www.rexegg.com/regex-style.html#contrast – Wiktor Stribiżew Feb 19 '20 at 15:18
  • 1
    @WiktorStribiżew Thank you, I always appreciate your insight and the performance gain is impressive. Answer updated. I just really wish the "contrast" of that site was better, *ba-dum-tss* – MonkeyZeus Feb 19 '20 at 15:26
  • 1
    @Nick See my update for a more performant regex – MonkeyZeus Feb 19 '20 at 15:28