Search code examples
regexnsis

NSIS using a regEx for pattern matching. Password validation


I've been trying to match a string to a pattern for password validation. e.g. password must contain upper/lower and special character.

I've used the StrCSpn (modified for case sensitivity) function to scan strings for a character, but I had to have a separate list for special characters and upper/lowercase letters. So the code is a bit bulky https://nsis.sourceforge.io/StrCSpn,_StrCSpnReverse:_Scan_strings_for_characters

Is there a way to use a regular expression in NSIS like here RegEx for at least 1 number, 1 lower case and 1 upper case letter


Solution

  • You could use the NSISpcre plug-in:

    Function PasswordMatchesPolicy
    Push $0
    Push $1
    Push $2
    Push 0
    Push 0
    Exch 5
    Push `^(?:(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*)$$` #stackoverflow.com/questions/43127814/regex-for-at-least-1-number-1-lower-case-and-1-upper-case-letter
    NSISpcre::REMatches 
    Pop $0
    ${If} $0 == true
        Pop $0
        ${For} $1 1 $0
            Pop $2
        ${Next}
        Push 1
    ${Else}
        Push ""
    ${EndIf}
    Exch 3
    Pop $2
    Pop $1
    Pop $0
    FunctionEnd
        
    !include LogicLib.nsh
    ShowInstDetails show
    Section
    
    !macro Test str
    Push "${str}"
    Call PasswordMatchesPolicy
    Pop $0
    ${If} $0 <> 0
        StrCpy $0 PASS
    ${Else}
        StrCpy $0 FAIL
    ${EndIf}
    DetailPrint "$0:${str}"
    !macroend
    
    !insertmacro Test "foo"
    !insertmacro Test "Foo"
    !insertmacro Test "Foo1"
    !insertmacro Test "Foo#"
    !insertmacro Test "Foo1#"
    SectionEnd
    

    Or if you only care about ASCII:

    Function PasswordMatchesPolicy
    !define /IfNDef C1_UPPER 0x0001
    !define /IfNDef C1_LOWER 0x0002
    !define /IfNDef C1_DIGIT 0x0004
    !define /IfNDef C1_DEFINED 0x0200
    System::Store S
    System::Call 'KERNEL32::lstrcpyA(@r1,ms)'
    StrCpy $5 ""
    StrCpy $3 0
    loop:
        System::Call '*$1(&i$3,&i1.r4)'
        ${If} $4 <> 0
            ${If} $4 >= 65
            ${AndIf} $4 <= 90
                IntOp $5 $5 | ${C1_UPPER}
            ${ElseIf} $4 >= 97
            ${AndIf} $4 <= 122
                IntOp $5 $5 | ${C1_LOWER}
            ${ElseIf} $4 >= 48
            ${AndIf} $4 <= 57
                IntOp $5 $5 | ${C1_DIGIT}
            ${Else}
                IntOp $5 $5 | ${C1_DEFINED} ; Just mark it as "something"
            ${EndIf}
            IntOp $3 $3 + 1
            Goto loop
        ${EndIf}
    
    IntOp $4 $5 & 3
    ${If} $4 = 3 ; C1_UPPER and C1_LOWER
    ${AndIf} $5 >= 4 ; Digit or symbol
        Push 1
    ${Else}
        Push 0
    ${EndIf}
    System::Store L
    FunctionEnd