Skip to main content

A modern password validation library powered by zxcvbn with policy enforcement and CLI support.

Project description

FortifyPass - Real-World Password Security for Developers

Python Version License Development Status zxcvbn powered

Every day, companies get breached because someone picked 123456 or password. Users think complexity is optional. Developers hope nobody notices. Security teams cry. FortifyPass guarantees your users pick passwords that actually protect them - real-world strong, actinable, and verifiable

Why Everyone will Agree

  • Weak passwords are everywhere - your users do it, your friends do it, your systems get hacked.
  • Length or character checks don't cut it - hackers exploit patterns, common words, and sequences.
  • Feedback is terrible in almost every validator - users ignore it, reuse passwords, and risk breaches skyrocket.

Your system is only as secure as the weakest password. FortifyPass fixes that.

Why You'll Want This

FortifyPass doesn't just validate - it forces strong passwords and explains why:

  • Real-world strength scoring powered by zxcvbn
  • Policy enforcement: uppercase, lowercase, digits, special, characters, banned words
  • Actionable feedback: your users actually learn to make secure passwords
  • Plug-and-Play: works in Python, CLI, scripts, and future APIs

Quick Demo

fortifypass
FortifyPass version 0.2.1      
Type .exit() to quit      
      
Enter password: ··············       Does not meet policy requirements      
   Policy requirement: add at least one special character       Too weak against common attacks      
      
Score: 0/4      
Strength: Very Weak      
      
Feedback:      
   Sequences like "abc" or "6543" are easy to guess.      
   Avoid sequences.      
      
------------------------------------------------------------       
      
Enter password: ·················       Too weak against common attacks      
      
Score: 2/4      
Strength: Moderate      
      
Feedback:      
   This is similar to a commonly used password.      
   Add another word or two. Uncommon words are better.      
   Capitalization doesn't help very much.      
  • Predictable substitutions like '@' instead of 'a' don't help very much.      
      
------------------------------------------------------------      
      
Enter password: ······························       Strong against common attacks      
      
Score: 4/4      
Strength: Very Strong      
      
------------------------------------------------------------
      
Enter password: ·······     
Goodbye.

Non-Interactive / Pipe mode

echo "Str0ngP@ssw0rd!" | fortifypass
{
  "valid": true,
  "policy_passed": true,
  "strength_passed": true,
  "errors": [],
  "score": 4,
  "label": "Very Strong",
  "feedback": []
}

Installation

Using pip (standard)

pip install fortifypass

Using uv (fast alternative)

Install uv (if not already) or see official installation guides

pip install uv
uv add fortifypass

if you're working in a project:

uv sync

From Source

git clone https://github.com/botshelo-mere/fortifypass.git
cd fortifypass

# Using pip
pip install .

# Using uv
uv sync

Dev / Testing

# Using pip
pip install "fortifypass[dev]"

# Using uv
uv sync --extra dev

Quick Start

Library Usage

from fortifypass import PasswordValidator

# Default policy: 12–64 chars, upper, lower, digit, special required
validator = PasswordValidator()

# --- validate() → (bool, list[str]) ---
valid, errors = validator.validate("Str0ngP@ssw0rd!")
print(valid)   # True
print(errors)  # []

valid, errors = validator.validate("weak")
print(valid)   # False
print(errors)  # ['Password must be at least 12 characters long', ...]

# --- estimate_strength() → dict ---
strength = validator.estimate_strength("Str0ngP@ssw0rd!")
print(strength["score"])    # 3
print(strength["label"])    # 'Strong'
print(strength["feedback"]) # []

# --- evaluate() — combines both in one call ---
result = validator.evaluate("Str0ngP@ssw0rd!")
# {
#   "valid": True,
#   "errors": [],
#   "score": 3,
#   "label": "Strong",
#   "feedback": []
# }

Custom Policy

validator = PasswordValidator(
    min_length=8,
    max_length=32,
    require_uppercase=True,
    require_lowercase=True,
    require_digit=True,
    require_special=True,
    special_chars="!@#$%",
    allow_spaces=False,
    banned_words=["password", "admin", "secret"]
)

valid, errors = validator.validate("Admin123!")
# valid=False → "Contains a banned word"

Configuration Reference

Parameter Type Default Description
min_length int 12 Minimum password length
max_length int 64 Maximum password length
require_uppercase bool True Require at least one uppercase letter
require_lowercase bool True Require at least one lowercase letter
require_digit bool True Require at least one digit
require_special bool True Require at least one special character
special_chars str "!@#$%^&*" Set of allowed special characters
allow_spaces bool False Whether whitespace is permitted
banned_words list[str] | None None Case-insensitive list of forbidden words
min_score int 0 Minimum zxcvbn score (0-4)

ValueError is raised on construction if min_length <= 0 or max_length < min_length.
ValueError is raised on construction if min_score < 0 or not 0 <= min_score <= 4.


API Reference

PasswordValidator.validate(pwd: str) → Tuple[bool, List[str]]

Runs all configured policy rules against the password.

  • Returns (True, []) if all rules pass.
  • Returns (False, [<error messages>]) if any rule fails.
  • Raises ValueError if pwd is not a str.

PasswordValidator.estimate_strength(pwd: str) → Dict[str, Any]

Uses zxcvbn to estimate password strength.

  • Passwords longer than 72 characters are truncated before scoring (zxcvbn limit).
  • Always returns a dictionary — never raises.
Key Type Description
score int 0 (Very Weak) → 4 (Very Strong)
label str Human-readable label
feedback list[str] Warnings and suggestions from zxcvbn

PasswordValidator.evaluate(pwd: str) → Dict[str, Any]

Combines validate() and estimate_strength() into a single result.

Key Type Description
valid bool Whether all policy rules passed
policy_passed bool Policy Compliance
strength_passed bool score >= min_core
errors list[str] Policy error messages
score int zxcvbn score 0–4
label str Strength label
feedback list[str] zxcvbn feedback

CLI Usage

Interactive Mode

fortifypass 

# Or use uv
uv run fortifypass
  • Password input is hidden (no echo).
  • Type .exit() to quit.
  • Press Ctrl+C to interrupt.

Non-Interactive / Pipe Mode

Pipe a password directly — output is JSON, exit code is 0 for score ≥ 3, 1 otherwise:

echo "Str0ngP@ssw0rd!" | fortifypass

Ideal for shell scripts and CI pipelines:

echo "$PASSWORD" | fortifypass && echo "Password accepted" || echo "Password rejected"

Exit code is 0 only if the password passes both policy and strength requirements


Strength Score Labels

Score Label Meaning
0 Very Weak Trivially crackable
1 Weak Easy to crack
2 Moderate Some resistance
3 Strong Good security
4 Very Strong Excellent security

Scoring is provided by zxcvbn, which models real-world cracking strategies (dictionary attacks, keyboard patterns, etc.) rather than simple character-class rules.


Development

Running Tests

# All tests
uv run pytest

# With coverage report
uv run pytest --cov=fortifypass --cov-report=term-missing

# Verbose output
uv run pytest -v

# Run a specific test function
uv run pytest tests/test_validator.py::Testvalidate::test_validate_valid_password

Running Benchmarks

uv run pytest tests/test_performance_benchmarks.py --benchmark-only

Code Coverage

uv run pytest --cov=fortifypass --cov-branch --cov-report=html
# Open htmlcov/index.html in your browser

NOTE: uv run pytest works because pytest is installed in the uv environment. Do not run python test_validator.py direclty - the test framework will not discover functions.


Version History

Version Changes
v0.2.1 Fixed password score display issue, improved CLI output formatting, enhanced feedback, internal improvements for stability
v0.2.0 Added zxcvbn strength estimation, evaluate() API, banned words, full type annotations, colorama CLI, pipe mode, comprehensive test suite
v0.1.1 Infrastructure improvements, packaging fixes
v0.1.0 Initial public release

License

This project is licensed under the MIT License — see LICENSE.md for details.


Author

Botshelo Mere
GitHub: botshelo-mere

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fortifypass-0.2.1.tar.gz (16.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fortifypass-0.2.1-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file fortifypass-0.2.1.tar.gz.

File metadata

  • Download URL: fortifypass-0.2.1.tar.gz
  • Upload date:
  • Size: 16.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for fortifypass-0.2.1.tar.gz
Algorithm Hash digest
SHA256 2a2b34357d1232c40080e8079d14a65de878908d520d3d4891d1935806dd7bb6
MD5 fcf66f55fb88961e35a0c8a9fd93bd10
BLAKE2b-256 4fbaee7d0fd6121677aa0f4cee82a2a8405d55f71f1bdd48a0f940d923102d6f

See more details on using hashes here.

File details

Details for the file fortifypass-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: fortifypass-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 9.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for fortifypass-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bf334fd473a88f79d631c8ae093f877c83ee4cedd49b05617cf7846d0a518605
MD5 7a2afe63dd692c7040254e12654eae9b
BLAKE2b-256 da3ac05196cb7be8c9657222b95457a349e93b9f6ebc053d2fea66cd096cf9ed

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page