Private/Crypto/Test-PukWeakness.ps1

function Test-PukWeakness {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Code,

        [Parameter(Mandatory)]
        [psobject]$WeaknessRules
    )

    function Test-PukSequentialRun {
        param([string]$Code, [int]$RunLength)
        for ($i = 0; $i -le $Code.Length - $RunLength; $i++) {
            $window = $Code.Substring($i, $RunLength)
            $ascending = $true
            $descending = $true
            for ($j = 1; $j -lt $window.Length; $j++) {
                if ([int][char]$window[$j] - [int][char]$window[$j - 1] -ne 1) { $ascending = $false }
                if ([int][char]$window[$j - 1] - [int][char]$window[$j] -ne 1) { $descending = $false }
            }
            if ($ascending -or $descending) { return $true }
        }
        return $false
    }

    function Test-PukRepeatedDigitRun {
        param([string]$Code, [int]$RunLength)
        for ($i = 0; $i -le $Code.Length - $RunLength; $i++) {
            $window = $Code.Substring($i, $RunLength)
            if (@($window.ToCharArray() | Select-Object -Unique).Count -eq 1) { return $true }
        }
        return $false
    }

    function Test-PukLowUniqueDigitCount {
        param([string]$Code, [int]$MinUniqueDigits)
        $uniqueCount = @($Code.ToCharArray() | Select-Object -Unique).Count
        return $uniqueCount -lt $MinUniqueDigits
    }

    function Test-PukRepeatingPattern {
        param([string]$Code)
        $length = $Code.Length
        for ($blockSize = 1; $blockSize -lt $length; $blockSize++) {
            if ($length % $blockSize -ne 0) { continue }
            $block = $Code.Substring(0, $blockSize)
            $candidate = $block * ($length / $blockSize)
            if ($candidate -eq $Code) { return $true }
        }
        return $false
    }

    function Test-PukPalindrome {
        param([string]$Code)
        $chars = $Code.ToCharArray()
        [Array]::Reverse($chars)
        return (-join $chars) -eq $Code
    }

    function Test-PukDatePattern {
        param([string]$Code)
        if ($Code.Length -ne 8) { return $false }
        foreach ($format in @('ddMMyyyy', 'MMddyyyy')) {
            $parsed = [DateTime]::MinValue
            if ([DateTime]::TryParseExact($Code, $format, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::None, [ref]$parsed)) {
                if ($parsed.Year -ge 1900 -and $parsed.Year -le 2099) { return $true }
            }
        }
        return $false
    }

    if ($WeaknessRules.rejectSequentialRun.enabled -and (Test-PukSequentialRun -Code $Code -RunLength $WeaknessRules.rejectSequentialRun.runLength)) {
        return $true
    }

    if ($WeaknessRules.rejectRepeatedDigitRun.enabled -and (Test-PukRepeatedDigitRun -Code $Code -RunLength $WeaknessRules.rejectRepeatedDigitRun.runLength)) {
        return $true
    }

    if ($WeaknessRules.rejectLowUniqueDigitCount.enabled -and (Test-PukLowUniqueDigitCount -Code $Code -MinUniqueDigits $WeaknessRules.rejectLowUniqueDigitCount.minUniqueDigits)) {
        return $true
    }

    if ($WeaknessRules.rejectRepeatingPattern.enabled -and (Test-PukRepeatingPattern -Code $Code)) {
        return $true
    }

    if ($WeaknessRules.rejectPalindrome.enabled -and (Test-PukPalindrome -Code $Code)) {
        return $true
    }

    if ($WeaknessRules.rejectDatePattern.enabled -and (Test-PukDatePattern -Code $Code)) {
        return $true
    }

    if ($WeaknessRules.customDenyList -and $Code -in @($WeaknessRules.customDenyList)) {
        return $true
    }

    return $false
}