FortifyPass - Real-World Password Security for Developers
Every day, companies get breached because someone picked
123456orpassword. 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) |
ValueErroris raised on construction ifmin_length <= 0ormax_length < min_length.
ValueErroris raised on construction ifmin_score < 0or not0 <= 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
ValueErrorifpwdis not astr.
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+Cto 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 pytestworks becausepytestis installed in the uv environment. Do not runpython test_validator.pydireclty - 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
Metadata
Release files for fortifypass 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fortifypass-0.2.1.tar.gz | 16.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fortifypass-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.2 kB
Release files / fortifypass-0.2.1.tar.gz
| Download URL | fortifypass-0.2.1.tar.gz |
|---|---|
| Size | 16.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2a2b34357d1232c40080e8079d14a65de878908d520d3d4891d1935806dd7bb6
|
|
BLAKE2b-256 checksum How to use checksums |
4fbaee7d0fd6121677aa0f4cee82a2a8405d55f71f1bdd48a0f940d923102d6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|
Release files / fortifypass-0.2.1-py3-none-any.whl
| Download URL | fortifypass-0.2.1-py3-none-any.whl |
|---|---|
| Size | 9.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf334fd473a88f79d631c8ae093f877c83ee4cedd49b05617cf7846d0a518605
|
|
BLAKE2b-256 checksum How to use checksums |
da3ac05196cb7be8c9657222b95457a349e93b9f6ebc053d2fea66cd096cf9ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|