POSIX extended regular expressions have no "and" operator and no look around operators, so to construct one regexp that positively validates those passwords, you'd need to build a thousands of characters long one that lists all the combinations of lower and digits and number of characters in between, something like:
u='[[:upper:]]' l='[[:lower:]]' d='[[:digit:]]'
regexp="^$u(($l$d|$d$l).{5,12}|($d.$l|.$d$l|$l.$d|.$l$d).{4,11}|...etc...)\$"
It would be so long that you'd likely reach some limit in your system's regexp engine.
Here, it would be easier to match several regexps:
valid_password=(
'^[[:upper:]]'
'[[:lower:]]'
'[[:digit:]]'
'^.{8,15}$'
)
validate_password() {
local regexp
for regexp in "${valid_password[@]}"; do
[[ $1 =~ $regexp ]] || return
done
}
if validate_password "$some_password"; then
echo OK
fi
Doing a negative matching with one regexp would be easier however:
incorrect='^([^[:upper:]].*|[^[:digit:]]*|[^[:lower]]*|.{0,7}|.{16,})$'
(incorrect if starting with a character other than an uppercase letter, or is made entirely of non-digits or of non-lowers or made of 0 to 7 characters or of 16 or more characters).
If [[ $password =~ $incorrect ]]
returns true, that means the password is incorrect. However, if it returns false, that could also be because $password
contains sequences of bytes that don't form valid characters, so you'd also want to add a check for [[ $password =~ ^.*$ ]]
to verify that the password is made of valid character before declaring it valid.
If switching from bash
to zsh
is an option, you could use PCREs that do have some look-around operators, which would make it easier:
set -o rematchpcre
[[ $password =~ '^(?=.*\d)(?=.*\p{Ll})\p{Lu}.{7,14}\Z' ]]
Note that if $password
is not valid text in the locale, that will fail (return false) and an error will be reported. Note that PCRE don't support multibyte encodings other than UTF-8.
Also note that variables in zsh can contain the NUL character, the PCRE API, unlike the POSIX ERE API doesn't choke on those bytes, but you'd likely want to reject those characters in passwords along with all other control characters (including newline).
(note that I've not tested any of this)
^[[:upper:]][[:alnum:]]{8,15}$
followed by checks for[[:lower:]]
and[[:digit:]]
. It'll be far easier to understand and maintain. – muru Apr 17 '22 at 03:37if [[ "$pass" =~ ^[[:upper:]][[:alnum:]]{8,15}$ && "$pass =~ [[:lower:]]+[[:digit:]]+ ]]
? I think the order of second condition will matter then. – Cruise5 Apr 17 '22 at 04:51