Jump to content

Regex validates as true when it should not


Kovacic
 Share

Go to solution Solved by Kovacic,

Recommended Posts

I am trying to validate input to make sure it follows Active Directory password rules, except it should have 10 char. Technically, this should come back as false but it seems to be validating as true...

$Password = "Pass1"
$result = StringRegExp($Password,"^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[#!@$%^&*()\-_+={}[\]|\\:;<>,.?\/]).{10,16}$")
if @Error then 
Msgbox(0,"error level",@Error)
else
Msgbox(0,"test",$result)
EndIf

 

This is a breakdown of the Regex I am using...

^                                              # anchors to start of string
(?=.*?[a-z])                                   # lookahead for lowercase
(?=.*?[A-Z])                                   # lookahead for uppercase
(?=.*?[0-9])                                   # lookahead for numbers
(?=.*?[#!@$%^&*()\-_+={}[\\]|\:;<>,.?\/])    # lookahead for special characters
.{8,14}                                        # the actual capture, also sets boundaries for 8-14
$                                              # anchors to end of string

 

Where did I go wrong? This should tell me that Pass1 is no good.

C0d3 is P0etry( ͡° ͜ʖ ͡°)

Link to comment
Share on other sites

Is that not what you are expecting? There's no @error, and there's no match with your RegEx, nothing is returning true. From the StringRegExp helpfile: Returns 1 (match) or 0 (no match). Are you thinking 0 should be true (as it is in some languages)?

When I run it I get 0 for $result as well (no match). There's a few AutoIt regex testers, but I personally like using regex101: https://regex101.com/r/81cRXx/1

Running it from there also shows there is no match, everything seems to be working fine from AutoIt as well.

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

4 minutes ago, mistersquirrle said:

Is that not what you are expecting? There's no @error, and there's no match with your RegEx, nothing is returning true. From the StringRegExp helpfile: Returns 1 (match) or 0 (no match). Are you thinking 0 should be true (as it is in some languages)?

When I run it I get 0 for $result as well (no match). There's a few AutoIt regex testers, but I personally like using regex101: https://regex101.com/r/81cRXx/1

Running it from there also shows there is no match, everything seems to be working fine from AutoIt as well.

Shoot... Yeah that is what I wanted.. First I got the response mixed up, then I didnt set my test password correct.. ok thank you, yes this works.. my bad.

C0d3 is P0etry( ͡° ͜ʖ ͡°)

Link to comment
Share on other sites

36 minutes ago, ioa747 said:

maybe help

 

On this subject, is there any easy way to generate a random string that conforms to that regular expression, even a bunch of random Chars? I can do this by writing up something messy and quick but if there is something that exists already that is tested, I would prefer that. Thanks!

C0d3 is P0etry( ͡° ͜ʖ ͡°)

Link to comment
Share on other sites

2 minutes ago, Kovacic said:

is there any easy way to generate a random string that conforms to that regular expression, even a bunch of random Chars?

Check this out, it looks like that it works, first time I've ever looked for something like this: https://www.browserling.com/tools/text-from-regex

qz= da^oZv2Ea
qu{z#@(=F$>j:rZ
,&Z^d>]l-;s
gSR(;,e[Dxy
bR1Syog,u,~{-H)

 

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

@Kovacic that's an interesting pattern you shared with us, because the match requires, at least :
* A lower case letter
* An upper case letter
* A digit
* A punctuation sign

May I suggest, if Active Directory allows it, to use any punctuation sign found in the appropriate POSIX character class, for example :

1353542016_RegExplookahead2.png.270e078b2700f7312456a2607ca95b39.png

It would simplify the pattern and get rid of the punctuation string found actually in your pattern, which includes the escaped metacharacters,  e.g. \ ^ - ] 

If I'm not mistaken, there are 32 punctuation characters in [:punct:] , all of them found in the interval chr(33) to chr(126) , e.g.
 94 characters found from chr(33) to chr(126)
-52 for the alphabetical characters (upper & lower)
-10 for digits
===
 32 punctuation characters in  [:punct:]

@ioa747 : as you can see in the pic above, version 2.5k will have a different context menu compared to version 2.5j but it takes time to rearrange it exactly as I wish...
A major improvement will be to save & restore binary data as UTF8 (necessary in some cases), 

Edited by pixelsearch
POSIX not PÖSIX (wondering what was keyed to display it like that lol)
Link to comment
Share on other sites

  • Solution

As a quick update to this, I was able to accomplish the goal just by generating a password using the following:

Func GetaPass()
    Do
        $Pw = Gen2()
    Until Validate($Pw) = 0
    Return $Pw
EndFunc   ;==>GetaPass
Func Gen2()
    $boop = ""
    $Beep = ""
    For $1 = 0 To 11
        $boop = Random(1, 4, 1)
        If $boop = 1 Then $Beep = $Beep & GenSymbol()
        If $boop = 2 Then $Beep = $Beep & String(Random(0, 9, 1))
        If $boop = 3 Then $Beep = $Beep & GenUpper()
        If $boop = 4 Then $Beep = $Beep & GenLower()
    Next
    Return $Beep
EndFunc   ;==>Gen2
Func GenUpper()
    Return Chr(Random(65, 90, 1))
EndFunc   ;==>GenUpper
Func GenLower()
    Return Chr(Random(97, 122, 1))
EndFunc   ;==>GenLower
Func GenSymbol()
    $symnum = Random(1, 7, 1)
    If $symnum = 1 Then Return "!"
    If $symnum = 2 Then Return "@"
    If $symnum = 3 Then Return "$"
    If $symnum = 4 Then Return ";"
    If $symnum = 5 Then Return "?"
    If $symnum = 6 Then Return "%"
    If $symnum = 7 Then Return "#"
EndFunc   ;==>GenSymbol

Then I validated it with this

Func Validate($Val)
    $result = StringRegExp($Val, "^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[#!@$%^&*()\-_+={}[\]|\\:;<>,.?\/]).{10,16}$")
    If @error Then
        Return 1
    Else
        Return 0
    EndIf
EndFunc   ;==>Validate

 

So in short, I generate the password randomly, and run it through a validation process to make sure whatever password comes out fits the bill. I only posed this because I hate it when im looking for a solution and I find a thread that doesn't mention how it was solved.

 

C0d3 is P0etry( ͡° ͜ʖ ͡°)

Link to comment
Share on other sites

glad you made it :)

and thanks for sharing it !

I definitely couldn't do the validation process, but I could make sure that at least 1 GenSymbol, 1 GenUpper, 1 GenLower, 1 Number, will appear

; https://www.autoitscript.com/forum/topic/209799-regex-validates-as-true-when-it-should-not/?do=findComment&comment=1514606

#include <Array.au3>

Global $Pword = PassMaker(12)

ConsoleWrite("Validate(" & $Pword & ")=" & Validate($Pword) & @CRLF)

;----------------------------------------------------------------------------------------
Func PassMaker($Length)
    Local $boop, $Rnt
    Local $sPass[$Length + 1]

    $sPass[1] = GenSymbol()
    $sPass[2] = String(Random(0, 9, 1))
    $sPass[3] = GenUpper()
    $sPass[4] = GenLower()

    For $i = 5 To $Length
        $boop = Random(1, 4, 1)
        Switch $boop
            Case 1
                $Rnt = GenSymbol()
            Case 2
                $Rnt = String(Random(0, 9, 1))
            Case 3
                $Rnt = GenUpper()
            Case 4
                $Rnt = GenLower()
        EndSwitch

        $sPass[$i] = $Rnt
    Next

    _ArrayShuffle($sPass, 1)

    For $i = 1 To $Length
        $sPass[0] &= $sPass[$i]
    Next

;~  _ArrayDisplay($sPass)

    Return $sPass[0]

EndFunc   ;==>PassMaker
;----------------------------------------------------------------------------------------
Func GenUpper()
    Return Chr(Random(65, 90, 1))
EndFunc   ;==>GenUpper
;----------------------------------------------------------------------------------------
Func GenLower()
    Return Chr(Random(97, 122, 1))
EndFunc   ;==>GenLower
;----------------------------------------------------------------------------------------
Func GenSymbol()
    $symnum = Random(1, 7, 1)
    If $symnum = 1 Then Return "!"
    If $symnum = 2 Then Return "@"
    If $symnum = 3 Then Return "$"
    If $symnum = 4 Then Return ";"
    If $symnum = 5 Then Return "?"
    If $symnum = 6 Then Return "%"
    If $symnum = 7 Then Return "#"
EndFunc   ;==>GenSymbol
;----------------------------------------------------------------------------------------
Func Validate($Val)
    $result = StringRegExp($Val, "^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[#!@$%^&*()\-_+={}[\]|\\:;<>,.?\/]).{10,16}$")
    If @error Then
        Return 1
    Else
        Return 0
    EndIf
EndFunc   ;==>Validate

 

I know that I know nothing

Link to comment
Share on other sites

4 hours ago, ioa747 said:

I definitely couldn't do the validation process

Me neither.

@Kovacic there's an issue with your script because Validate() validates any password.
You can reproduct it this way, which accepts "123" as password, when it shouldn't.

Local $Password = GetaPass()
MsgBox(0, "Done", "Password = " & $Password)

Func GetaPass()
    Do
        ; $Pw = Gen2()
        $Pw = "123"
    Until Validate($Pw) = 0
    Return $Pw
EndFunc   ;==>GetaPass

The reason is that there is something wrong with the Validate function. You're calling StringRegExp with 2 parameters only, which means that the 3rd parameter (the flag) is set to 0 (by default)

This Mode 0 returns :
* 1 (match)
* 0 (no match)

And @error (in Mode 0) is only used when it's = 2 (Bad pattern) but your pattern is always correct, which means the function always return 0 actually.

The return of this function should be based on $result and not on @error value. It should be easy now to fix your code.

Edit: alternatively, adding only 2 characters to the script should fix it, without modifying anything else, let's quiz :D

Edited by pixelsearch
Link to comment
Share on other sites

Here would be my take on your solution @Kovacic, with @ioa747's approach, because I like that and avoids a 'brute force' approach:

#include <Array.au3>

Global $sGeneratedPassword
Global $nGenTime
Global $hGenerateTimer = TimerInit()
For $iPassword = 0 To 9
    ; We don't need to use GenerateValidPassword if we're forcing each type into the string manually first, then mixing it up
;~  $sGeneratedPassword = GenerateValidPassword(Random(10, 16, 1), Random(10, 16, 1))
    $sGeneratedPassword = GeneratePassword(Random(10, 16, 1), Random(10, 16, 1))
    $nGenTime = @extended / 1000

;~  ConsoleWrite('Password #' & $iPassword & ': ' & $sGeneratedPassword & ' - ' & StringLen($sGeneratedPassword) & ' chars' & @CRLF)
    ConsoleWrite(StringFormat('Password #%3s: %16s - %s chars in %.2f ms, is valid: %s', _
            $iPassword, _ ; Password #%3s
            $sGeneratedPassword, _ ; %16s
            StringLen($sGeneratedPassword), _ ; %s chars
            $nGenTime, _ ; %.2f ms
            Validate($sGeneratedPassword) _ ; valid: %s
            ) & @CRLF)
Next
ConsoleWrite('Wrote ' & $iPassword & ' passwords in ' & Round(TimerDiff($hGenerateTimer), 2) & ' ms' & @CRLF)

Func GenerateValidPassword($iMinLength = 10, $iMaxLength = 16)
    Local $hTimer = TimerInit()
    Local $iLoops = 0
    Local $sPassword
    Local $bValid = False

    Do
        $iLoops += 1
        $sPassword = GeneratePassword($iMinLength, $iMaxLength) ; We don't know if this password is valid (aka has at least one lower, UPPER, num and symbol)
        $bValid = Validate($sPassword)
    Until $bValid = True

    Return SetError(0, Int(TimerDiff($hTimer) * 1000), $sPassword)
EndFunc   ;==>GenerateValidPassword

Func GeneratePassword($iMinLength = 10, $iMaxLength = 16)
    Local Const $iMin = 4, $iMax = 64
    Local $hTimer = TimerInit()
    EnforceMinMax($iMinLength, $iMin, $iMaxLength, $iMax)
    Local $aPass[$iMaxLength]
    Local $sPassword

    $aPass[0] = GenSymbol()
    $aPass[1] = GenNumber()
    $aPass[2] = GenUpper()
    $aPass[3] = GenLower()

    For $iChar = 4 To Random($iMinLength, $iMaxLength, 1) - 1 ; 0 based, not 1
        $iGenType = Random(1, 4, 1)
        Switch $iGenType
            Case 1
                $aPass[$iChar] = GenSymbol()
            Case 2
                $aPass[$iChar] = GenUpper()
            Case 3
                $aPass[$iChar] = GenLower()
            Case 4
                $aPass[$iChar] = GenNumber()
            Case Else
                ConsoleWrite('Error in $iGenType')
                $iChar -= 1
                ContinueLoop
        EndSwitch
    Next

;~  ConsoleWrite('$aPass before: ' & _ArrayToString($aPass, ''))
    _ArrayShuffle($aPass)
;~  ConsoleWrite(', $aPass after: ' & _ArrayToString($aPass, '') & @CRLF)

    $sPassword = _ArrayToString($aPass, '')

    Return SetError(0, Int(TimerDiff($hTimer) * 1000), $sPassword)
EndFunc   ;==>GeneratePassword

Func GenUpper($iMinCharIndex = 65, $iMaxCharIndex = 90)
    Local Const $iMin = 65, $iMax = 90
    EnforceMinMax($iMinCharIndex, $iMin, $iMaxCharIndex, $iMax)

    Return Chr(Random($iMinCharIndex, $iMaxCharIndex, 1))
EndFunc   ;==>GenUpper

Func GenLower($iMinCharIndex = 97, $iMaxCharIndex = 122)
    Local Const $iMin = 97, $iMax = 122
    EnforceMinMax($iMinCharIndex, $iMin, $iMaxCharIndex, $iMax)

    Return Chr(Random($iMinCharIndex, $iMaxCharIndex, 1))
EndFunc   ;==>GenLower

Func GenNumber($iMinNum = 0, $iMaxNum = 9)
    Local Const $iMin = 0, $iMax = 9
    EnforceMinMax($iMinNum, $iMin, $iMaxNum, $iMax)

    Return Random($iMinNum, $iMaxNum, 1)
EndFunc   ;==>GenNumber

Func VerifyMinMax(ByRef $iMin, ByRef $iMax)
    If $iMin > $iMax Then
        Local $iTemp = $iMax ; Store the current min value
        $iMax = $iMin
        $iMin = $iTemp ; Restore min value

        Return False ; Meaning we had to make a change
    EndIf
    Return True ; Meaning Min <= Max
EndFunc   ;==>VerifyMinMax

Func EnforceMinMax(ByRef $iMin, $iAbsoluteMin, ByRef $iMax, $iAbsoluteMax)
    If $iMin < $iAbsoluteMin Then $iMin = $iAbsoluteMin
    If $iMax > $iAbsoluteMax Then $iMax = $iAbsoluteMax
    VerifyMinMax($iMin, $iMax)
EndFunc   ;==>EnforceMinMax

Func GenSymbol()
    Local Const $aSymbols[] = ['#', '!', '@', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', '{', '}', '[', ']', '|', '\', ':', ';', '<', '>', ',', '.', '?', '/']
    Local Static $iSymbolMax = UBound($aSymbols) - 1
    Local $iIndex = Random(0, $iSymbolMax, 1)
    Return $aSymbols[$iIndex]
EndFunc   ;==>GenSymbol

Func Validate($sVal)
    Local $bMatch = StringRegExp($sVal, "^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[#!@$%^&*()\-_+={}[\]|\\:;<>,.?\/]).{10,16}$", $STR_REGEXPMATCH)
    If @error Then
        Return SetError(1, 0, 0)
    EndIf

;~  ConsoleWrite('$bMatch: ' & $bMatch & ' - ' & $sVal & @CRLF)

    Return SetError(0, 0, $bMatch)
EndFunc   ;==>Validate

If you know the format required, creating a generator is a bit easier, but I'm still not sure how you'd really create a reverse regex parser without just creating a bunch of rules/checks for things, and verifying things like positive/negative lookaheads would be very difficult without just brute forcing it.

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

for fun
shuffle using regex

ConsoleWrite(__PasswordGenerator(4) & @crlf)
ConsoleWrite(__PasswordGenerator(11) & @crlf)
ConsoleWrite(__PasswordGenerator(100) & @crlf)

Func __PasswordGenerator($i_Length = 4)
    If $i_Length < 4 Then Return SetError(1)
    Local $Base = $i_Length - 4
    Local $password = ''
    Local $temp
    For $k = 0 To 3
        $password = (($k = 0) ? __GetLetter($k) : _
                                StringRegExpReplace($password, '^(.{' & Random(1, StringLen($password), 1) & '})', '$1 ' & __GetLetter($k)))
        $password = StringReplace($password, ' ', '')
        If $Base = 0 Then ContinueLoop
        $temp = Random(1, $Base, 1)
        $Base = $Base - $temp
        If ($k = 3) And $Base > 0 Then $temp = $temp + $Base
        For $z = 1 To $temp
            $password = StringRegExpReplace($password, '^(.{' & Random(0, StringLen($password), 1) & '})', '$1 ' & __GetLetter($k))
            $password = StringReplace($password, ' ', '')
        Next
    Next
    Return $password
EndFunc

Func __GetLetter($index)
    Local $o_Array[] = ['0123456789', '!@#$%;?', 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
    Local $StrLength = StringLen($o_Array[$index])
    Local $o_Num = Random ( 1, $StrLength, 1)
    If Mod($o_Num, 2) = 0 And ($o_Num < $StrLength) Then $o_Num = $o_Num + 1
    Return StringMid($o_Array[$index], $o_Num, 1)
EndFunc

 

Edited by jugador
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...