=== Login Armor ===
Contributors: wpformation
Donate link: https://wpformation.com
Tags: login security, hide login, brute force, limit login, activity log
Requires at least: 6.8
Tested up to: 7.0
Stable tag: 2.4.4
Requires PHP: 8.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Twelve security modules + AI briefing: hide login, request firewall, brute force, 2FA, password policy, sessions, hardening, audit log. No upsells.

== Description ==

**Twelve security modules. One lightweight plugin. Zero compromise.**

Login Armor is a complete WordPress security stack built for agencies, freelancers and pros who deliver audit-ready sites. No premium tier, no bundled marketing dashboard, no telemetry. Every module runs locally, ships with safe defaults, and stays out of your way.

Stop juggling Wordfence's bloat, Solid Security's upsells, and Limit Login Attempts' gaps — Login Armor delivers twelve independent modules in about one megabyte.

= New in 2.4.0 =

* **Request Firewall** — an optional, 8G-inspired PHP filter that blocks malicious requests (SQL injection, code execution, traversal, XSS, disallowed HTTP methods) before WordPress finishes loading, on Apache, Nginx and LiteSpeed alike. Off by default, it starts in monitor mode and never filters logged-in administrators; every block is logged, aggregated to one incident per IP per hour.
* **Guided onboarding** — a first-run wizard offers a one-click "safe baseline" that turns on the no-risk essentials, so a beginner is protected in seconds. The same "Apply safe baseline" button stays available any time.

= Why Login Armor =

* **No upsells, ever.** No "premium" tier, no greyed-out "Pro" buttons. Every feature is GPL.
* **No external services to sign up for.** No API keys, no remote dashboards, no telemetry. The only outbound calls are opt-in: Have I Been Pwned (breach/password checks), Slack/Discord/webhook (notifications), the keyless ipwho.is API (geolocation), and your own WordPress 7 AI connector.
* **Built to be invisible.** Sub-megabyte ZIP, lazy-loaded modules, indexed queries — under 2 ms on a normal login flow.
* **Multisite-aware, PHP 8.1-native, production-grade defaults.** Network-activate a fleet, configure per-site, manage from a complete WP-CLI suite; zero-config gets you 80 percent of the protection.

= Twelve independent modules =

1. **Hide Login** — Replace wp-login.php with a custom slug; the old URL returns a 404, and a branded pre-activation modal lets you pick or generate the slug and emails it to you so you can't lock yourself out. Compatible with multisite, reverse proxies and password-recovery flows.
2. **Brute Force Protection** — Cascading lockouts escalating to a 24-hour ban, with subnet blocking and trusted X-Forwarded-For; lostpassword, register, XML-RPC and the REST users endpoint are all gated when an IP is locked, and every lockout surfaces as an incident.
3. **Hardening** — Fifteen one-click toggles across surface reduction, credential hardening, request filtering and account monitoring: disable XML-RPC/pingbacks, the file editor, version exposure, application passwords and author enumeration; block reserved usernames (Unicode-confusable detection); add a login honeypot; get alerted on new administrators.
4. **Two-Factor Authentication** — TOTP, one-time codes by email and printable backup codes, with trusted devices for thirty days, per-role enforcement, a configurable grace period and an email recovery flow when the authenticator is lost.
5. **Detection and Incidents** — A real-time engine groups raw events into six attack patterns, each with a drill-down (timeline, source IPs, target users, severity, UA fingerprint) and one-click actions (reset password, block subnet, mark resolved).
6. **Activity Log** — A compliance-ready, tamper-evident (hash-chained) audit trail of admin actions across seven logger domains, with filtering, CSV export, configurable retention and optional signed webhook forwarding to a SIEM.
7. **Login Page Security Headers** — Content-Security-Policy, X-Frame-Options, Permissions-Policy, Referrer-Policy and X-Content-Type-Options on wp-login.php and the lockout page, in two presets with an optional CSP report-uri; baseline headers can optionally extend site-wide.
8. **Breach Check** — Detect logins using a breached password via privacy-preserving k-anonymity against Have I Been Pwned (only a 5-character SHA-1 prefix leaves the server); optional XposedOrNot email lookup, fail-soft so an outage never blocks login.
9. **Password Policy** — Enforce strong, unique passwords at registration, profile update and reset: minimum length and character classes, forbid the username inside the password, optionally reject breached passwords, with optional non-locking expiration nudges.
10. **Session Management** — Idle-timeout logout measured on real page loads, a maximum session lifetime regardless of "remember me", an optional single-active-device restriction, and a one-click "sign out all other devices".
11. **IP Geolocation** — Show the attacker's country on the Incidents and Events tabs; lazy, cached thirty days, capped per page load, private ranges never sent. Keyless ipwho.is by default, swappable for an offline database via a filter.
12. **Request Firewall** — An optional, 8G-inspired PHP filter that blocks malicious query strings, paths, HTTP methods and (opt-in) user-agents/referrers before WordPress loads, on Apache/Nginx/LiteSpeed alike; off by default, starts in monitor mode, never filters admins, skips REST/cron/WP-CLI, with an IP/path allowlist (CIDR). Not scored.

= AI Security Briefing (optional) =

Built on the WordPress 7 native AI Client, one click turns your last thirty days of activity into a plain-language verdict, an IP picture and a short list of prioritised actions; "Explain with AI" does the same on a single incident. Minimised mode (anonymised signals) is the default and deep mode is an explicit opt-in. No API key is stored — it uses your own WordPress AI connector, so provider and cost stay yours. It always leads with a deterministic facts snapshot that works with or without AI.

= Plus =

* **Guided onboarding** — a first-run wizard with a one-click safe baseline (Simple) or manual setup (Advanced); the "Apply safe baseline" button stays available, and upgrading sites never see the wizard.
* **Security score** — a weighted 0-100 read of your posture with a one-click "next best action"; observability features (geolocation, notifications, the AI assistant) are deliberately not scored.
* **Conflict detection** — warns when another login-security plugin (Wordfence, Solid Security, Sucuri, All-In-One Security, SecuPress and more) or a cache plugin (with Hide Login on) could clash.
* **Notifications** — email, Slack, Discord or webhook with SSRF-safe URL validation, severity threshold and rate limiting.
* **WP-CLI suite** and a **dashboard widget** (14-day sparkline, six headline metrics).

GPL forever. PHP 8.1+. WordPress 6.8+. Zero dependencies.

---

**Douze modules de sécurité. Une seule extension légère. Zéro compromis.**

Login Armor est une stack complète de sécurité WordPress conçue pour les agences, les freelances et les pros qui livrent des sites prêts à passer un audit. Pas de version premium, pas de tableau de bord marketing intégré, pas de télémétrie. Chaque module tourne en local, embarque des réglages par défaut sécurisés, et reste discret.

Fini de jongler entre la lourdeur de Wordfence, les fenêtres d'upsell de Solid Security et les angles morts de Limit Login Attempts — Login Armor regroupe douze modules indépendants en environ un méga-octet.

= Nouveau en 2.4.0 =

* **Pare-feu de requêtes** : un filtre PHP optionnel, inspiré du pare-feu 8G, qui bloque les requêtes malveillantes (injection SQL, exécution de code, traversée de répertoires, XSS, méthodes HTTP non autorisées) avant même que WordPress ait fini de charger, aussi bien sur Apache que Nginx ou LiteSpeed. Désactivé par défaut, il démarre en mode surveillance et ne filtre jamais les administrateurs connectés ; chaque blocage est journalisé, agrégé en un incident par IP et par heure.
* **Assistant de configuration** : à la première activation, un assistant propose une « base sûre » en un clic qui active les essentiels sans risque — un débutant est protégé en quelques secondes. Le même bouton « Appliquer la base sûre » reste disponible à tout moment.

= Pourquoi Login Armor =

* **Aucun upsell, jamais.** Pas de niveau « premium », pas de boutons « Pro » grisés. Tout est en GPL.
* **Aucun service externe à activer.** Pas de clé API, pas de tableau distant, pas de télémétrie. Les seuls appels sortants sont opt-in : Have I Been Pwned (fuites/mots de passe), Slack/Discord/webhook (notifications), l'API sans clé ipwho.is (géolocalisation) et votre propre connecteur IA WordPress 7.
* **Conçu pour être invisible.** ZIP de moins d'un méga, modules chargés à la demande, requêtes indexées — sous 2 ms sur un flux de connexion normal.
* **Compatible multisite, natif PHP 8.1, réglages prêts pour la production.** Activation réseau d'une flotte, configuration par site, pilotage via une suite WP-CLI complète ; sans configuration, vous avez déjà 80 % de la protection.

= Douze modules indépendants =

1. **Masquer la connexion** : remplace wp-login.php par une URL personnalisée (l'ancienne renvoie une 404) ; une modale de pré-activation choisit ou génère le slug et vous l'envoie par e-mail pour éviter tout verrouillage. Compatible multisite, reverse proxies et récupération de mot de passe.
2. **Protection contre la force brute** : verrouillages en cascade montant à un bannissement de 24 h, blocage de sous-réseaux et support X-Forwarded-For ; lostpassword, register, XML-RPC et l'endpoint REST users sont bloqués pour une IP verrouillée, et chaque verrouillage devient un incident.
3. **Renforcement** : quinze bascules en un clic (réduction de surface, identifiants, filtrage des requêtes, surveillance des comptes) — désactiver XML-RPC/pingbacks, l'éditeur de fichiers, l'exposition de version, les mots de passe applicatifs et l'énumération d'auteurs ; bloquer les identifiants réservés (homoglyphes Unicode) ; ajouter un pot de miel ; être alerté à la création d'un administrateur.
4. **Authentification à deux facteurs** : TOTP, codes à usage unique par e-mail et codes de secours imprimables, avec appareils de confiance 30 jours, application par rôle, période de grâce configurable et récupération par e-mail en cas de perte.
5. **Détection et incidents** : un moteur en temps réel regroupe les événements en six patterns d'attaque, chacun avec une vue détaillée (chronologie, IP sources, comptes cibles, sévérité, empreinte UA) et des actions en un clic.
6. **Journal d'activité** : piste d'audit conforme et inviolable (chaîne de hachage) des actions admin sur sept domaines, avec filtrage, export CSV, rétention configurable et transfert webhook signé optionnel vers un SIEM.
7. **En-têtes de sécurité** : CSP, X-Frame-Options, Permissions-Policy, Referrer-Policy et X-Content-Type-Options sur wp-login.php et la page de verrouillage, en deux préréglages avec CSP report-uri optionnel ; les en-têtes de base peuvent s'étendre à tout le site.
8. **Détection de fuites** : repère les connexions avec un mot de passe fuité via k-anonymat sur Have I Been Pwned (seul un préfixe SHA-1 de 5 caractères sort) ; vérification e-mail XposedOrNot optionnelle, fail-soft.
9. **Politique de mot de passe** : impose des mots de passe forts à l'inscription, au profil et à la réinitialisation (longueur, classes de caractères, interdiction de l'identifiant, rejet optionnel des mots de passe fuités), avec expiration optionnelle qui ne verrouille jamais personne dehors.
10. **Gestion des sessions** : déconnexion sur inactivité mesurée sur les vrais chargements, durée de vie maximale indépendante de « se souvenir de moi », limitation optionnelle à un seul appareil actif, et « déconnecter tous les autres appareils » en un clic.
11. **Géolocalisation IP** : affiche le pays des IP attaquantes dans Incidents et Événements ; recherches paresseuses, cache 30 jours, plafonnées par page, plages privées jamais envoyées. ipwho.is sans clé par défaut, base hors ligne possible via un filtre.
12. **Pare-feu de requêtes** : filtre PHP optionnel inspiré du 8G qui bloque chaînes de requête, chemins, méthodes HTTP et (en option) user-agents/referrers malveillants avant le chargement de WordPress, sur Apache/Nginx/LiteSpeed ; désactivé par défaut, démarre en mode surveillance, ne filtre jamais les admins, ignore REST/cron/WP-CLI, allowlist IP/chemins (CIDR). Non noté.

= Briefing de sécurité IA (optionnel) =

Bâti sur le client IA natif de WordPress 7, un clic transforme vos trente derniers jours d'activité en un verdict en langage clair, un panorama des IP et une courte liste d'actions prioritaires ; « Expliquer avec l'IA » fait de même sur un incident. Le mode minimisé (signaux anonymisés) est par défaut, le mode approfondi est un opt-in explicite. Aucune clé API stockée : il utilise votre propre connecteur IA WordPress, le coût et le fournisseur restent les vôtres. Il s'ouvre toujours sur un instantané de faits déterministes, utile avec ou sans IA.

= En plus =

* **Assistant de configuration** : un assistant à la première activation propose une base sûre en un clic (Simple) ou une voie manuelle (Avancée) ; le bouton « Appliquer la base sûre » reste disponible, et les sites en mise à jour ne le voient jamais.
* **Score de sécurité** : lecture pondérée 0-100 de votre posture avec une action prioritaire en un clic ; les fonctions d'observabilité (géolocalisation, notifications, assistant IA) ne sont pas notées.
* **Détection de conflits** : alerte quand une autre extension de sécurité axée connexion (Wordfence, Solid Security, Sucuri, All-In-One Security, SecuPress et d'autres) ou un plugin de cache (avec Hide Login actif) peut entrer en conflit.
* **Notifications** : e-mail, Slack, Discord ou webhook, avec validation d'URL anti-SSRF, seuil de sévérité et rate limiting.
* **Suite WP-CLI** et **widget Tableau de bord** (sparkline 14 jours, six métriques clés).

= Conçu par =

Login Armor est conçu et maintenu par Fabrice Ducarme de [WPFormation](https://wpformation.com/login-armor/), expert WordPress français obsédé par les sites propres, rapides et prêts pour l'audit. On l'utilise sur chaque site qu'on livre.

* [Présentation et fonctionnement de Login Armor](https://wpformation.com/login-armor/)
* [Guides de sécurité WordPress](https://wpformation.com/securite-wordpress/) sur WPFormation
* [Veille des vulnérabilités WordPress](https://wpformation.com/outils/veille-securite/) : l'outil de veille sécurité de WPFormation

GPL pour toujours. PHP 8.1+. WordPress 6.8+. Zéro dépendance.

== Installation ==

1. Upload the `login-armor` directory to `/wp-content/plugins/`
2. Activate the plugin through the 'Plugins' menu in WordPress
3. Go to LoginArmor in the admin menu to configure

For multisite: Network Activate the plugin to apply it across all sites.

= Setting up Hide Login =

1. Go to LoginArmor > Settings > Hide Login section
2. Enter your desired login slug (e.g., `my-login`)
3. Save settings
4. **Bookmark your new login URL**: you will need it to access your admin

= Recovering access =

If you forget your custom login URL:

* Use the recovery email feature (configurable in settings)
* Connect to your database and delete the `login_armor_hide_slug` row from the `wp_options` table
* Use WP-CLI: `wp option delete login_armor_hide_slug`

== Frequently Asked Questions ==

= Will it lock me out of my own site? =

No. Hide Login always sends a one-time recovery URL to the admin email. If you lose the slug, check your inbox. The plugin also honors `wp-cli` fallback so you can reset anything from SSH.

= Does it slow my site down? =

No. Everything is lazy-loaded and indexed. On a normal login flow the extra SQL cost is under 2 ms.

= Is it compatible with Cloudflare / reverse proxies? =

Yes. IP detection honors trusted `X-Forwarded-For` headers; you pick the header in Settings.

= Does it work with multisite? =

Yes, subdomain and subfolder. Each site has its own modules, logs, and thresholds.

= Can I use LoginArmor alongside Wordfence / iThemes Security / Solid Security? =

Yes, but disable overlapping modules on one side to avoid double lockouts.

= Where is the data stored? =

Three custom tables in your own database: events, incidents, activity. Nothing leaves your server.

= How do I migrate my configuration? =

Settings are plain WordPress options. Export/import via WP-CLI or any standard options-sync tool.

= Is there a pro version? =

Not currently. LoginArmor is fully free and open source. GPL forever.

= Where can I report bugs or request features? =

Support forum: [wordpress.org/support/plugin/login-armor/](https://wordpress.org/support/plugin/login-armor/).

== Screenshots ==

1. Quick tour of all eight modules - Hide Login, Hardening, 2FA setup with QR code, Incidents drill-down, Activity Log, Events, and Overview dashboard.
2. Overview dashboard - health cards, security pulse, live event tail, threat banner that surfaces active attacks.
3. Incidents - real-time pattern detection grouped by attack class with severity and one-click resolution.
4. Incident drill-down - full timeline, user-agent fingerprint, suggested actions, escalation flag.
5. Events - complete login attempts log with filters and CSV export.
6. Activity Log - admin action audit trail across seven domains, filterable and exportable.
7. Settings - modular configuration with live security score and a sticky save bar.
8. Hide Login pre-activation modal - pick or generate the secret URL and email it to yourself before flipping the switch.
9. Hardening - thirteen one-click toggles grouped by surface reduction, credential hardening, and request filtering.
10. Two-factor authentication setup - QR code for any authenticator app, copy-paste fallback, and live verification.
11. Breach Check - fully transparent k-anonymity lookups, separate password and email toggles, opt-in email check disabled by default.

== External Services ==

= AI Security Briefing (optional) =

The AI Security Briefing and the "Explain with AI" incident analysis are powered by the **WordPress 7 native AI Client** (`wp_ai_client_prompt()`). When the administrator clicks the analysis button, LoginArmor asks WordPress to send a prompt to the **AI connector that the administrator configured in their own WordPress** (for example OpenAI, Anthropic or Google, depending on the connector). LoginArmor itself stores no API key and contacts no endpoint directly: the request, the provider and the cost are owned by the site's own AI connector.

Data sent: a text prompt describing the security situation. In **minimised mode (the default)**, only anonymised, non-identifying signals are included (counts, categories, severities, role buckets) - no IP address and no username in clear. In **deep mode** (an explicit, off-by-default opt-in), the prompt additionally includes real IP addresses and event details so the analysis can name specific sources. No data is ever sent unless the administrator clicks the analysis button.

This feature is inactive unless WordPress 7 (or the AI Building Blocks feature plugin) is present with a configured, approved AI connector. The applicable terms and privacy policy are those of the AI provider the administrator chose for their connector; please refer to that provider's documentation.

= Webhook Notifications (optional) =

When explicitly enabled and configured by the administrator in LoginArmor > Settings > Notifications, the plugin sends incident data to third-party services via webhooks.

Data sent: incident type, severity level, IP address, target username, event count, and site URL.

No data is sent unless the administrator actively enables and configures a notification channel.

* **Slack** - [Terms of Service](https://slack.com/terms-of-service) | [Privacy Policy](https://slack.com/privacy-policy)
* **Discord** - [Terms of Service](https://discord.com/terms) | [Privacy Policy](https://discord.com/privacy)
* **Custom Webhook URL** - User-configured endpoint (administrator's responsibility)

= Gravatar (Automattic) =

The Activity Log tab uses WordPress core's `get_avatar()` function to display user avatars. WordPress may send a hashed email address to [Gravatar](https://gravatar.com/) servers to retrieve avatar images. This is controlled by Settings > Discussion > Avatars.

* **Gravatar** - [Automattic Terms of Service](https://automattic.com/tos/) | [Privacy Policy](https://automattic.com/privacy/)

= Breach Check - Have I Been Pwned (optional) =

When the administrator explicitly enables the **Breach Check** module (LoginArmor > Settings > Breach Check), LoginArmor queries the public Have I Been Pwned Pwned Passwords API on each successful login and on password changes to detect user passwords that appear in public data breach corpora.

Data sent: the **first 5 hex characters** of a SHA-1 hash of the password (k-anonymity lookup). The full password and its full hash never leave the server. The API cannot determine which password is being checked - it only sees a 5-character prefix that is mathematically shared with ~500-900 other candidate hashes.

The same privacy-preserving lookup is also used by the **Password Policy** module when its "block compromised passwords" rule is enabled: a new password is checked the moment it is set (registration, profile update, password reset) and rejected if it appears in the corpus. Identical k-anonymity model - only a 5-character hash prefix is sent - and identical fail-soft behaviour: if the API is unreachable, the password is allowed rather than blocking the reset.

No API key or account is required. The endpoint is free and public.

* **Have I Been Pwned** - [Pwned Passwords privacy statement](https://haveibeenpwned.com/Privacy) | [Acceptable Use Policy](https://haveibeenpwned.com/AcceptableUse)

= Breach Check - XposedOrNot (optional email sub-toggle) =

When the administrator additionally enables the **Email check** sub-toggle inside the Breach Check module (off by default), LoginArmor queries the public XposedOrNot check-email API on new user creation and on email change to detect email addresses that appear in publicly disclosed data breaches.

Data sent: the user's email address (URL-encoded) and a plugin-identifying User-Agent string. This is unavoidable for the lookup - there is no k-anonymity variant for email breach checks. Because this call transmits an email address to a third party, it is opt-in and off by default.

No API key or account is required. The endpoint is free and public.

* **XposedOrNot** - [xposedornot.com](https://xposedornot.com/) | [Privacy Policy](https://xposedornot.com/privacy.html)

= IP Geolocation - ipwho.is (optional) =

When the administrator explicitly enables the **IP Geolocation** module (LoginArmor > Settings > IP Geolocation), LoginArmor resolves the IP addresses it already displays (attacking IPs on the Incidents and Events tabs) to a country, so the admin can see where attacks originate.

Data sent: the public IP address being displayed, sent to the keyless ipwho.is HTTPS API, which returns the country. The lookup happens lazily - only when an administrator opens a screen that lists IPs - and each result is cached for 30 days, so a given IP is sent at most once per month. **Private and reserved IP ranges are never sent.** No API key or account is required.

Developers can short-circuit the lookup with the `login_armor_geoip_lookup` filter (e.g. to use a bundled MaxMind/IP2Location database), in which case no request is sent to ipwho.is at all.

* **ipwho.is** - [ipwho.is](https://ipwho.is/) | [Documentation](https://ipwhois.io/documentation)

== Changelog ==

= 2.4.4 =
Hardening release from an external code audit; five confirmed issues fixed and each verified end-to-end (real HTTP flow locally, re-run under PHP 8.4, and validated on a live WordPress 7.0 / PHP 8.3 install). The Overview dashboard now computes the threat level and active-incident count over every active incident, so an older critical incident can no longer drop the headline back to "Normal". A successful two-factor login now fires the canonical wp_login cycle, so failed-attempt counters reset, the login is logged, the single-session policy applies and third-party integrations run. Lockout escalation is now atomic under concurrency, so a burst of simultaneous failures can no longer turn a first offence into an immediate long ban. The Slack/Discord/generic notification webhooks are re-validated against private, reserved, link-local and IPv6-internal addresses before every send (SSRF), and CSV log exports neutralise spreadsheet formula injection from attacker-controlled fields. Plugin Check 0 ERROR.

= 2.4.3 =
Security release. Mandatory two-factor authentication is now a hard gate: a user in an enforced role who never enrolled and whose grace period has expired is blocked at login instead of being let in with only a redirect to their profile (reported by the WordPress Plugin Review Team). Existing users are never locked out unexpectedly - the grace window is started automatically the first time enforcement applies, and an admin can reopen it from the Users list ("Restart 2FA grace") or via WP-CLI. Hardened: TOTP codes can no longer be replayed within their validity window (single-use per time-step, RFC 6238); the Activity Log webhook URL is validated against private, reserved, link-local and IPv6-internal addresses (SSRF), matching the notification channels; attacker-submitted usernames are neutralised before entering the optional AI incident prompt. A filter (login_armor_2fa_hard_enforcement) restores the previous soft behaviour if needed.

= 2.4.2 =
Coherence and lifecycle release. Fixes: deactivation now clears every scheduled task (three were left running); uninstall removes all options, user meta and caches from the newer modules (AI, Password Policy, Sessions, GeoIP, Firewall); disabling Two-Factor now asks for confirmation like every other module; the plugin's own conflict warnings, silently hidden since 2.4.0, show again. Improvements: a composite database index speeds up brute-force lookups, open incidents are capped so the table cannot grow forever, and the admin UI is more consistent (numeric fields, empty states, module counters). New: a warning when a front-end login plugin such as Ultimate Member is active, since Two-Factor and Hide Login are not compatible with a front-end login form. No behaviour change on standard installs.

= 2.4.1 =
Fix: a "critical error" could occur during password recovery when another active plugin re-fires a WordPress core hook with an off-contract argument type or arity (for example a null passed to retrieve_password_message ahead of Login Armor). Strict parameter hints are relaxed, each with an internal type guard, on every core-hook callback across all modules; behaviour is unchanged on canonical WordPress calls. Generalises the 2.1.15 URL-builder fix to the whole plugin.

= 2.4.0 =
Feature release - request firewall, guided onboarding, fuller in-app docs.

* New - **Request Firewall** (optional, off by default): a PHP "8G"-style filter that blocks malicious query strings, paths and HTTP methods before WordPress fully loads (Apache/Nginx/LiteSpeed/IIS). Starts in monitor mode; admins, REST, cron, WP-CLI and admin-ajax are never filtered; IP/path allowlist (CIDR); blocks aggregate to one incident per IP per hour.
* New - **Onboarding wizard** with a one-click safe baseline (Simple) or manual setup (Advanced), plus a permanent "Apply safe baseline" button. Upgrading sites see no change.
* Improvement - Granular security-plugin conflict warnings, a cache-plugin warning when Hide Login is on, and contextual help for the modules added since 2.2.0.

= 2.3.0 =
Feature release - account-security hardening.

* New - **Password Policy**: minimum length and character-class rules, forbid the username in the password, optionally reject breached passwords (privacy-preserving HIBP), optional non-locking expiration.
* New - **Session Management**: idle timeout, maximum session lifetime, optional single active device, and "sign out all other devices".
* New - **IP Geolocation** (opt-in): country next to IPs on Incidents/Events; lazy, cached, private ranges never sent (see External Services).
* Improvement - Score now accounts for Password Policy and Session Management; baseline headers can apply site-wide; every IP lockout creates an incident. New hardening: disable pingbacks, alert on new admin.

= 2.2.0 =
Feature release - the AI Security Briefing.

* New - **AI Security Briefing** on the Overview: one click turns your last 30 days of activity into a plain-language verdict, an IP picture and prioritised actions. Built on the WordPress 7 native AI Client - uses your own connector, stores no API key, runs only on click. Always leads with a deterministic facts snapshot (with or without AI); plus "Explain with AI" on an incident.
* Privacy - Minimised mode (anonymised signals) is the default; deep mode (real IPs) is an explicit opt-in. See External Services.

= 2.1.26 =
Fix: email/backup 2FA bouncing to "session expired" on browsers that don't return the verification cookie; the form now also carries the session token. Security unchanged.

= 2.1.25 =
Fix: email/backup two-factor verification rejected in some browsers (notably Chrome); the form is now uncached and authenticated by the signed same-site cookie.

= 2.1.24 =
Fix: fatal error during authenticator-app setup on hosts whose wp-config.php does not define AUTH_KEY (e.g. some Infomaniak installs). Existing setups unaffected.

= 2.1.23 =
Fix: 2FA login screen - "use a different method" links now work, expired/locked sessions explain themselves, and the setup button reports errors.

= 2.1.22 =
Fix: the Security Score now counts default-on modules (Brute Force, Detection). Display and scoring only.

= 2.1.21 =
Cleaner user-agent labels in the Events table.

= 2.1.20 =
Migration-aware Activity Log integrity (amber "Keys changed" instead of a false TAMPERED alarm), an XML-RPC blind-spot warning, and a complete French translation.

= 2.1.19 =
Clearer attack-type labels on incidents, translatable admin toasts, French translation of the visible tabs, and an integrity-badge verify fix.

= 2.1.18 =
Fix: bulk actions now work when incidents are all resolved; the attack-vector pill shows only for XML-RPC/REST.

= 2.1.17 =
Incidents now record and show the attack vector (XML-RPC / REST / login form) and support bulk resolve/ignore.

= 2.1.16 =
Plain-permalink fixes (Hide Login URL, REST allowlist), activity-log coverage for 2FA/registration/reset, and Honeypot on WooCommerce and frontend forms.

= 2.1.15 =
Fix: fatal TypeError when plugins (e.g. WP Fastest Cache) call WordPress URL builders with off-contract argument types.

= 2.1.14 =
Fix: the prevent_author_enum toggle no longer blocks the legitimate ?author=N filter in the wp-admin Posts/Pages lists.

= 2.1.13 =
Fix: silent 2FA failure on non-trailing-slash permalinks (e.g. /%postname%) - the verify cookie path mismatched the request path.

= 2.1.12 and earlier =
Bug fixes, security hardening and i18n across the 2.1.x and 2.0.x series (Hide Login host-awareness, CSP, lockout delivery, REST scope, IPv6, HTTP/2, Activity Log integrity), through the initial 2.0.0 release. Full per-version notes: CHANGELOG.md in the plugin folder.

== Upgrade Notice ==

= 2.4.4 =
Hardening release: the dashboard threat level no longer under-reports an older critical incident, two-factor logins now fire the canonical wp_login cycle (attempt reset, logging, single-session), lockout escalation is atomic under concurrency, notification webhooks are re-validated against SSRF before every send, and CSV exports neutralise formula injection. Verified locally and on a live WordPress 7.0 / PHP 8.3 install. Recommended for all installs.

= 2.4.3 =
Security fix: mandatory 2FA is now actually enforced at login for enrolled roles past their grace period (reported by the WordPress Plugin Review Team), without locking out existing users - an admin can reopen the setup window from the Users list. Also blocks TOTP code replay, tightens webhook SSRF checks, and neutralises AI-prompt input. Recommended for all installs.

= 2.4.2 =
Coherence release: cleaner deactivation and uninstall, a confirmation before disabling Two-Factor, faster brute-force lookups, and a heads-up when a front-end login plugin (e.g. Ultimate Member) is active. No behaviour change on standard installs. Recommended for all.

= 2.4.1 =
Fixes a "critical error" during password recovery when another plugin re-fires a WordPress hook with an off-contract argument type. Strict parameter hints relaxed, with internal guards, on every core-hook callback plugin-wide. Neutral on standard calls. Recommended for all installs.

= 2.4.0 =
New: an optional Request Firewall (8G-inspired PHP filter) that blocks malicious requests - off by default, starts in monitor mode (logs without blocking), admins never filtered. Plus a first-run onboarding wizard with a one-click safe baseline. All opt-in; existing sites unchanged.

= 2.3.0 =
Account-security release: Password Policy (length/complexity + reject breached passwords via privacy-preserving HIBP), Session Management (idle timeout, max lifetime, single session), and opt-in IP Geolocation. All off by default; nothing changes until you enable it.

= 2.2.0 =
New: the AI Security Briefing turns your last 30 days of activity into a plain-language verdict, an IP picture and prioritised actions, on top of a deterministic facts snapshot. Built on the WordPress 7 native AI Client - uses your own connector, no API key stored, runs on click.

= 2.1.26 =
Fixes email/backup 2FA bouncing to "session expired" on browsers that don't return the verification cookie on submit (some Chrome setups; Firefox worked). The form now also carries the session token, so login works regardless. Recommended if Email 2FA is enabled. Security unchanged.

= 2.1.25 =
Fixes email/backup two-factor verification being rejected ("session expired") in some browsers, notably Chrome, while Firefox worked. The form is now uncached and authenticated by the signed same-site cookie. Recommended if Email 2FA is enabled.

= 2.1.24 =
Fixes a fatal error (HTTP 500 / "network error") during authenticator-app (TOTP) setup on hosts whose wp-config.php does not define AUTH_KEY, such as some Infomaniak installs. Recommended if Two-Factor is enabled. Existing setups are unaffected.

= 2.1.23 =
Fixes the two-factor login screen: the "use a different method" links now work (and email a fresh code when switching to Email), expired/locked sessions explain themselves, and the authenticator-setup button reports errors. Recommended for 2FA users.

= 2.1.22 =
Fixes a Security Score that under-counted active modules: Brute Force and Detection (on by default) are now scored correctly, so the header, the score number and the module list agree. Display and scoring only — recommended for all installs.

= 2.1.21 =
Cosmetic patch: cleaner user-agent labels in the Events table — Jetpack/WordPress.com clients are recognised, and long agents are trimmed at a word boundary with an ellipsis instead of a chopped-off string with a dangling parenthesis.

= 2.1.20 =
Migration-friendly integrity: a security-key change now shows an amber "Keys changed" advisory with one-click chain re-baseline instead of a false "TAMPERED" alarm. Adds an XML-RPC blind-spot warning when Hide Login is on but XML-RPC stays open. Completes the French translation.

= 2.1.19 =
Clearer attack-type labels + descriptions on incidents, French translation of the visible admin tabs, translatable toast notifications, and a fix for the Activity Log integrity badge staying "UNVERIFIED" after a successful verify. Recommended for all installs.

= 2.1.18 =
Patch. Fixes "Select all" / bulk actions when incidents are all resolved (checkboxes now on every card) and only labels the attack vector for XML-RPC/REST (no more misleading "via login form"). Recommended for 2.1.17 users.

= 2.1.17 =
Feature release. Incidents now show the attack vector (XML-RPC / REST / login form) — spot which attempts bypass your hidden login URL — plus bulk mark-resolved/ignore. Adds a vector column to the incidents table (auto migration). Recommended for all installs.

= 2.1.16 =
Bug fix release from an external audit. Fixes plain-permalinks compat (Hide Login URL, REST API allowlist), restores activity-log coverage for 2FA, frontend registration and password reset, and extends Honeypot to WooCommerce + frontend login forms. Recommended for all installs.

= 2.1.15 =
Fixes a fatal TypeError when third-party plugins (e.g. WP Fastest Cache) call WordPress URL builders with off-contract argument types. Strict parameter hints relaxed on seven callbacks; return types unchanged. Neutral on canonical WP calls.

= 2.1.14 =
Bug fix. The prevent_author_enum hardening toggle no longer blocks the legitimate ?author=N filter in wp-admin Posts/Pages lists ("All / Mine / <author>" links). Public enumeration block unchanged. Three-line fix.

= 2.1.13 =
Bug fix. Silent 2FA failure on installs with permalink_structure without trailing slash (e.g. /%postname%) — the verify cookie path mismatched the request path after handle_loaded's normalisation. Fixed cookie path to omit trailing slash. Neutral on trailing-slash installs.

= 2.1.12 =
Bug fix. Hide Login rendered without CSS when both apex and www routed to the same WP (shared hosting). Two fixes: canonical-host 301 in Hide Login + host-aware CSP in Login Page Security Headers. Neutral on single-host installs. New filter login_armor_canonical_host_redirect for opt-out.

= 2.1.11 =
Bug fix for multisite + domain mapping: the Hide Login URL is now host-aware (picks home_url or site_url from HTTP_HOST), fixing a 2.1.9 regression where mapped subsites redirected to /wp-admin/ (404). Standard and headless installs keep working.

= 2.1.10 =
Cosmetic fix. The 404 page served when an anonymous visitor hits `/wp-admin/` with Hide Login enabled now renders as a proper WordPress 404 (body class `error404`, SEO `noindex` meta, theme 404 template) instead of a half-bootstrapped page. No security or functional change.

= 2.1.9 =
Bug fix. Hide Login now builds the rewritten login URL from `site_url()` (matching `wp_login_url()` in WP core) instead of `home_url()`. Fixes silent breakage on multisite headless, WordPress in subdirectory, and reverse-proxy installs. Neutral on standard installs.

= 2.1.8 =
Hygiene release after a full 2.1.7 audit. Three LOW fixes: the webhook stats query no longer warns on fresh installs, the lockout_window option is cleaned on uninstall, and five missing French translations were added. No end-user-visible change.

= 2.1.7 =
Preventive: hardens the Email 2FA enrollment flow. Failed `wp_mail()` no longer leaves a half-committed 2FA state, and a new pre-activation modal forces a real test email + a safety-net check before the user can lock themselves out. Recommended for every install where Email-based 2FA is enabled.

= 2.1.6 =
Preventive release. Eliminates a latent V2.1.3-style fatal risk in the TwoFactor module. Finishes the uninstall.php cleanup (zero residual data). Surfaces Activity Log integrity coverage scope in admin UI. No new features, no DB migration.

= 2.1.4 =
Critical hotfix: 2.1.3 fatal-errored on every fresh install (Class "LoginArmor\ActivityLog\ActivityLog" not found). Sites with Activity Log already enabled were unaffected. Recommended for every install, urgent for new installs.

= 2.1.3 =
Critical hotfix: Hardening "Hide WP version" was stripping cache-buster from our own assets, so updates past 2.1.0 were invisible behind hosting CDNs (LiteSpeed LSADC, Cloudflare). Recommended for every install.

= 2.1.2 =
Critical hotfix: the Settings tab fatal-errored on every fresh install that had not yet enabled the Activity Log module (Class WebhookDispatcher not found). Recommended for every install.

= 2.1.1 =
Activity Log integrity: every row is HMAC-signed and chained, detects any tampering. Optional signed webhook forwarding (SIEM / Slack / Datadog / any HTTPS). New WP-CLI verify-chain. Bundles 6 hardening fixes. Migration automatic. Recommended for every install.

= 2.1.0 =
Security: 2FA pending token moved from URL query string to a signed HttpOnly + SameSite=Strict cookie. Closes URL-leak (browser history / Referer / access logs) and DB-leak (clear token no longer in wp_options). Recommended for every install with 2FA enabled.

= 2.0.5 =
Security audit pass: REST author-enum scope, optional HSTS, IPv6 subnet fix, 0.0.0.0 placeholder DoS skip, .htaccess admin-rules preservation. No regression. Recommended.

= 2.0.4 =
Real fix for the lockout 429 page on hosts with a public page cache (LiteSpeed Cache, WP Rocket, Cloudflare). Recommended after the 2.0.1-2.0.3 sequence.

= 2.0.3 =
Hotfix: HTTP/2 stream termination on LiteSpeed/LSAPI for the branded lockout page. Recommended.

= 2.0.2 =
Critical fix: 429 branded lockout page now reaches the browser. Recommended.

= 2.0.1 =
Branded 429 lockout page on the triggering attempt + Reset Stats UI + correct WP.org banner/icon. Recommended.

= 2.0.0 =
First WordPress.org release of the V2 line. Eight independent security modules. Recommended.
