Password Policy Tester
Test passwords against configurable policy rules: length, complexity, forbidden words.
Policy Rules
Frequently Asked Questions
Code Implementation
import re
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class PasswordPolicy:
min_length: int = 8
max_length: int = 128
require_uppercase: bool = True
require_lowercase: bool = True
require_digit: bool = True
require_symbol: bool = True
min_unique_chars: int = 5
forbidden_patterns: list[str] = field(default_factory=lambda: [
r"(..)\1{2,}", # repeated two-char block 3+ times
r"(.)\1{3,}", # same char 4+ times in a row
r"(?i)password", # literal word "password"
r"(?i)qwerty",
])
symbol_chars: str = r"!@#$%^&*()-_=+[]{}|;':",./<>?"
@dataclass
class PolicyResult:
passed: bool
violations: list[str] = field(default_factory=list)
score: int = 0 # 0-100
def check_password(password: str, policy: Optional[PasswordPolicy] = None) -> PolicyResult:
if policy is None:
policy = PasswordPolicy()
violations: list[str] = []
score = 0
# Length checks
if len(password) < policy.min_length:
violations.append(f"Too short: minimum {policy.min_length} characters")
elif len(password) >= policy.min_length:
score += 25
if len(password) > policy.max_length:
violations.append(f"Too long: maximum {policy.max_length} characters")
# Character class checks
if policy.require_uppercase and not re.search(r"[A-Z]", password):
violations.append("Must contain at least one uppercase letter")
else:
score += 15
if policy.require_lowercase and not re.search(r"[a-z]", password):
violations.append("Must contain at least one lowercase letter")
else:
score += 15
if policy.require_digit and not re.search(r"\d", password):
violations.append("Must contain at least one digit")
else:
score += 15
if policy.require_symbol and not re.search(
f"[{re.escape(policy.symbol_chars)}]", password
):
violations.append("Must contain at least one symbol")
else:
score += 15
# Unique characters
if len(set(password)) < policy.min_unique_chars:
violations.append(f"Must use at least {policy.min_unique_chars} different characters")
else:
score += 15
# Forbidden patterns
for pattern in policy.forbidden_patterns:
if re.search(pattern, password):
violations.append(f"Contains forbidden pattern: {pattern}")
passed = len(violations) == 0
return PolicyResult(passed=passed, violations=violations, score=min(score, 100))
# Example usage
if __name__ == "__main__":
tests = ["abc", "Password1!", "C0rrectH0rseBatteryStaple!"]
policy = PasswordPolicy(min_length=12)
for pwd in tests:
result = check_password(pwd, policy)
print(f"{pwd!r}: passed={result.passed}, score={result.score}")
for v in result.violations:
print(f" - {v}")Comments & Feedback
Comments are powered by Giscus. Sign in with GitHub to leave a comment.