passcheck

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2026 License: MIT Imports: 15 Imported by: 0

README

passcheck

CI codecov Go Reference Go Report Card Go Version

A comprehensive, zero-dependency Go library for password strength checking.

Passcheck evaluates passwords against multiple criteria — basic rules, pattern detection, dictionary checks, and entropy calculation — returning a scored result with a verdict and structured, actionable feedback.

Features

  • Score & Verdict — 0-100 score mapped to Very Weak / Weak / Okay / Strong / Very Strong
  • Structured Issues — typed Issue (Code, Message, Category, Severity) for programmatic handling
  • Pattern Detection — keyboard walks, sequences, repeated blocks, leetspeak
  • Dictionary Checks — ~950 common passwords, ~490 common words, leet variants
  • Context-Aware Detection — reject passwords containing username, email, or custom terms
  • Policy Presets — NIST, PCI-DSS, OWASP, Enterprise, UserFriendly in one call
  • Breach Database (HIBP) — optional Have I Been Pwned integration via k-anonymity
  • HTTP Middleware — drop-in for net/http; Gin, Echo, Fiber adapters as independent submodules
  • Entropy Modes — Simple, Advanced (pattern-aware), Pattern-Aware (Markov-chain)
  • Passphrase Support — word-based entropy with diceware model
  • Configurable Weights — customize penalty multipliers and entropy weight
  • Real-Time FeedbackCheckIncremental with delta for live strength meters
  • Secure MemoryCheckBytes zeros input after analysis
  • CLI Tool — colored output, JSON mode, verbose mode
  • WebAssemblyWASM build for client-side validation; includes a TypeScript/Vite web app
  • Zero Dependencies — root library uses stdlib only

For upgrade instructions see MIGRATION.md.

Installation

# Library
go get github.com/rafaelsanzio/passcheck

# CLI
go install github.com/rafaelsanzio/passcheck/cmd/passcheck@latest

Or build from source:

git clone https://github.com/rafaelsanzio/passcheck.git
cd passcheck
make build       # builds to bin/passcheck
make install     # installs to $GOPATH/bin
make test        # run tests
make wasm        # build WASM and copy to wasm/web/public/

Quick Start

package main

import (
    "fmt"
    "github.com/rafaelsanzio/passcheck"
)

func main() {
    result := passcheck.Check("MyP@ssw0rd123!")

    fmt.Printf("Score:   %d/100\n", result.Score)
    fmt.Printf("Verdict: %s\n", result.Verdict)
    fmt.Printf("Entropy: %.1f bits\n", result.Entropy)

    for _, iss := range result.Issues {
        fmt.Printf("  - %s\n", iss.Message)
    }
    for _, s := range result.Suggestions {
        fmt.Printf("  + %s\n", s)
    }
}

CLI Usage

passcheck "MyP@ssw0rd123!"          # basic check
passcheck "qwerty" --json           # JSON output
passcheck "password" --verbose      # all issues and extra details
passcheck "aB3!xY" --min-length=6   # custom minimum length
passcheck -- "-mypassword"          # password starting with a dash
passcheck --help
Flag Short Description
--json Output as JSON
--verbose -v Show all issues and extra details
--no-color Disable ANSI colors (NO_COLOR env also works)
--min-length=N Override minimum password length (default: 12)
--version Show version
--help -h Show help

API Reference

Core Functions
func Check(password string) Result
func CheckWithConfig(password string, cfg Config) (Result, error)
func CheckBytes(password []byte) Result
func CheckBytesWithConfig(password []byte, cfg Config) (Result, error)
func CheckIncremental(password string, previous *Result) Result
func CheckIncrementalWithConfig(password string, previous *Result, cfg Config) (Result, IncrementalDelta, error)
Result and Issue
type Result struct {
    Score       int      // 0–100
    Verdict     string   // "Very Weak" … "Very Strong"
    MeetsPolicy bool     // all configured minimums satisfied
    Issues      []Issue  // prioritized, deduplicated problems
    Suggestions []string // positive feedback
    Entropy     float64  // estimated bits
}

type Issue struct {
    Code     string // e.g. "RULE_TOO_SHORT", "DICT_COMMON_PASSWORD", "HIBP_BREACHED"
    Message  string
    Category string // "rule", "pattern", "dictionary", "context", "breach"
    Severity int    // 1 (low) – 3 (high)
}

type IncrementalDelta struct {
    ScoreChanged       bool
    IssuesChanged      bool
    SuggestionsChanged bool
}

Use result.IssueMessages() for a []string of messages (backward compatibility).

Verdicts
Score Verdict
0–20 Very Weak
21–40 Weak
41–60 Okay
61–80 Strong
81–100 Very Strong

Configuration

cfg := passcheck.DefaultConfig()
cfg.MinLength = 8
cfg.RequireSymbol = false
cfg.MaxRepeats = 4

result, err := passcheck.CheckWithConfig("mypassword", cfg)

Key fields (see pkg.go.dev for the full reference):

Field Default Description
MinLength 12 Minimum runes required
RequireUpper true Require uppercase letter
RequireLower true Require lowercase letter
RequireDigit true Require numeric digit
RequireSymbol true Require symbol character
MaxRepeats 3 Max consecutive identical characters
ContextWords nil User-specific terms (username, email) to reject
HIBPChecker nil Optional breach check; see hibp/
PassphraseMode false Word-based entropy and scoring for passphrases
EntropyMode "simple" "simple", "advanced", or "pattern-aware"
PenaltyWeights nil Custom penalty multipliers; see docs/WEIGHT_TUNING.md
RedactSensitive false Mask password substrings in issue messages
Policy Presets
Preset Use case Min length Complexity
NISTConfig() NIST SP 800-63B (length over rules) 8 None
UserFriendlyConfig() Consumer apps, low friction 10 Lower + digit
OWASPConfig() Web apps, SaaS 10 Upper + lower + digit
PCIDSSConfig() PCI-DSS v4.0 12 Full
EnterpriseConfig() High-security / enterprise 14 Full, strict
cfg := passcheck.NISTConfig()
result, _ := passcheck.CheckWithConfig("correct-horse-battery-staple", cfg)

Presets can be further customized: cfg := passcheck.NISTConfig(); cfg.CustomPasswords = myList.

Custom Blocklists & Context-Aware Detection
cfg := passcheck.DefaultConfig()
cfg.CustomPasswords = []string{"CompanyName2024", "InternalProject"}
cfg.CustomWords     = []string{"acmecorp", "projectx"}
cfg.ContextWords    = []string{"john", "john.doe@acme.com"} // username / email

ContextWords matching is case-insensitive, supports substrings and leetspeak variants. Email addresses are split into local and domain parts. Words shorter than 3 characters are ignored.

Breach Database (HIBP)

Only the first 5 characters of the SHA-1 hash are sent to the API — the full password is never transmitted (k-anonymity).

import (
    "github.com/rafaelsanzio/passcheck"
    "github.com/rafaelsanzio/passcheck/hibp"
)

cfg := passcheck.DefaultConfig()
client := hibp.NewClient()
client.Cache = hibp.NewMemoryCacheWithTTL(256, hibp.DefaultCacheTTL)
cfg.HIBPChecker = client

result, _ := passcheck.CheckWithConfig(password, cfg)

On network errors the breach check is skipped and the rest of the result is returned (graceful degradation). For WASM builds, pass a pre-computed result via Config.HIBPResult. See examples/hibp and hibp/.

Passphrase Mode
cfg := passcheck.DefaultConfig()
cfg.PassphraseMode = true
cfg.MinWords     = 4     // minimum distinct words
cfg.WordDictSize = 7776  // diceware dictionary size

result, _ := passcheck.CheckWithConfig("correct-horse-battery-staple", cfg)
// word-based entropy: 4 × log₂(7776) ≈ 51 bits
Advanced Entropy & Scoring Weights
cfg.EntropyMode = passcheck.EntropyModeAdvanced      // pattern-aware reduction
cfg.EntropyMode = passcheck.EntropyModePatternAware  // + Markov-chain analysis

cfg.PenaltyWeights = &passcheck.PenaltyWeights{
    DictionaryMatch: 2.0,
    PatternMatch:    1.5,
    EntropyWeight:   0.8,
}

See docs/WEIGHT_TUNING.md for tuning guidance.

Real-Time Feedback
var last *passcheck.Result
func onPasswordChange(password string) {
    result, delta, _ := passcheck.CheckIncrementalWithConfig(password, last, cfg)
    if delta.ScoreChanged || delta.IssuesChanged {
        updateMeter(result.Score, result.Issues)
    }
    last = &result
}

Debounce calls on every keystroke (100–300 ms) to limit CPU usage.

WebAssembly (client-side)

Build with make wasm, then run make serve-wasm to start the TypeScript/Vite dev server. The WASM build exposes passcheckCheck, passcheckCheckWithConfig, and incremental variants as global JS functions. A modern web app with dark mode, Web Workers, and full configuration UI is included.

HTTP Middleware
import "github.com/rafaelsanzio/passcheck/middleware"

mux.Handle("/register", middleware.HTTP(middleware.Config{
    MinScore:      60,
    PasswordField: "password",
}, registerHandler))

Framework adapters (independent submodules — add only what you need):

go get github.com/rafaelsanzio/passcheck/middleware/gin
go get github.com/rafaelsanzio/passcheck/middleware/echo
go get github.com/rafaelsanzio/passcheck/middleware/fiber
// Gin
r.POST("/register", passcheckgin.Gin(middleware.Config{MinScore: 60}), handler)

// Echo
e.POST("/register", handler, passcheckecho.Echo(middleware.Config{MinScore: 60}))

// Fiber
app.Post("/register", passcheckfiber.Fiber(middleware.Config{MinScore: 60}), handler)

Chi uses the standard middleware.HTTP wrapper — no extra dependency needed. See examples/middleware.

Security Best Practices

  1. Do not log Result.Issues raw — messages may contain password substrings. Log only Code, or set Config.RedactSensitive = true.
  2. Prefer CheckBytes — use when passwords are available as []byte; the buffer is zeroed immediately after analysis.
  3. Enable ConstantTimeMode — mitigates timing side channels in dictionary lookups for high-assurance scenarios.
  4. Run security toolingmake security runs govulncheck and gosec locally.

Architecture

passcheck/
├── passcheck.go        # Public API: Check, CheckIncremental, CheckWithConfig, CheckBytes
├── config.go           # Config struct, DefaultConfig, Validate
├── presets.go          # NIST, PCI-DSS, OWASP, Enterprise, UserFriendly presets
├── hibp/               # Optional HIBP breach API client (k-anonymity)
├── middleware/         # HTTP middleware (net/http, Chi); gin/echo/fiber as submodules
├── internal/
│   ├── rules/          # Basic rules: length, charsets, whitespace, repeats
│   ├── patterns/       # Pattern detection: keyboard, sequence, blocks, substitution, dates
│   ├── dictionary/     # Dictionary checks: common passwords, common words
│   ├── entropy/        # Entropy calculation (simple, advanced, pattern-aware modes)
│   ├── passphrase/     # Passphrase detection and word-based entropy
│   ├── scoring/        # Weighted scoring algorithm
│   ├── feedback/       # Issue dedup, priority sort, positive feedback
│   ├── context/        # Context-aware detection
│   ├── hibpcheck/      # HIBP breach result integration
│   ├── issue/          # Shared issue type definitions and constants
│   ├── leet/           # Leetspeak normalisation utilities
│   └── safemem/        # Secure memory zeroing and constant-time comparisons
├── cmd/passcheck/      # CLI tool
├── examples/           # Usage examples
└── Makefile            # Build, test, cross-compile

Performance

Benchmarks on Apple Silicon (M-series), Go 1.24+:

Input Time Allocs Bytes
Empty password ~420 ns 7 184 B
Short (6 chars) ~1.0 µs 20 923 B
Common password ~4.1 µs 38 1.6 KB
Medium (12 chars) ~12 µs 52 872 B
Strong (20 chars) ~23 µs 95 1.3 KB
Long (100 chars) ~137 µs 598 10 KB
Very long (1000 chars) ~1.4 ms 5999 89 KB

All functions are safe for concurrent use. Run make bench to benchmark locally.

Development

make test       # run tests (core module)
make test-all   # run tests for core and middleware submodules
make cover      # coverage report
make bench      # run benchmarks
make lint-ci    # golangci-lint (comprehensive)
make build      # build CLI
make wasm       # build WASM binary
make cross      # cross-compile for all platforms
make clean      # remove build artifacts
make help       # show all targets

CI runs lint, go test -race, govulncheck, and coverage upload on every push/PR. A WASM workflow reports bundle sizes; a nightly Fuzz workflow runs fuzz targets.

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Write tests for new functionality
  4. Ensure tests pass (make test) and lint passes (make lint-ci)
  5. Open a pull request

Please follow existing code style: Go conventions, table-driven tests, godoc comments.

License

This project is available under the MIT License. See LICENSE for details.

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

Constants

View Source
const (
	VerdictVeryWeak   = "Very Weak"
	VerdictWeak       = "Weak"
	VerdictOkay       = "Okay"
	VerdictStrong     = "Strong"
	VerdictVeryStrong = "Very Strong"
)

Verdict constants represent the password strength levels.

View Source
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").

View Source
const MaxCustomPasswordsSize = 100_000

MaxCustomPasswordsSize is the maximum number of entries allowed in Config.CustomPasswords. See MaxCustomWordsSize for the rationale.

View Source
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.

View Source
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

View Source
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

type Checker interface {
	Check(password string) (Result, error)
}

Checker performs password strength checks.

func NewChecker added in v1.3.0

func NewChecker(cfg Config) (Checker, error)

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

func (c Config) Check(password string) (Result, error)

Check implements the Checker interface for a given configuration.

func (Config) Validate

func (c Config) Validate() error

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

type HIBPCheckResult struct {
	Breached bool
	Count    int
}

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

func Check(password string) Result

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

func CheckBytes(password []byte) Result

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

func CheckBytesWithConfig(password []byte, cfg Config) (Result, error)

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

func CheckIncremental(password string, previous *Result) Result

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

func CheckWithConfig(password string, cfg Config) (Result, error)

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

func (r Result) IssueMessages() []string

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL