#!/usr/bin/env python3 """Passcheck — password strength auditor.""" import argparse, math, re, sys, string def color(text, code): return f"\033[{code}m{text}\033[0m" if sys.stdout.isatty() else text COMMON_PATTERNS = [ r"password", r"1234", r"qwerty", r"abc123", r"letmein", r"admin", r"welcome", r"monkey", r"dragon", r"master", r"123456", r"passw0rd", r"iloveyou", r"sunshine", r"princess", r"football", r"baseball", r"trustno1", ] def entropy(password): char_sets = 0 if re.search(r"[a-z]", password): char_sets += 26 if re.search(r"[A-Z]", password): char_sets += 26 if re.search(r"[0-9]", password): char_sets += 10 if re.search(r"[!@#$%^&*()_+\-=\[\]{}|;:,.<>?/~`]", password): char_sets += 32 if char_sets == 0: return 0 ent = len(password) * math.log2(char_sets) return ent def analyze(password): issues = [] score = 100 # Length checks if len(password) < 8: score -= 25 issues.append("Too short ( < 8 chars )") elif len(password) < 12: score -= 10 issues.append("Could be longer ( < 12 chars )") # Character diversity if not re.search(r"[a-z]", password): score -= 10 issues.append("Missing lowercase letters") if not re.search(r"[A-Z]", password): score -= 10 issues.append("Missing uppercase letters") if not re.search(r"[0-9]", password): score -= 10 issues.append("Missing digits") if not re.search(r"[!@#$%^&*()_+\-=\[\]{}|;:,.<>?/~`]", password): score -= 10 issues.append("Missing special characters") # Repeated chars if re.search(r"(.)\1{3,}", password): score -= 10 issues.append("Contains repeated characters (aaa)") # Sequential seqs = ["abcdefghijklmnopqrstuvwxyz", "0123456789", "qwertyuiop", "asdfghjkl", "zxcvbnm"] lower = password.lower() for seq in seqs: for i in range(len(seq) - 2): if seq[i:i+3] in lower: score -= 10 issues.append(f"Contains sequential pattern '{seq[i:i+3]}'") break # Common patterns for pattern in COMMON_PATTERNS: if pattern in password.lower(): score -= 20 issues.append(f"Contains common pattern '{pattern}'") break # Entropy ent = entropy(password) if ent < 30: score -= 15 issues.append(f"Low entropy ({ent:.1f} bits)") score = max(0, min(100, score)) # Grade if score >= 80: grade = color("Strong", "32") elif score >= 60: grade = color("Moderate", "33") elif score >= 40: grade = color("Weak", "31") else: grade = color("Very Weak", "31;1") return score, grade, ent, issues def main(): parser = argparse.ArgumentParser( description="Passcheck — password strength auditor", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=( "Examples:\n" " passcheck # Interactive mode\n" " echo 'mypassword' | passcheck # Read from stdin\n" " passcheck --stdin # Read one line from stdin\n" " passcheck --batch passwords.txt # Check passwords in file\n" ), ) parser.add_argument("--stdin", action="store_true", help="Read password from stdin") parser.add_argument("--batch", metavar="FILE", help="Check passwords from file (one per line)") parser.add_argument("--version", action="version", version="passcheck 1.0.0") args = parser.parse_args() passwords = [] if args.batch: try: with open(args.batch) as f: passwords = [l.strip() for l in f if l.strip()] except FileNotFoundError: print(f"Error: File '{args.batch}' not found", file=sys.stderr) sys.exit(1) elif args.stdin or not sys.stdin.isatty(): passwords = [sys.stdin.readline().strip()] else: import getpass pwd = getpass.getpass("Enter password to check: ") passwords = [pwd] for pwd in passwords: if not pwd: continue score, grade, ent, issues = analyze(pwd) print() print(f" {color('█', '36')} Password Analysis") print(f" {color('─' * 40, '90')}") print(f" Length: {len(pwd)} characters") print(f" Entropy: {ent:.1f} bits") print(f" Score: {score}/100 {grade}") print() # Visual bar bar_width = 30 filled = int(score / 100 * bar_width) bar = color("█" * filled, "32") if score >= 60 else \ color("█" * filled, "33") if score >= 40 else \ color("█" * filled, "31") empty = "░" * (bar_width - filled) print(f" [{bar}{empty}]") print() if issues: print(f" {color('Issues:', '33')}") for iss in issues: print(f" {color('•', '31')} {iss}") else: print(f" {color('✓ No issues found', '32')}") print() if __name__ == "__main__": main()