2

I'm trying to validate a password in R using regex and POSIX character classes. I know a fair amount of regex but not the POSIX pieces. I've looked up the SO resources when asking a regex question, plus half a dozen SO questions and answers, such as this and that. The rules for the password are:

  • one uppercase letter
  • one lowercase letter
  • one or more special characters (ASCII punctuation)
  • between 8 and 32 characters
  • ASCII visible and space characters only

I've tried a number of code variations, as seen by the code that is commented out. I started building the regex at a lower level then worked my way up to the regex that should include all requirements.

    ValidatePassword <- function(stringChar) {
    #  validPW <- "([[:lower:]]+[[:upper:]]+){8,32}"
    #  validPW <- "[[:lower:]]+[[:upper:]]+"
    #  validPW <- "^(?=.*[[:lower:]])(?=.*[[:upper:]])(?=.*[[:punct:]])[[:print:]]{8,32}$"
    #  validPW <- "^(?:[[:lower:]])(?:[[:upper:]])(?:[[:punct:]])[[:print:]]{8,32}$"
    #  validPW <- "^(?:.*[[:lower:]])(?:.*[[:upper:]])(?:.*[[:punct:]])[[:print:]]{8,32}$"
    #  validPW <- "^([[:lower:]]+|[[:upper:]]+|[[:punct:]]+)[[:print:]]{8,32}$"
    #  validPW <- "^(?=.*[a-z])(?=.*[A-Z])(?=.*[[:punct:]])[[:print:]]{8,32}$"
    #  validPW <- "^([[:lower:][:upper:][:punct:]])[[:print:]]{8,32}"
      validPW <- "^([[:lower:]]+|[[:upper:]]+|[[:punct:]]+)[[:print:]]{8,32}$"
      return(length(grep(validPW, stringChar, value = TRUE)))
    }
    ifelse(ValidatePassword("#Password"), "valid", "not valid") # valid
    ifelse(ValidatePassword("_PASSWORD"), "valid", "not valid") # not valid
    ifelse(ValidatePassword("Password"), "valid", "not valid") # not valid
    ifelse(ValidatePassword("password"), "valid", "not valid") # not valid
    ifelse(ValidatePassword("passwor"), "valid", "not valid") # not valid
    ifelse(ValidatePassword("pAsswords"), "valid", "not valid") # not valid
    ifelse(ValidatePassword("Pa&sword"), "valid", "not valid") # valid
    ifelse(ValidatePassword("Pa&s word"), "valid", "not valid") # valid

I realize that the code is not working properly even when the result says "valid". Can someone offer assistance? ty

Community
  • 1
  • 1
R. Martin
  • 109
  • 9

2 Answers2

1

I tend to prefer readable logic over l33t regex-fu so other humans (or one's future self) can grok what's going on.

is_valid_password <- function(x) {
  grepl('((?=.*[[:lower:]])(?=.*[[:upper:]])(?=.*[[:punct:]]).{8,32})', x, perl=TRUE) & 
    grepl("[[:print:][:space:]]", x)
}

pwds <- c("#Password", "_PASSWORD", "Password", "password", "passwor", "pAsswords", "Pa&sword", "Pa&s word")

is_valid_password(pwds)
## [1]  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE
hrbrmstr
  • 77,368
  • 11
  • 139
  • 205
  • I do not believe this is passing the max length test condition. I added "123456789O12345678(o1234567890123" to the pwds variable, and the function is returning TRUE. I'm still looking at it to see if I can figure this one out. – R. Martin Nov 30 '16 at 23:19
1

You could use the following regular expression to validate:

is.validpw <- function(x) {
  grepl('(?x)                 # free-spacing mode
          ^                   # assert position = beginning of string
           (?=.*[[:upper:]])  # match one uppercase letter
           (?=.*[[:lower:]])  # match one lowercase letter
           (?=.*[[:punct:]])  # match one special character
           [ -~]{8,32}        # match printable ascii characters
          $                   # assert position = end of string', x, perl = TRUE)
}

is.validpw(c('#Password', '_PASSWORD', 'Password', 'password', 
             'passwor', 'pAsswords', 'Pa&sword', 'Pa&s word'))

## [1]  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE
hwnd
  • 69,796
  • 4
  • 95
  • 132
  • This answer passes the length test plus a few more tests that I added. I've never seen "free-spacing mode" before. Can you explain a little or point me to somewhere that does explain? Also, what does (?x) do? – R. Martin Dec 01 '16 at 00:40
  • 1
    free-spacing mode allows whitespace inside the regex so you can comment throughout, explaining each part. `(?x)` is the inline modifier that allows this mode. – hwnd Dec 01 '16 at 02:43
  • I was curious and recoded `[ -~]` to `[[:print:]]` and that ended up breaking the code, allowing an invalid password "pass Word" to sneak through. – R. Martin Dec 01 '16 at 17:07
  • 1
    `[ -~]` matches ascii char 32 to ascii char 126 in the [ascii table](http://www.asciitable.com/) – hwnd Dec 01 '16 at 17:11