Documentation
¶
Overview ¶
Package passcheck provides password strength checking and validation.
It evaluates passwords against multiple criteria including basic rules, pattern detection, dictionary checks, and entropy calculation, returning a comprehensive result with a score, verdict, and actionable feedback.
Usage ¶
res := passcheck.Check("P@ssw0rd123")
fmt.Println(res.Score) // 42
fmt.Println(res.Verdict) // "Weak"
for _, iss := range res.Issues { fmt.Println(iss.Message) }
fmt.Println(res.Suggestions) // ["Good length (16 characters)", ...]
Custom Configuration ¶
cfg := passcheck.DefaultConfig()
cfg.MinLength = 8
cfg.RequireSymbol = false
result, err := passcheck.CheckWithConfig("mypassword", cfg)
Breach database (optional) ¶
Set [Config.HIBPChecker] to a client from the [hibp] package to check passwords against the Have I Been Pwned API (k-anonymity; only a 5-char hash prefix is sent). On API errors, the check is skipped.
Real-time feedback ¶
For password strength meters and live feedback, use CheckIncremental or CheckIncrementalWithConfig. Pass the previous result so the API can return an IncrementalDelta indicating what changed; the UI can skip updates when nothing changed. Debounce input (e.g. 100–300 ms) when calling on every keystroke to keep the UI responsive.
Security Considerations ¶
Passwords are Go strings, which are immutable and garbage-collected. The library cannot zero them from memory after use. For applications that handle passwords as []byte (e.g. reading from an HTTP request body), CheckBytes accepts a byte slice and zeros it immediately after analysis, reducing the window during which plaintext resides in memory.
The library never logs, prints, or persists passwords. Analysis results contain only aggregate scores and generic issue descriptions — never the password itself or sensitive substrings.
A maximum input length of MaxPasswordLength runes is enforced to prevent denial-of-service through algorithmic complexity. Inputs beyond this limit are silently truncated for analysis purposes.
Index ¶
Examples ¶
- Check
- Check (Suggestions)
- Check (Weak)
- CheckBytes
- CheckBytesWithConfig
- CheckIncremental
- CheckIncrementalWithConfig
- CheckWithConfig
- CheckWithConfig (ContextAware)
- CheckWithConfig (ContextAwareEmail)
- CheckWithConfig (ContextAwareLeetspeak)
- CheckWithConfig (CustomPasswords)
- CheckWithConfig (CustomWords)
- CheckWithConfig (DisableLeet)
- CheckWithConfig (Hibp)
- CheckWithConfig (InvalidConfig)
- Config.Validate
- DefaultConfig
- EnterpriseConfig
- Issue (Codes)
- NISTConfig
- NISTConfig (WithContext)
- OWASPConfig
- PCIDSSConfig
- Result.IssueMessages
- UserFriendlyConfig
Constants ¶
const ( VerdictVeryWeak = "Very Weak" VerdictWeak = "Weak" VerdictOkay = "Okay" VerdictStrong = "Strong" VerdictVeryStrong = "Very Strong" )
Verdict constants represent the password strength levels.
const ( CodeRuleTooShort = issue.CodeRuleTooShort CodeRuleNoUpper = issue.CodeRuleNoUpper CodeRuleNoLower = issue.CodeRuleNoLower CodeRuleNoDigit = issue.CodeRuleNoDigit CodeRuleNoSymbol = issue.CodeRuleNoSymbol CodeRuleWhitespace = issue.CodeRuleWhitespace CodeRuleControlChar = issue.CodeRuleControlChar CodeRuleRepeatedChars = issue.CodeRuleRepeatedChars CodePatternKeyboard = issue.CodePatternKeyboard CodePatternSequence = issue.CodePatternSequence CodePatternBlock = issue.CodePatternBlock CodePatternSubstitution = issue.CodePatternSubstitution CodePatternDate = issue.CodePatternDate CodeDictCommonPassword = issue.CodeDictCommonPassword CodeDictLeetVariant = issue.CodeDictLeetVariant CodeDictCommonWord = issue.CodeDictCommonWord CodeDictCommonWordSub = issue.CodeDictCommonWordSub CodeHIBPBreached = issue.CodeHIBPBreached CodeContextWord = issue.CodeContextWord )
Issue codes — stable identifiers for programmatic handling. Consumers can switch on Code to react differently (e.g. "RULE_TOO_SHORT" vs "DICT_COMMON_PASSWORD").
const MaxCustomPasswordsSize = 100_000
MaxCustomPasswordsSize is the maximum number of entries allowed in Config.CustomPasswords. See MaxCustomWordsSize for the rationale.
const MaxCustomWordsSize = 100_000
MaxCustomWordsSize is the maximum number of entries allowed in Config.CustomWords. Larger lists cause O(N×len(password)) dictionary scans that can spike CPU in multi-tenant APIs.
const MaxPasswordLength = 1024
MaxPasswordLength is the maximum number of runes analyzed. Inputs longer than this are truncated to bound CPU and memory usage of the pattern-detection and dictionary-lookup phases.
Variables ¶
var ErrInvalidConfig = errors.New("passcheck: invalid configuration")
ErrInvalidConfig is returned when the configuration fails validation.
Functions ¶
func CheckIncrementalWithConfig ¶ added in v1.2.0
func CheckIncrementalWithConfig(password string, previous *Result, cfg Config) (Result, IncrementalDelta, error)
CheckIncrementalWithConfig evaluates the strength of a password using a custom configuration and returns the result plus an IncrementalDelta describing what changed relative to the previous result.
When previous is nil, a full check is performed and the delta has all Changed fields set to true. When previous is non-nil, the delta indicates whether score, issues, or suggestions differ so the UI can skip redundant updates. Returns an error if the configuration is invalid.
For real-time UIs, debounce input (e.g. 100–300 ms) before calling to avoid excessive work on every keystroke.
Example ¶
ExampleCheckIncrementalWithConfig shows how to use the delta to avoid redundant UI updates when nothing changed.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
var lastResult *passcheck.Result
password := "Xk9$mP2!vR7@nL4&wQzB"
result, delta, _ := passcheck.CheckIncrementalWithConfig(password, lastResult, cfg)
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("ScoreChanged: %v\n", delta.ScoreChanged)
// Call again with same password and previous result; deltas are false.
result2, delta2, _ := passcheck.CheckIncrementalWithConfig(password, &result, cfg)
fmt.Printf("Same score: %v\n", result2.Score == result.Score)
fmt.Printf("ScoreChanged: %v\n", delta2.ScoreChanged)
}
Output: Score: 100 ScoreChanged: true Same score: true ScoreChanged: false
Types ¶
type Checker ¶ added in v1.3.0
Checker performs password strength checks.
func NewChecker ¶ added in v1.3.0
NewChecker returns a validated Checker built from cfg.
It calls cfg.Validate() and returns ErrInvalidConfig if the configuration is invalid. Use this factory when you want to validate the configuration once at startup and reuse the Checker across multiple calls.
checker, err := passcheck.NewChecker(cfg)
if err != nil { /* cfg is invalid */ }
result, _ := checker.Check("mypassword")
type Config ¶
type Config struct {
// MinLength is the minimum number of runes required (default: 12).
MinLength int
// RequireUpper requires at least one uppercase letter (default: true).
RequireUpper bool
// RequireLower requires at least one lowercase letter (default: true).
RequireLower bool
// RequireDigit requires at least one numeric digit (default: true).
RequireDigit bool
// RequireSymbol requires at least one symbol character (default: true).
RequireSymbol bool
// MaxRepeats is the maximum number of consecutive identical characters
// allowed before an issue is reported (default: 3).
MaxRepeats int
// PatternMinLength is the minimum length for keyboard and sequence
// pattern detection (default: 4).
PatternMinLength int
// MaxIssues is the maximum number of issues returned in the result.
// Set to 0 for no limit (default: 5).
MaxIssues int
// CustomPasswords is an optional list of additional passwords to check
// against during dictionary checks. Entries are matched case-insensitively.
// Nil or empty means use only the built-in common password list.
// Must not exceed MaxCustomPasswordsSize entries; Validate() returns an
// error for larger lists to prevent algorithmic DoS on long passwords.
CustomPasswords []string
// CustomWords is an optional list of additional words to detect as
// substrings during dictionary checks. Entries are matched
// case-insensitively. Words shorter than 4 characters are ignored.
// Nil or empty means use only the built-in common word list.
// Must not exceed MaxCustomWordsSize entries; Validate() returns an
// error for larger lists to prevent algorithmic DoS on long passwords.
CustomWords []string
// ContextWords is an optional list of user-specific terms to detect
// in passwords (e.g., username, email, company name). Entries are
// matched case-insensitively and checked for exact matches, substrings,
// and leetspeak variants. Words shorter than 3 characters are ignored.
// Email addresses are automatically parsed to extract individual components.
// Nil or empty means no context-aware checking is performed.
ContextWords []string
// DisableLeet disables leetspeak normalization during dictionary
// checks. When true, substitutions like @ → a, 0 → o, $ → s are
// not applied, and only the plain password is checked against
// dictionaries. Default: false (leet normalization enabled).
DisableLeet bool
// HIBPChecker is an optional checker for the Have I Been Pwned (HIBP)
// breach database. When set, the password is checked via k-anonymity
// (only a 5-character prefix of its SHA-1 hash is sent). If the
// password is found and the count meets HIBPMinOccurrences, an
// HIBP_BREACHED issue is added. On network or API errors, the check
// is skipped (graceful degradation). Use the hibp package to obtain
// a Client that implements this interface.
HIBPChecker interface {
Check(password string) (breached bool, count int, err error)
}
// HIBPMinOccurrences is the minimum breach count required to report
// an HIBP_BREACHED issue. Only used when HIBPChecker or HIBPResult is set.
// Default: 1 (report if found in any breach).
HIBPMinOccurrences int
// HIBPResult, when non-nil, is used instead of calling HIBPChecker. This
// allows callers (e.g. browser WASM) to perform the HIBP lookup outside Go
// and pass the result in, avoiding blocking or CORS issues. When set,
// HIBPChecker is ignored for this check.
HIBPResult *HIBPCheckResult
// ConstantTimeMode, when true, uses constant-time string comparison and
// substring checks in dictionary lookups so that response time does not
// leak whether the password matched a blocklist entry or where it matched.
// Default: false (faster, non-constant-time lookups).
//
// WARNING: ConstantTimeMode reduces timing leakage from branch-dependent
// early exits, but does NOT guarantee wall-clock constant time on real
// hardware. CPU caches and the memory prefetcher introduce measurable
// timing variation for inputs that differ in length or content. For the
// strongest protection, pair ConstantTimeMode with a non-zero
// MinExecutionTimeMs so all responses complete in a uniform minimum time.
ConstantTimeMode bool
// PassphraseMode, when true, enables passphrase-friendly scoring. When a
// password is detected as a passphrase (has at least MinWords distinct words),
// word-based entropy is used instead of character-based entropy, and dictionary
// penalties are reduced. Word boundaries are detected using spaces, hyphens,
// camelCase, and snake_case. Default: false (standard password scoring).
PassphraseMode bool
// MinWords is the minimum number of distinct words required to consider a
// password a passphrase. Only used when PassphraseMode is true.
// Default: 4 (NIST SP 800-63B recommends 4+ words for passphrases).
MinWords int
// WordDictSize is the assumed dictionary size for word-based entropy calculation
// when PassphraseMode is true and a passphrase is detected. Used in the diceware
// model: entropy = wordCount × log2(WordDictSize). Default: 7776 (diceware standard).
WordDictSize int
// MinExecutionTimeMs is the minimum total execution time in milliseconds
// for CheckWithConfig (and related) when ConstantTimeMode is true. The
// function sleeps for the remaining time so that response duration does not
// leak information. Ignored when zero or negative or when ConstantTimeMode
// is false. Default: 0 (no padding).
MinExecutionTimeMs int
// EntropyMode controls how entropy is calculated. Simple mode uses the
// basic character-pool × length formula. Advanced mode (default) uses a
// segment-based model that assigns intrinsic entropy to each detected
// pattern rather than the inflated pool-size estimate. PatternAware mode
// layers Markov-chain analysis on top of Advanced.
EntropyMode EntropyMode
// PenaltyWeights allows customization of penalty multipliers and entropy
// weight for scoring. When nil, default weights are used (all multipliers = 1.0).
// Organizations can adjust these to prioritize different security concerns.
// For example, setting DictionaryMatch to 2.0 doubles dictionary penalties,
// while setting EntropyWeight to 0.5 reduces the influence of entropy on the score.
PenaltyWeights *PenaltyWeights
// VerdictThresholds overrides the score boundaries used to map a numeric
// score to a human-readable verdict label. When nil the built-in defaults
// (Very Weak ≤ 20, Weak ≤ 40, Okay ≤ 60, Strong ≤ 80, Very Strong > 80)
// are used. See [VerdictThresholds] for field details.
VerdictThresholds *VerdictThresholds
// RedactSensitive, when true, masks potential password substrings in
// issue messages (e.g., "Contains common word: '***'"). This prevents
// sensitive substrings from being inadvertently logged or persisted.
// Default: false (full messages returned).
RedactSensitive bool
}
Config holds configuration options for password strength checking.
Use DefaultConfig to obtain a Config with recommended defaults, then override individual fields:
cfg := passcheck.DefaultConfig()
cfg.MinLength = 8
cfg.RequireSymbol = false
result, err := passcheck.CheckWithConfig("mypassword", cfg)
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the recommended configuration with sensible defaults for general-purpose password validation.
Example ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
fmt.Printf("MinLength: %d\n", cfg.MinLength)
fmt.Printf("RequireUpper: %v\n", cfg.RequireUpper)
fmt.Printf("MaxRepeats: %d\n", cfg.MaxRepeats)
}
Output: MinLength: 12 RequireUpper: true MaxRepeats: 3
func EnterpriseConfig ¶ added in v1.2.0
func EnterpriseConfig() Config
EnterpriseConfig returns a strict configuration for high-security enterprise environments.
This preset implements maximum security controls suitable for organizations with stringent security requirements such as government agencies, healthcare providers, and financial institutions.
Key characteristics:
- Minimum 14 characters (enhanced security)
- Requires all character types (uppercase, lowercase, digits, symbols)
- Maximum 2 consecutive repeated characters (stricter than standard)
- Aggressive pattern detection (minimum length 3)
- Shows up to 10 issues for comprehensive feedback
- Dictionary checking enabled
Suitable for:
- Government systems
- Healthcare applications (HIPAA compliance)
- Financial services
- High-security corporate environments
- Systems handling sensitive data
Recommendation: Combine with ContextWords for maximum security by preventing passwords that contain usernames, email addresses, or company names.
Example:
cfg := passcheck.EnterpriseConfig()
cfg.ContextWords = []string{"username", "user@company.com", "CompanyName"}
result, _ := passcheck.CheckWithConfig("MyC0mplex!P@ssw0rd2024", cfg)
// Maximum security for high-risk environments
Example ¶
ExampleEnterpriseConfig demonstrates strict enterprise configuration.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.EnterpriseConfig()
// Enterprise requires maximum security
result, _ := passcheck.CheckWithConfig("MyC0mplex!Enterpr1se@2024", cfg)
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 100 Verdict: Very Strong
func NISTConfig ¶ added in v1.2.0
func NISTConfig() Config
NISTConfig returns a configuration compliant with NIST SP 800-63B Digital Identity Guidelines.
NIST emphasizes password length over complexity, rejecting traditional composition rules (required character types) in favor of checking against breach databases and common passwords.
Key characteristics:
- Minimum 8 characters (NIST minimum requirement)
- No composition rules (no required uppercase, lowercase, digits, or symbols)
- No character restrictions (allows maximum user flexibility)
- Dictionary checking enabled to prevent common passwords
- Pattern detection effectively disabled (PatternMinLength set to 99)
Suitable for:
- General-purpose applications
- Consumer-facing services
- Applications prioritizing user experience
Reference: NIST SP 800-63B Section 5.1.1 https://pages.nist.gov/800-63-3/sp800-63b.html
Example:
cfg := passcheck.NISTConfig()
result, _ := passcheck.CheckWithConfig("MySecret2024", cfg)
// Accepts 8+ character passwords without composition requirements
Example ¶
ExampleNISTConfig demonstrates NIST SP 800-63B compliant configuration.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.NISTConfig()
// NIST focuses on length, not composition
result, _ := passcheck.CheckWithConfig("MySecret2024", cfg)
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 44 Verdict: Okay
Example (WithContext) ¶
ExampleNISTConfig_withContext demonstrates combining NIST preset with context checking.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.NISTConfig()
cfg.ContextWords = []string{"john", "john.doe@acme.com"}
// NIST + context-aware checking (password has no context-word matches)
result, _ := passcheck.CheckWithConfig("MySecret2024", cfg)
fmt.Printf("Has issues: %v\n", len(result.Issues) > 0)
}
Output: Has issues: true
func OWASPConfig ¶ added in v1.2.0
func OWASPConfig() Config
OWASPConfig returns a configuration following OWASP password recommendations for web applications.
OWASP provides balanced guidance that prioritizes both security and usability, recommending longer passwords with reasonable complexity requirements.
Key characteristics:
- Minimum 10 characters (OWASP recommendation)
- Requires uppercase, lowercase, and digits
- Symbols optional (improves usability while maintaining security)
- Maximum 3 consecutive repeated characters
- Pattern detection enabled
- Dictionary checking enabled
Suitable for:
- Web applications
- SaaS platforms
- API services
- General business applications
Reference: OWASP Authentication Cheat Sheet https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
Example:
cfg := passcheck.OWASPConfig()
result, _ := passcheck.CheckWithConfig("MyPassword2024", cfg)
// Balanced security and usability for web applications
Example ¶
ExampleOWASPConfig demonstrates OWASP recommended configuration.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.OWASPConfig()
// OWASP balances security and usability
result, _ := passcheck.CheckWithConfig("MyPassword2024", cfg)
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 25 Verdict: Weak
func PCIDSSConfig ¶ added in v1.2.0
func PCIDSSConfig() Config
PCIDSSConfig returns a configuration compliant with PCI-DSS v4.0 password requirements for payment card industry applications.
PCI-DSS requires strict password complexity to protect payment card data. This preset enforces all composition rules and pattern detection.
Key characteristics:
- Minimum 12 characters (PCI-DSS Requirement 8.3.6)
- Requires all character types (uppercase, lowercase, digits, symbols)
- Maximum 3 consecutive repeated characters
- Pattern detection enabled (keyboard walks, sequences)
- Dictionary checking enabled
Suitable for:
- Payment processing systems
- Financial applications
- E-commerce platforms
- Any application handling payment card data
Note: PCI-DSS also requires password expiration (90 days) and password history (4 previous passwords), which are policy requirements that must be enforced separately from password strength checking.
Reference: PCI-DSS v4.0 Requirement 8.3.6 https://www.pcisecuritystandards.org/
Example:
cfg := passcheck.PCIDSSConfig()
result, _ := passcheck.CheckWithConfig("MyP@ssw0rd2024!", cfg)
// Enforces strict complexity for PCI-DSS compliance
Example ¶
ExamplePCIDSSConfig demonstrates PCI-DSS v4.0 compliant configuration.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.PCIDSSConfig()
// PCI-DSS requires strict complexity
result, _ := passcheck.CheckWithConfig("MyC0mpl3x!P@ss2024", cfg)
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 93 Verdict: Very Strong
func UserFriendlyConfig ¶ added in v1.2.0
func UserFriendlyConfig() Config
UserFriendlyConfig returns a balanced configuration prioritizing user experience while maintaining reasonable security.
This preset is designed for consumer-facing applications where user experience is a priority, but basic security standards must still be met. It requires length and some complexity without being overly restrictive.
Key characteristics:
- Minimum 10 characters (reasonable length)
- Requires lowercase and digits (flexible composition)
- Uppercase and symbols optional (reduces user friction)
- Maximum 4 consecutive repeated characters (lenient)
- Relaxed pattern detection (minimum length 5)
- Shows only 3 issues (focused feedback)
- Dictionary checking enabled
Suitable for:
- Consumer applications
- Social media platforms
- Low-risk web services
- Internal tools
- Prototypes and MVPs
Note: While this preset prioritizes usability, consider upgrading to OWASPConfig() or stricter presets for production applications handling sensitive data.
Example:
cfg := passcheck.UserFriendlyConfig()
result, _ := passcheck.CheckWithConfig("mypassword2024", cfg)
// Balanced approach for consumer applications
Example ¶
ExampleUserFriendlyConfig demonstrates user-friendly configuration.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.UserFriendlyConfig()
// User-friendly allows more flexibility
result, _ := passcheck.CheckWithConfig("mypassword2024", cfg)
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 32 Verdict: Weak
func (Config) Check ¶ added in v1.3.0
Check implements the Checker interface for a given configuration.
func (Config) Validate ¶
Validate checks the configuration for invalid values and returns an error describing the first problem found.
Callers can use errors.Is(err, passcheck.ErrInvalidConfig) to identify validation failures.
Example ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.Config{MinLength: 0} // invalid
err := cfg.Validate()
fmt.Println(err)
}
Output: passcheck: invalid configuration: MinLength must be >= 1, got 0
type EntropyMode ¶ added in v1.3.0
type EntropyMode string
EntropyMode specifies the entropy calculation method.
const ( // EntropyModeSimple uses the basic character-pool × length formula. // This mode dramatically overestimates strength for patterned passwords // (e.g. "Password123!" scores ~55 bits). Prefer EntropyModeAdvanced. EntropyModeSimple EntropyMode = "simple" // EntropyModeAdvanced reduces entropy for detected patterns (keyboard // walks, sequences, repeated blocks) to provide more accurate strength // estimates for patterned passwords. EntropyModeAdvanced EntropyMode = "advanced" // EntropyModePatternAware includes full pattern analysis plus Markov-chain // analysis for character transition probabilities, providing the most // accurate entropy estimates. EntropyModePatternAware EntropyMode = "pattern-aware" )
type HIBPCheckResult ¶ added in v1.2.0
HIBPCheckResult is a pre-computed result from an HIBP (Have I Been Pwned) lookup. When Config.HIBPResult is set, the library uses it instead of calling HIBPChecker.
type IncrementalDelta ¶ added in v1.2.0
type IncrementalDelta struct {
// ScoreChanged is true if the score differs from the previous result.
ScoreChanged bool
// IssuesChanged is true if the issues list (codes or messages) differs from the previous result.
IssuesChanged bool
// SuggestionsChanged is true if the suggestions list differs from the previous result.
SuggestionsChanged bool
}
IncrementalDelta describes what changed between a previous check result and the current one. Use it to avoid redundant UI updates when using CheckIncrementalWithConfig.
type Issue ¶ added in v1.2.0
type Issue struct {
Code string `json:"code"` // Stable identifier (e.g. "RULE_TOO_SHORT", "DICT_COMMON_PASSWORD")
Message string `json:"message"` // Human-readable description
Category string `json:"category"` // "rule", "pattern", "dictionary"
Severity int `json:"severity"` // 1 (low) – 3 (high)
}
Issue represents a single finding from a password check.
Example (Codes) ¶
ExampleIssue_codes shows programmatic handling of issues by code (e.g. for i18n or custom UI).
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
result := passcheck.Check("short")
for _, iss := range result.Issues {
switch iss.Code {
case passcheck.CodeRuleTooShort:
fmt.Println("Rule: too short")
case passcheck.CodeDictCommonPassword:
fmt.Println("Dictionary: common password")
case passcheck.CodeHIBPBreached:
fmt.Println("Breach: found in HIBP")
default:
fmt.Printf("Other: %s\n", iss.Code)
}
}
}
Output: Rule: too short Other: RULE_NO_UPPER Other: RULE_NO_DIGIT Other: RULE_NO_SYMBOL
type PenaltyWeights ¶ added in v1.3.0
type PenaltyWeights struct {
// RuleViolation multiplies penalties for rule violations (length, charset, etc.).
// Default: 1.0 (PenaltyPerRule = 5 per violation).
RuleViolation float64
// PatternMatch multiplies penalties for pattern detections (keyboard walks, sequences).
// Default: 1.0 (PenaltyPerPattern = 10 per pattern).
PatternMatch float64
// DictionaryMatch multiplies penalties for dictionary matches (common passwords, words).
// Default: 1.0 (PenaltyPerDictMatch = 15 per match).
DictionaryMatch float64
// ContextMatch multiplies penalties for context-aware detections (username, email).
// Default: 1.0 (PenaltyPerContext = 20 per match).
ContextMatch float64
// HIBPBreach multiplies penalties for HIBP breach database matches.
// Default: 1.0 (PenaltyPerHIBP = 25 per breach).
HIBPBreach float64
// EntropyWeight multiplies the base score derived from entropy.
// Default: 1.0 (entropy contributes fully to base score).
// Values < 1.0 reduce entropy influence; values > 1.0 increase it.
EntropyWeight float64
}
PenaltyWeights allows customization of penalty multipliers and entropy weight for password strength scoring. All weights default to 1.0 when nil or when individual fields are zero.
Example: To double dictionary penalties and reduce entropy influence:
weights := &passcheck.PenaltyWeights{
DictionaryMatch: 2.0,
EntropyWeight: 0.5,
}
cfg.PenaltyWeights = weights
func (*PenaltyWeights) Validate ¶ added in v1.3.0
func (w *PenaltyWeights) Validate() error
Validate checks that all penalty weights are non-negative. Zero values are treated as defaults (1.0) during scoring.
type Result ¶
type Result struct {
// Score is the overall password strength score from 0 (weakest) to 100 (strongest).
Score int `json:"score"`
// Verdict is a human-readable strength label.
// One of: "Very Weak", "Weak", "Okay", "Strong", "Very Strong".
Verdict string `json:"verdict"`
// MeetsPolicy is true when the password satisfies all configured minimum
// requirements (length, character-set rules, repeat limits). A password can
// meet policy and still have a low score if it relies on common patterns or
// dictionary words; conversely, failing policy means at least one hard
// requirement (e.g. MinLength, RequireUpper) was not satisfied.
//
// Use this field to distinguish "did not meet minimum requirements" from
// "meets requirements but is a weak choice" — a distinction the Score alone
// cannot express.
MeetsPolicy bool `json:"meets_policy"`
// Issues is a deduplicated, priority-sorted list of structured problems
// found with the password. Use [Result.IssueMessages] for a []string of
// messages only (backward compatibility).
Issues []Issue `json:"issues"`
// Suggestions contains positive feedback about the password's
// strengths (e.g. "Good length", "No common patterns detected").
// Empty when the password has no notable strengths.
Suggestions []string `json:"suggestions"`
// Entropy is the estimated entropy of the password in bits.
Entropy float64 `json:"entropy"`
}
Result holds the outcome of a password strength check.
func Check ¶
Check evaluates the strength of a password using the default configuration and returns a Result.
This is a convenience wrapper around CheckWithConfig using DefaultConfig. It never returns an error because the default configuration is always valid.
Example ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
result := passcheck.Check("Xk9$mP2!vR7@nL4&wQzB")
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 100 Verdict: Very Strong
Example (Suggestions) ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
result := passcheck.Check("Xk9$mP2!vR7@nL4&wQzB")
fmt.Printf("Suggestions: %d\n", len(result.Suggestions))
for _, s := range result.Suggestions {
fmt.Println(s)
}
}
Output: Suggestions: 5 Good length (20 characters) Good character diversity (4 of 4 character types) No common patterns detected Not found in common password lists Good entropy (131 bits)
Example (Weak) ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
result := passcheck.Check("password")
fmt.Printf("Verdict: %s\n", result.Verdict)
fmt.Printf("Issues: %d\n", len(result.Issues))
}
Output: Verdict: Very Weak Issues: 5
func CheckBytes ¶
CheckBytes evaluates password strength from a mutable byte slice using the default configuration.
After converting the input to a string for analysis, the original byte slice is immediately zeroed to minimize the time plaintext resides in process memory. The caller should not reuse the slice after this call.
Prefer CheckBytes over Check when the password originates from a mutable source (e.g. an HTTP request body or a terminal read buffer).
Example ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
buf := []byte("Xk9$mP2!vR7@nL4&wQzB")
result := passcheck.CheckBytes(buf)
fmt.Printf("Score: %d\n", result.Score)
// buf is now zeroed.
allZero := true
for _, b := range buf {
if b != 0 {
allZero = false
}
}
fmt.Printf("Input zeroed: %v\n", allZero)
}
Output: Score: 100 Input zeroed: true
func CheckBytesWithConfig ¶
CheckBytesWithConfig evaluates password strength from a mutable byte slice using a custom configuration. The input is zeroed after analysis.
Returns an error if the configuration is invalid.
Example ¶
ExampleCheckBytesWithConfig runs CheckBytes with custom config and zeroes the buffer.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.MinLength = 8
cfg.RequireSymbol = false
buf := []byte("MyPass99")
result, err := passcheck.CheckBytesWithConfig(buf, cfg)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Score: %d\n", result.Score)
allZero := true
for _, b := range buf {
if b != 0 {
allZero = false
break
}
}
fmt.Printf("Input zeroed: %v\n", allZero)
}
Output: Score: 43 Input zeroed: true
func CheckIncremental ¶ added in v1.2.0
CheckIncremental evaluates the strength of a password using the default configuration and is intended for real-time feedback (e.g. strength meters).
A full check is always performed; previous is not used to skip work. When previous is nil, the returned result is equivalent to calling Check directly. Callers can compare the returned result with previous to detect changes. For delta information and custom config, use CheckIncrementalWithConfig.
When used on every keystroke, callers should debounce (e.g. 100–300 ms) to limit CPU usage and keep the UI responsive.
Example ¶
ExampleCheckIncremental demonstrates real-time feedback: pass the previous result so the UI can update only when the score or issues change. Debounce input (e.g. 100–300 ms) when calling on every keystroke.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
var lastResult *passcheck.Result
onPasswordChange := func(password string) {
result := passcheck.CheckIncremental(password, lastResult)
fmt.Printf("Score: %d, Verdict: %s\n", result.Score, result.Verdict)
lastResult = &result
}
onPasswordChange("a")
onPasswordChange("ab")
onPasswordChange("MyP@ssw0rd")
}
Output: Score: 0, Verdict: Very Weak Score: 0, Verdict: Very Weak Score: 20, Verdict: Very Weak
func CheckWithConfig ¶
CheckWithConfig evaluates the strength of a password using a custom configuration. It returns an error if the configuration is invalid.
It runs the password through multiple checks:
- Basic rules (length, character sets, repeated characters)
- Pattern detection (keyboard patterns, sequences, repeated blocks)
- Dictionary checks (common passwords, leetspeak variants)
- Entropy calculation
Issues are deduplicated, sorted by severity, and limited to cfg.MaxIssues. Positive suggestions are generated for the password's strengths.
Passwords longer than MaxPasswordLength runes are truncated before analysis to prevent excessive CPU usage.
Example ¶
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.MinLength = 6
cfg.RequireSymbol = false
result, err := passcheck.CheckWithConfig("Hello1", cfg)
if err != nil {
fmt.Println("config error:", err)
return
}
fmt.Printf("Score: %d\n", result.Score)
fmt.Printf("Verdict: %s\n", result.Verdict)
}
Output: Score: 8 Verdict: Very Weak
Example (ContextAware) ¶
ExampleCheckWithConfig_contextAware demonstrates context-aware password checking.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
// Configure with user-specific context
cfg := passcheck.DefaultConfig()
cfg.ContextWords = []string{
"john", // username
"john.doe@acme-corp.com", // email (auto-parsed)
"acmecorp", // company name
}
// This password contains the username "john"
result, _ := passcheck.CheckWithConfig("John123!", cfg)
// Check if context was detected
hasContextIssue := false
for _, iss := range result.Issues {
if iss.Category == "context" {
hasContextIssue = true
fmt.Println("Detected:", iss.Message)
break
}
}
fmt.Printf("Contains personal info: %v\n", hasContextIssue)
}
Output: Detected: Contains personal information: "john" Contains personal info: true
Example (ContextAwareEmail) ¶
ExampleCheckWithConfig_contextAwareEmail demonstrates email extraction.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.ContextWords = []string{"john.doe@example.com"}
// Password contains "doe" extracted from the email
result, _ := passcheck.CheckWithConfig("MyDoe2024!", cfg)
hasContextIssue := false
for _, iss := range result.Issues {
if iss.Category == "context" {
hasContextIssue = true
break
}
}
fmt.Printf("Detected email component: %v\n", hasContextIssue)
}
Output: Detected email component: true
Example (ContextAwareLeetspeak) ¶
ExampleCheckWithConfig_contextAwareLeetspeak demonstrates leetspeak detection.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.ContextWords = []string{"admin"}
// Password contains "@dm1n" (leetspeak variant of "admin")
result, _ := passcheck.CheckWithConfig("@dm1n2024!", cfg)
hasContextIssue := false
for _, iss := range result.Issues {
if iss.Category == "context" {
hasContextIssue = true
break
}
}
fmt.Printf("Detected leetspeak variant: %v\n", hasContextIssue)
}
Output: Detected leetspeak variant: true
Example (CustomPasswords) ¶
ExampleCheckWithConfig_customPasswords demonstrates blocking additional passwords via CustomPasswords.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.MinLength = 6
cfg.RequireSymbol = false
cfg.CustomPasswords = []string{"internal2024", "company_secret"}
result, _ := passcheck.CheckWithConfig("internal2024", cfg)
fmt.Printf("Blocked by CustomPasswords: %v\n", len(result.Issues) > 0)
}
Output: Blocked by CustomPasswords: true
Example (CustomWords) ¶
ExampleCheckWithConfig_customWords demonstrates detecting additional substrings via CustomWords.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.MinLength = 8
cfg.RequireSymbol = false
cfg.CustomWords = []string{"acme", "widget"}
result, _ := passcheck.CheckWithConfig("Xk9$mP2!AcmeR7", cfg)
hasCustomWord := false
for _, iss := range result.Issues {
if iss.Code == passcheck.CodeDictCommonWord || iss.Code == passcheck.CodeDictCommonWordSub {
hasCustomWord = true
break
}
}
fmt.Printf("Custom word detected: %v\n", hasCustomWord)
}
Output: Custom word detected: true
Example (DisableLeet) ¶
ExampleCheckWithConfig_disableLeet demonstrates disabling leetspeak normalization. When DisableLeet is true, only the plain password is checked against dictionaries.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.MinLength = 6
cfg.RequireSymbol = false
cfg.DisableLeet = true
// "p@ssw0rd" with leet enabled would match "password"; with DisableLeet it does not.
result, _ := passcheck.CheckWithConfig("p@ssw0rd", cfg)
leetIssue := false
for _, iss := range result.Issues {
if iss.Code == passcheck.CodeDictLeetVariant {
leetIssue = true
break
}
}
fmt.Printf("Leet variant reported: %v\n", leetIssue)
}
Output: Leet variant reported: false
Example (Hibp) ¶
ExampleCheckWithConfig_hibp shows breach checking using the hibp package (mock for deterministic output).
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
"github.com/rafaelsanzio/passcheck/hibp"
)
func main() {
cfg := passcheck.DefaultConfig()
cfg.MinLength = 6
cfg.RequireSymbol = false
// In production use hibp.NewClient(); here a mock avoids network calls.
cfg.HIBPChecker = &hibp.MockClient{
CheckFunc: func(_ string) (bool, int, error) { return true, 42, nil },
}
result, err := passcheck.CheckWithConfig("aB3!xy", cfg)
if err != nil {
fmt.Println("Error:", err)
return
}
for _, iss := range result.Issues {
if iss.Code == passcheck.CodeHIBPBreached {
fmt.Println("Breach reported: true")
return
}
}
fmt.Println("Breach reported: false")
}
Output: Breach reported: true
Example (InvalidConfig) ¶
ExampleCheckWithConfig_invalidConfig shows that an invalid config returns an error.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
cfg := passcheck.Config{MinLength: -1}
result, err := passcheck.CheckWithConfig("any", cfg)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Score: %d\n", result.Score)
}
Output: Error: passcheck: invalid configuration: MinLength must be >= 1, got -1
func (Result) IssueMessages ¶ added in v1.2.0
IssueMessages returns the human-readable message for each issue, in order. Use this when migrating from the previous Result.Issues []string API.
Example ¶
ExampleResult_IssueMessages shows backward-compatible message slice from Result.Issues.
package main
import (
"fmt"
"github.com/rafaelsanzio/passcheck"
)
func main() {
// Use a password that fails only length so we get one predictable issue.
result := passcheck.Check("Xk9$m")
messages := result.IssueMessages()
fmt.Printf("Issue count: %d\n", len(messages))
for _, msg := range messages {
fmt.Println(msg)
}
}
Output: Issue count: 1 Password is too short (5 chars, minimum 12)
type VerdictThresholds ¶ added in v1.3.0
type VerdictThresholds struct {
// VeryWeakMax is the highest score that produces the "Very Weak" verdict.
// Default: 20.
VeryWeakMax int
// WeakMax is the highest score that produces the "Weak" verdict.
// Must be > VeryWeakMax. Default: 40.
WeakMax int
// OkayMax is the highest score that produces the "Okay" verdict.
// Must be > WeakMax. Default: 60.
OkayMax int
// StrongMax is the highest score that produces the "Strong" verdict.
// Scores above StrongMax produce "Very Strong".
// Must be > OkayMax and < 100. Default: 80.
StrongMax int
}
VerdictThresholds defines the score boundaries that map a numeric score (0–100) to a human-readable verdict label. All four fields must be set as a strictly increasing sequence with VeryWeakMax ≥ 1 and StrongMax ≤ 99.
Zero-value (nil pointer) means use the built-in defaults:
VeryWeakMax = 20 (scores 0–20 → "Very Weak")
WeakMax = 40 (scores 21–40 → "Weak")
OkayMax = 60 (scores 41–60 → "Okay")
StrongMax = 80 (scores 61–80 → "Strong")
> 80 → "Very Strong"
Example — stricter thresholds that push users toward stronger passwords:
cfg.VerdictThresholds = &passcheck.VerdictThresholds{
VeryWeakMax: 30,
WeakMax: 50,
OkayMax: 70,
StrongMax: 85,
}
func (*VerdictThresholds) Validate ¶ added in v1.3.0
func (t *VerdictThresholds) Validate() error
Validate checks that the threshold values form a valid strictly increasing sequence within [1, 99].
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
passcheck
command
Command passcheck is a CLI tool for checking password strength.
|
Command passcheck is a CLI tool for checking password strength. |
|
examples
|
|
|
basic
command
Command basic demonstrates the core passcheck library API.
|
Command basic demonstrates the core passcheck library API. |
|
config
command
Command config demonstrates using passcheck with custom configuration.
|
Command config demonstrates using passcheck with custom configuration. |
|
context
command
Command context demonstrates context-aware password checking: rejecting passwords that contain the username, email, or other user-specific terms.
|
Command context demonstrates context-aware password checking: rejecting passwords that contain the username, email, or other user-specific terms. |
|
hibp
command
This example shows how to integrate the HIBP (Have I Been Pwned) breach database with passcheck.
|
This example shows how to integrate the HIBP (Have I Been Pwned) breach database with passcheck. |
|
middleware
command
Command middleware demonstrates the passcheck HTTP middleware for protecting password-related endpoints (registration, password change, etc.).
|
Command middleware demonstrates the passcheck HTTP middleware for protecting password-related endpoints (registration, password change, etc.). |
|
presets
command
Command presets demonstrates policy presets: NIST, OWASP, PCI-DSS, etc.
|
Command presets demonstrates policy presets: NIST, OWASP, PCI-DSS, etc. |
|
webserver
command
Command webserver demonstrates passcheck as an HTTP password-checking service.
|
Command webserver demonstrates passcheck as an HTTP password-checking service. |
|
Package hibp provides a client for the Have I Been Pwned (HIBP) Pwned Passwords API using k-anonymity.
|
Package hibp provides a client for the Have I Been Pwned (HIBP) Pwned Passwords API using k-anonymity. |
|
internal
|
|
|
context
Package context implements context-aware password checking.
|
Package context implements context-aware password checking. |
|
dictionary
Package dictionary implements password dictionary checks.
|
Package dictionary implements password dictionary checks. |
|
entropy
Package entropy implements password entropy calculation.
|
Package entropy implements password entropy calculation. |
|
feedback
Package feedback refines raw analysis issues into a curated, user-friendly list of actionable messages.
|
Package feedback refines raw analysis issues into a curated, user-friendly list of actionable messages. |
|
hibpcheck
Package hibpcheck integrates Have I Been Pwned breach checks into the passcheck scoring pipeline.
|
Package hibpcheck integrates Have I Been Pwned breach checks into the passcheck scoring pipeline. |
|
issue
Package issue defines a structured representation of password check findings used across rules, patterns, dictionary, and feedback packages.
|
Package issue defines a structured representation of password check findings used across rules, patterns, dictionary, and feedback packages. |
|
leet
Package leet provides leetspeak normalization utilities shared by the pattern-detection and dictionary-lookup packages.
|
Package leet provides leetspeak normalization utilities shared by the pattern-detection and dictionary-lookup packages. |
|
passphrase
Package passphrase implements word-based entropy calculation for passphrases.
|
Package passphrase implements word-based entropy calculation for passphrases. |
|
patterns
Package patterns implements password pattern detection.
|
Package patterns implements password pattern detection. |
|
rules
Package rules implements basic password policy checks.
|
Package rules implements basic password policy checks. |
|
safemem
Package safemem provides utilities for handling sensitive data in memory.
|
Package safemem provides utilities for handling sensitive data in memory. |
|
scoring
Package scoring implements the password strength scoring algorithm.
|
Package scoring implements the password strength scoring algorithm. |
|
Package middleware provides HTTP middleware for password strength validation using passcheck.
|
Package middleware provides HTTP middleware for password strength validation using passcheck. |
|
Package main is the WebAssembly entry point for passcheck.
|
Package main is the WebAssembly entry point for passcheck. |