1

In my Javascript angular application I have a regex to validate usernames.

The issue I am facing, after doing much research is that utilising negative lookahead with expressions that contain white spaces is not working.

The Requirements

Username can be composed of many alphanumeric strings split at most with one space. Spaces at edges are not allowed. also the username should be filtered against a couple of banned names.

1)

(/^[a-zA-Z\d]+([\s][a-zA-Z\d]+)+?$/).test("admin may not be used") 

allows alphanumeric words to be split by one consequent space at a time, and disallows spaces at edges

2)

(/^(?!(?:admin|alfred)$)[a-zA-Z\d]+$/).test("admin")

works and word admin is not allowed

3) merging both:

(/^(?!(?:admin|alfred)$)[a-zA-Z\d]+([\s][a-zA-Z\d]+)+?$/).test("admin may not be used") 

fails! and will allow the banned word admin to be used.

Expected Result:

Both filters are expected to work, that is the banned words list , as well as consequent space filter. Can you please point what possibly is wrong with my expression?

Community
  • 1
  • 1
VendettaTurkey
  • 330
  • 3
  • 10
  • 1
    Your lookahead fails, as it requires the end of the string after your banned word. Try `(?!(?:admin|alfred))` to ban only the first word, `(?!.*(?:admin|alfred))` to ban anywhere in the string or `(?!.*\b(?:admin|alfred)\b)` to ban anywhere in the string when it is a whole word. – Sebastian Proske Jul 02 '17 at 09:50
  • The end of the word anchor $ is included as suggested by this SO answer [link]https://stackoverflow.com/a/5540478/4052207 the issue with your suggestion is that it doesn't respect the multi-word rule, try it with multiple words split with a space it will fail. @SebastianProske – VendettaTurkey Jul 02 '17 at 10:36
  • 1
    Have a look [at this demo](https://regex101.com/r/2wHElL/1). You wrote *split at most with one space*, thus I guess only 1 or 2 word strings are allowed. – Wiktor Stribiżew Jul 02 '17 at 10:38
  • @WiktorStribiżew That is exactly the right solution. except that multiple words can exist. adding a * would nail it right (/^(?!.*\b(?:alfred|edward)\b)[a-zA-Z\d]+(?: [a-zA-Z\d]+)*?$/).test("edwards test edward") Please post it in an answer and I will mark it correct. Thank you very much – VendettaTurkey Jul 02 '17 at 10:59

1 Answers1

2

You may consider using

/^(?!.*\b(?:admin|alfred)\b)[a-zA-Z\d]+(?:\s[a-zA-Z\d]+)+$/

or a bit longer one that is a bit more effecient (but less readable):

/^(?!(?:admin|alfred)\b)[a-zA-Z\d]+(?:\s(?!(?:admin|alfred)\b)[a-zA-Z\d]+)+$/

See the regex demo

If one word usernames are allowed, replace the last + (1 or more repetitions) with a ? (1 or 0 repetitions) quantifier (demo).

If you use it in AngularJS, also use ng-trim="false" to make sure leading and trailing whitespaces are not allowed.

Pattern details:

  • ^ - start of string
  • (?!.*\b(?:admin|alfred)\b) - after zero or more chars (.*) there can't be admin or alfred as whole words (else, the regex will return false)
  • [a-zA-Z\d]+ - 1 or more alphanumerics
  • (?: - start of a non-capturing group
    • \s - a whitespace
    • [a-zA-Z\d]+ - 1 or more alphanumerics
  • )+ - end of the non-capturing group that will be repeated 1 or more times
  • $ - end of string.
Graham
  • 7,431
  • 18
  • 59
  • 84
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563