DefendKing
A defensive-only Python security toolkit (~70 functions across 9 sections) covering passwords, phishing/URL safety, brute-force protection, file hygiene, web-app security, network checks, cryptography, monitoring/ reporting, and API/session security.
Every function protects, detects, or reports — none of them attack, exploit, or access systems you don't own/manage.
Author: Barman License: MIT Current version: 2.5.1
Table of contents
- Install
- Quick start
- Error handling (read this first)
- 1. Passwords & authentication
- 2. Phishing & URL/email safety
- 3. Brute-force / abuse protection
- 4. File / malware hygiene
- 5. Web application security
- 6. Network-level defense
- 7. Cryptography & secrets management
- 8. Monitoring, risk scoring & reporting
- 9. API & session security
- Known limitations
- Changelog
Install
pip install defendking # core package, no third-party deps required
pip install "defendking[full]" # adds requests / cryptography / pyjwt for the
# functions that call external APIs or do AES/JWT
pip install "defendking[dev]" # adds pytest for running the test suite
For local development (editable install from a clone):
pip install -e ".[full,dev]"
python -m pytest tests/ -v
Quick start
import defendking as dk
result = dk.check_password_strength("Tr0ub4dor&3")
print(result)
# {'score': 64, 'label': 'medium', 'entropy_bits': 65.4, 'suggestions': [...]}
url_check = dk.check_suspicious_url("http://192.168.1.1@paypal-login.tk/verify")
print(url_check["suspicious"], url_check["risk_score"])
# True 85
Everything is also importable by name:
from defendking import check_password_strength, generate_strong_password
Error handling
DefendKing follows one consistent rule everywhere (see defendking.exceptions):
- Programmer errors (wrong argument type, empty required value, value
out of range) raise
InvalidInputErrorimmediately. Catch it like:from defendking import InvalidInputError try: dk.generate_strong_password(length=2) except InvalidInputError as e: print("bad input:", e)
- External-world failures (network down, DNS failed, file missing):
- Functions that already return a dict report the failure inside that
dict —
error/error_type: "ExternalServiceError"— and do not raise.result = dk.check_ssl_certificate("unreachable-host.example") if not result["valid"]: print(result["error"])
- Functions whose normal return is a plain scalar (a hash string, a
bool) raise
ExternalServiceErrorinstead, since silently turning their return type into a dict would break every call site.from defendking import ExternalServiceError try: dk.compute_file_hash("missing.txt") except ExternalServiceError as e: print("could not hash file:", e)
- Functions that already return a dict report the failure inside that
dict —
Every function's docstring says explicitly which behavior applies to it.
1. Passwords & authentication
check_password_strength(password: str) -> dict
Scores a password 0-100 using length, character diversity, estimated entropy (bits), and known weak-pattern detection.
dk.check_password_strength("correct horse battery staple 42!")
# {'score': 100, 'label': 'strong', 'entropy_bits': 112.8, 'suggestions': []}
check_password_breached(password: str, use_api: bool = True) -> dict
Checks a password against Have I Been Pwned (k-anonymity model — only a hash prefix is sent) with a local-list fallback.
dk.check_password_breached("123456")
# {'breached': True, 'times_seen': 37810483, 'source': 'hibp'}
generate_strong_password(length: int = 16, use_symbols: bool = True) -> str
dk.generate_strong_password(20)
# 'xQ2!kP9$mZr7@vLwT4nB'
hash_password(password: str) -> str / verify_password(password, stored_hash) -> bool
PBKDF2-HMAC-SHA256 password hashing with a random salt.
stored = dk.hash_password("my secret password")
dk.verify_password("my secret password", stored) # True
dk.verify_password("wrong guess", stored) # False
detect_common_password_patterns(password: str) -> list[str]
Flags keyboard walks, sequences, repeats, dates, and leetspeak substitutions.
dk.detect_common_password_patterns("Qwerty123")
# ['Contains a keyboard-walk pattern', 'Likely contains a birth year or date']
check_two_factor_status(is_enabled_flag: bool, backup_codes_count: int = 0) -> dict
dk.check_two_factor_status(True, backup_codes_count=1)
# {'enabled': True, 'has_backup_codes': True, 'low_on_backup_codes': True, 'recommendation': 'Good posture'}
check_password_expiry(last_changed: datetime, max_age_days: int = 90) -> dict
from datetime import datetime, timedelta, timezone
dk.check_password_expiry(datetime.now(timezone.utc) - timedelta(days=95))
# {'age_days': 95, 'max_age_days': 90, 'expired': True, ...}
validate_password_policy_compliance(password: str, policy: dict = None) -> dict
dk.validate_password_policy_compliance("alllowercase")
# {'compliant': False, 'violations': ['An uppercase letter is required', ...]}
check_default_credentials(username: str, password: str) -> bool
dk.check_default_credentials("admin", "admin") # True
2. Phishing & URL/email safety
check_suspicious_url(url: str) -> dict
Regex/heuristic phishing signal detection with a weighted risk_score
(0-100): raw IPs, punycode, homograph domains, open-redirect params,
brand impersonation, and more.
dk.check_suspicious_url("http://192.168.1.1@paypal-login.tk/verify")
# {'suspicious': True, 'risk_score': 100, 'reasons': [...]}
check_domain_similarity(domain: str, trusted_domains: list[str]) -> dict
Typosquat detection via Levenshtein distance, homoglyph normalization
(rn→m, 1→l, ...), substring spoofing, and TLD-swap detection.
dk.check_domain_similarity("paypa1.com", ["paypal.com"])
# {'likely_typosquat': True, 'matched_via_homoglyph_normalization': True, ...}
check_ssl_certificate(hostname: str, port: int = 443, timeout: float = 5.0) -> dict
dk.check_ssl_certificate("example.com")
# {'valid': True, 'expires': '...', 'days_until_expiry': 62, 'expiring_soon': False}
scan_email_for_phishing_signs(email_text: str) -> dict
Weighted-score scan for urgency language, credential harvesting, unusual payment requests, BEC/executive-impersonation patterns, and more.
dk.scan_email_for_phishing_signs("Dear customer, act now and click here to verify your account: http://bit.ly/xyz")
# {'suspicious': True, 'risk_score': 60, 'reasons': [...]}
extract_and_check_links(text: str) -> list[dict]
dk.extract_and_check_links("Check this out: http://bit.ly/xyz")
# [{'url': 'http://bit.ly/xyz', 'suspicious': True, ...}]
validate_email_format_and_mx(email: str, check_mx: bool = True) -> dict
dk.validate_email_format_and_mx("test@example.com", check_mx=False)
# {'email': 'test@example.com', 'valid_format': True, 'has_mx_record': None, 'is_disposable_domain': False}
3. Brute-force / abuse protection
RateLimiter(max_attempts=5, window_seconds=300)
limiter = dk.RateLimiter(max_attempts=3, window_seconds=60)
limiter.allow("1.2.3.4") # True, True, True, then False on the 4th call
limiter.reset("1.2.3.4") # clear after a successful login
rate_limiter(key, store, max_attempts=5, window_seconds=300) -> bool
Functional variant for callers managing their own storage dict.
detect_brute_force_attempt(failed_timestamps: list[float], threshold=5, window_seconds=60) -> bool
dk.detect_brute_force_attempt([t, t+1, t+2, t+3, t+4], threshold=5, window_seconds=60)
# True
IPBlocklist()
blocklist = dk.IPBlocklist()
blocklist.block("10.0.0.1", duration_seconds=900)
blocklist.is_blocked("10.0.0.1") # True
blocklist.unblock("10.0.0.1")
implement_captcha_trigger(failed_attempts, threshold=3, window_seconds=None, failed_timestamps=None) -> bool
dk.implement_captcha_trigger(failed_attempts=3, threshold=3) # True
log_failed_login_attempt(username, ip, log_path="failed_logins.csv") -> dict
dk.log_failed_login_attempt("baduser", "1.2.3.4")
# {'success': True, 'path': 'failed_logins.csv'}
distance_km_between(lat1, lon1, lat2, lon2) -> float
Haversine great-circle distance.
dk.distance_km_between(35.6892, 51.3890, 40.7128, -74.0060)
# 9877.5 (Tehran to New York, km)
detect_anomalous_login_location(previous_country, new_country, previous_time, new_time, previous_coords=None, new_coords=None) -> dict
"Impossible travel" detector.
dk.detect_anomalous_login_location(
"IR", "US", t1, t2,
previous_coords=(35.6892, 51.3890), new_coords=(40.7128, -74.0060),
)
# {'anomalous': True, 'high_confidence': True, 'implied_speed_kmh': 118530.0, ...}
detect_privilege_escalation_attempt(role_before, role_after, allowed_transitions: dict) -> bool
dk.detect_privilege_escalation_attempt("viewer", "admin", {"viewer": {"editor"}})
# True (not an allowed transition)
4. File / malware hygiene
compute_file_hash(filepath, algorithm="sha256") -> str
dk.compute_file_hash("report.pdf")
# 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3...'
scan_file_hash_virustotal(filepath, api_key) -> dict
Looks up a file's hash on VirusTotal (does not upload the file).
detect_suspicious_file_extension(filename: str) -> dict
Flags dangerous extensions, double extensions, RTL-override tricks, and padded/hidden extensions.
dk.detect_suspicious_file_extension("invoice.pdf.exe")
# {'flagged': True, 'double_extension_trick': True, ...}
check_file_integrity(filepath, known_good_hash, algorithm="sha256") -> bool
dk.check_file_integrity("backup.zip", "a94a8fe5...")
scan_directory_for_malware_signatures(directory, signatures: dict) -> list[dict]
dk.scan_directory_for_malware_signatures("/downloads", {"a94a8f...": "known_trojan_x"})
quarantine_suspicious_file(filepath, quarantine_dir="./quarantine") -> str
dk.quarantine_suspicious_file("suspicious.exe")
# './quarantine/1735689600_suspicious.exe.quarantined'
verify_backup_integrity(backup_path, expected_hash) -> bool
5. Web application security
sanitize_user_input(user_input: str) -> str
dk.sanitize_user_input("<script>alert(1)</script>")
# '<script>alert(1)</script>'
generate_csrf_token() -> str / check_csrf_token_validity(request_token, session_token) -> bool
validate_cors_policy(allowed_origins: list[str], request_origin: str) -> dict
dk.validate_cors_policy(["*"], "https://example.com")
# {'allowed': True, 'warning': "Using '*' together with credentials is a security risk", ...}
detect_xss_patterns(input_text: str) -> list[str]
Covers <script>, event handlers, javascript:/vbscript:, data:
URIs, meta-refresh, entity/unicode encoding, template-literal injection,
and more.
dk.detect_xss_patterns("<img src=x onerror=alert(1)>")
# ['inline event handler (...)', '<img> tag with onerror handler']
check_secure_cookie_flags(set_cookie_header: str) -> dict
dk.check_secure_cookie_flags("session=abc; SameSite=None")
# {'httponly': False, 'secure': False, 'samesite': True, 'samesite_none_without_secure': True}
validate_input_against_sql_injection(user_input: str) -> dict
Returns a severity classification (none/low/medium/high) in addition to
the boolean.
dk.validate_input_against_sql_injection("' UNION ALL SELECT username, password FROM users--")
# {'suspicious': True, 'matched_patterns': 4, 'severity': 'high'}
check_security_headers(headers: dict) -> dict
dk.check_security_headers({"Content-Security-Policy": "default-src 'self'"})
# {'checklist': {...}, 'missing': [...], 'weak_configurations': [...], 'score': 33}
verify_csp_header(csp_header: str) -> dict
dk.verify_csp_header("default-src 'self'; script-src 'unsafe-inline'")
# {'issues': [...], 'safe': False}
6. Network-level defense
(Run these only against systems you own or are authorized to assess.)
check_tls_version_support(hostname, port=443, timeout=5.0) -> dict
dk.check_tls_version_support("example.com")
# {'tls_version': 'TLSv1.3', 'outdated': False, 'cipher_suite': '...', 'weak_cipher': False}
check_firewall_rules_status(rules: list[dict]) -> dict
dk.check_firewall_rules_status([{"source": "0.0.0.0/0", "port": 22}])
# {'total_rules': 1, 'risky_count': 1, ...}
detect_arp_spoofing(arp_table: dict, known_good_mappings: dict) -> list[dict]
dk.detect_arp_spoofing({"192.168.1.1": "AA:BB:CC:DD:EE:FF"}, {"192.168.1.1": "11:22:33:44:55:66"})
validate_dns_response(hostname, resolved_ip, expected_ips) -> dict
check_vpn_connection_security(public_ip_before, public_ip_after, dns_servers_after, expected_vpn_dns) -> dict
audit_server_config(config: dict) -> list[str]
dk.audit_server_config({"debug": True, "firewall_enabled": False})
# ['Debug mode is on - must not be enabled in production', 'Firewall is disabled']
7. Cryptography & secrets management
generate_secure_token(length_bytes=32) -> str
ApiKeyRecord / rotate_api_key(current, grace_period_seconds=3600) -> tuple
mask_sensitive_data_in_logs(log_line, fields_to_mask=(...)) -> str
dk.mask_sensitive_data_in_logs('login attempt password="hunter2" user=barman')
# 'login attempt password=***REDACTED*** user=barman'
encrypt_sensitive_data(plaintext, key: bytes) -> str / decrypt_sensitive_data(token, key: bytes) -> str
AES-256-GCM. Requires pip install "defendking[full]".
import secrets
key = secrets.token_bytes(32)
token = dk.encrypt_sensitive_data("top secret", key)
dk.decrypt_sensitive_data(token, key) # 'top secret'
validate_jwt_token(token, secret, algorithms=None) -> dict
Also rejects alg: none tokens explicitly.
check_secrets_in_codebase(directory, file_extensions=(...)) -> list[dict]
Detects AWS/GitHub/Slack/Google/Stripe/SendGrid/Twilio/Mailgun keys, JWTs, private keys, and DB connection strings with embedded credentials.
dk.check_secrets_in_codebase("./src")
# [{'file': './src/config.py', 'line': 4, 'type': 'Possible GitHub personal access / OAuth token'}]
8. Monitoring, risk scoring & reporting
SecurityFinding(category, description, severity, timestamp=now)
severity must be one of low/medium/high/critical.
calculate_risk_score(findings: list[SecurityFinding]) -> dict
findings = [dk.SecurityFinding("auth", "weak password policy", "high")]
dk.calculate_risk_score(findings)
# {'score': 14, 'level': 'low', 'finding_count': 1, 'by_severity': {'high': 1}, 'has_critical_finding': False}
generate_security_report(findings, target_name="system") -> str
Markdown report.
export_findings_to_json(findings, output_path) -> dict
dk.export_findings_to_json(findings, "report.json")
# {'success': True, 'path': 'report.json'}
generate_incident_response_report(incident_title, detected_at, affected_systems, actions_taken) -> str
audit_user_permissions(users: list[dict], expected_max_role: dict) -> list[dict]
notify_admin_dashboard(finding, webhook_url) -> dict / send_security_alert(message, telegram_bot_token, chat_id) -> dict
schedule_periodic_scan(interval_seconds, scan_function, max_runs=None) -> None
generate_security_checklist(system_type) -> list[str]
system_type is one of web/server/network/cloud/mobile.
dk.generate_security_checklist("cloud")
9. API & session security
validate_api_request_signature(payload, timestamp, signature, secret, tolerance_seconds=300) -> dict
HMAC-SHA256 request signing/verification (the pattern used by most REST/exchange/trading APIs).
import time, hmac, hashlib
secret = "shared-secret"
ts = str(time.time())
sig = hmac.new(secret.encode(), f"{ts}{payload}".encode(), hashlib.sha256).hexdigest()
dk.validate_api_request_signature(payload, ts, sig, secret)
# {'valid': True, 'reason': None}
detect_replay_attack(nonce, seen_nonces: set, max_stored=100_000) -> bool
seen = set()
dk.detect_replay_attack("req-123", seen) # False (new)
dk.detect_replay_attack("req-123", seen) # True (replay!)
check_session_fixation(pre_login_session_id, post_login_session_id) -> dict
dk.check_session_fixation("sess-abc", "sess-abc")
# {'rotated': False, 'vulnerable_to_fixation': True, ...}
generate_scoped_api_key(scopes: list[str], prefix="sk") -> dict
dk.generate_scoped_api_key(["read", "trade"])
# {'api_key': 'sk_...', 'stored_hash': '...', 'scopes': ['read', 'trade'], ...}
check_api_rate_limit_headers(headers: dict) -> dict
dk.check_api_rate_limit_headers({"X-RateLimit-Remaining": "0", "Retry-After": "30"})
# {'should_back_off': True, 'suggested_backoff_seconds': 30, ...}
Known limitations
- Heuristic detection still isn't a real classifier.
detect_xss_patterns,validate_input_against_sql_injection, andcheck_suspicious_urlare regex-based signals, not a guarantee of safety. A determined attacker can craft a payload that slips past a fixed pattern list. Always pair them with the real defenses: parameterized queries/ORM for SQL, contextual output encoding + a strict CSP for XSS, and a reputation/block-list service for URLs. - The local common-password list is a sample, not a real breach corpus.
check_password_breachedqueries the Have I Been Pwned API by default — the local list is only an offline fallback. detect_anomalous_login_locationgives an accurate result only when you pass realprevious_coords/new_coords(haversine distance viadistance_km_between). Without coordinates it falls back to a conservative fixed-distance estimate.detect_replay_attack's in-memoryseen_noncesset doesn't expire entries by time, only by count (max_stored) — for a real multi-process service, back it with a TTL-based store (e.g. Redis).schedule_periodic_scanis a simple blocking loop for scripts/demos, not a production scheduler — useAPScheduleror a system cron job for real deployments.
Changelog
2.5.1
- Added
defendking.exceptions(DefendKingError,InvalidInputError,ExternalServiceError) and applied a consistent error-handling convention across every function (see Error handling). - Substantially deepened detection accuracy across every section: password entropy scoring, homoglyph/TLD-swap domain-similarity checks, weighted risk scoring for URLs and phishing emails, SQLi severity classification, more secret-scanning patterns, more security-header/CSP checks, "high confidence" flagging for impossible-travel detection, and more.
- Full README rewrite with per-function documentation and examples.
- No new functions were added in this release — the focus was entirely on robustness and detection depth of the existing ~65 functions plus the five functions the 1.5.0 API-security section already introduced.
1.5.0
- New section: API & session security (
validate_api_request_signature,detect_replay_attack,check_session_fixation,generate_scoped_api_key,check_api_rate_limit_headers). - First pass at sharpening detection across passwords, URLs/phishing, XSS/SQLi, file extensions, secret-scanning, security headers/CSP, and firewall/default-credential checks.
1.0.0
- Initial public release: 8 sections, ~65 functions.
Versioning
Following semantic-ish convention: first digit = structural changes, second = new features, third = bug fixes.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file defendking-2.5.1.tar.gz.
File metadata
- Download URL: defendking-2.5.1.tar.gz
- Upload date:
- Size: 59.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.1.0 CPython/3.8.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01e1e4581d7da35ab8a2caddeab1b7ef30e3ddf64297e117a15a0328c0520470
|
|
| MD5 |
d2bb0da2ba7538fb4b2bcff551fdd8e8
|
|
| BLAKE2b-256 |
30af9f66ed643dc147da2d652fe9b924363894d01a99aa2f0c93075c3be4ec7a
|
File details
Details for the file defendking-2.5.1-py3-none-any.whl.
File metadata
- Download URL: defendking-2.5.1-py3-none-any.whl
- Upload date:
- Size: 46.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.1.0 CPython/3.8.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c350528de7d00792f4c62348cae394c0f19907d2a44a870b4027afc735158719
|
|
| MD5 |
bded483a5f14af9f8e0b73d8831f4e62
|
|
| BLAKE2b-256 |
8370875f24bac555145dba0a5266b680f6ae61b5b8d260f6faa6ed7c1188cd4c
|