PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 3.0.0
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v3.0.0
3.0.0 2.11.12 2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 2.9.9 2.9.8 2.9.6 2.9.7 2.9.5 All 88 releases
← All changes | includes/class-login-security.php +48 -8 2.11.103.0.0 View file →
@@ -101,9 +101,9 @@
101 101 // Check lockout before authentication
102 102 add_filter( 'authenticate', array( $this, 'check_lockout' ), 30, 3 );
103 103
104 104 // Track login attempts
105 - add_action( 'wp_login_failed', array( $this, 'handle_failed_login' ) );
105 + add_action( 'wp_login_failed', array( $this, 'handle_failed_login' ), 10, 2 );
106 106 add_action( 'wp_login', array( $this, 'handle_successful_login' ), 10, 2 );
107 107
108 108 // Hide login errors
109 109 if ( ! empty( $this->options['hide_login_errors'] ) ) {
@@ -964,11 +964,10 @@
964 964 // This rejection is ours, not a wrong password. wp_authenticate()
965 965 // still fires wp_login_failed for it, and until 2.11.0 that counted
966 966 // the blocked attempt as one more failure, which rewrote the row's
967 967 // status and produced a fresh lockout, with its critical entry and
968 - // its email, on every POST made during the lockout (S8).
969 - add_filter( 'vigilante_skip_failed_login_count', '__return_true' );
970 -
968 + // its email, on every POST made during the lockout (S8). The error
969 + // code below is what handle_failed_login() reads to leave it alone.
971 970 return new WP_Error(
972 971 'vigilante_lockout',
973 972 sprintf(
974 973 /* translators: %d: Minutes remaining */
@@ -981,15 +980,56 @@
981 980 return $user;
982 981 }
983 982
984 983 /**
984 + * Error codes of the refusals Vigilant issues itself
985 + *
986 + * WordPress treats every WP_Error out of the authenticate chain as a failed
987 + * login and fires wp_login_failed for it (wp-includes/pluggable.php,
988 + * wp_authenticate()). These seven are not wrong passwords: the credentials
989 + * were right and Vigilant stopped the login for a reason of its own, so none
990 + * of them counts towards the brute force lockout.
991 + *
992 + * The rejection identifies itself by the error code it carries. Until
993 + * 2.11.12 each one instead added a filter that stayed registered for the
994 + * rest of the request, which meant that one controlled rejection stopped
995 + * every later failed login of the same request from being counted: measured
996 + * on 17 Sep 2026, three wrong passwords for a different account, sent in the
997 + * same request, none of them recorded. A single XML-RPC system.multicall is
998 + * enough to make that one request.
999 + *
1000 + * @since 2.11.12
1001 + *
1002 + * @var string[]
1003 + */
1004 + const CONTROLLED_REJECTIONS = array(
1005 + 'vigilante_2fa_required', // Two factor asked for, by app or by email.
1006 + 'vigilante_lockout', // This address is already locked out.
1007 + 'vigilante_force_reset', // An administrator forced a password reset.
1008 + 'vigilante_password_expired', // Password expiry policy, over XML-RPC.
1009 + 'pending_approval', // Registration awaiting approval.
1010 + 'session_limit_exceeded', // Too many sessions already open.
1011 + 'email_not_verified', // Email address not verified yet.
1012 + );
1013 +
1014 + /**
985 1015 * Handle failed login attempt
986 1016 *
987 - * @param string $username Username that failed.
1017 + * @since 2.11.12 Receives the WP_Error, so a refusal of Vigilant's own is told
1018 + * apart from a wrong password by what it is and not by a flag
1019 + * left behind for the rest of the request.
1020 + *
1021 + * @param string $username Username that failed.
1022 + * @param WP_Error|null $error The error WordPress is reporting, if any.
988 1023 */
989 - public function handle_failed_login( $username ) {
990 - // Skip counting if this is a Vigilante-controlled rejection
991 - // (pending approval, session limit, email verification, etc.)
1024 + public function handle_failed_login( $username, $error = null ) {
1025 + // A refusal of ours, not a wrong password.
1026 + if ( $error instanceof WP_Error && array_intersect( $error->get_error_codes(), self::CONTROLLED_REJECTIONS ) ) {
1027 + return;
1028 + }
1029 +
1030 + // Kept for anything outside the plugin that marks its own controlled
1031 + // rejection. Nothing inside Vigilant sets it any more.
992 1032 if ( apply_filters( 'vigilante_skip_failed_login_count', false ) ) {
993 1033 return;
994 1034 }
995 1035