PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 3.0.4
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v3.0.4
3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 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 All 93 releases
← All changes | includes/class-login-security.php +27 -8 2.11.12 → 3.0.4 View file →
@@ -207,9 +207,9 @@
207 207 return $location;
208 208 }
209 209
210 210 // Check if the redirect is coming from wp-admin area
211 - $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
211 + $request = Vigilante_IP_Utils::request_uri();
212 212
213 213
214 214 // If accessing wp-admin and being redirected to login, show 404 instead
215 215 if ( self::is_wp_admin_request() ) {
@@ -272,13 +272,19 @@
272 272 * home_url(): the admin area can hang from a different path than the
273 273 * site itself (WP_SITEURL vs WP_HOME), so wp-admin matching needs the
274 274 * raw path.
275 275 *
276 + * Raw also means the %XX octets are neither decoded nor deleted. This path
277 + * decides what is the admin area and what is an open endpoint, so it must
278 + * not be a string the client never sent: with sanitize_text_field() a
279 + * /wp-%41admin/page was read as /wp-admin/page, and /wp-admin/admin-ajax.php%20
280 + * as the open endpoint.
281 + *
276 282 * @since 2.9.4
277 283 * @return string
278 284 */
279 285 private static function get_request_uri_path() {
280 - $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
286 + $request = Vigilante_IP_Utils::request_uri();
281 287
282 288 if ( '' === $request ) {
283 289 return '';
284 290 }
@@ -383,9 +389,9 @@
383 389 return;
384 390 }
385 391
386 392 // Get the request URI
387 - $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
393 + $request = Vigilante_IP_Utils::request_uri();
388 394
389 395
390 396 // Check if accessing wp-admin
391 397 if ( ! self::is_wp_admin_request() ) {
@@ -567,9 +573,9 @@
567 573 }
568 574
569 575 // Log the attempt
570 576 if ( $this->activity_log ) {
571 - $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
577 + $request = Vigilante_IP_Utils::request_uri();
572 578 $this->activity_log->log(
573 579 'login',
574 580 'hidden_login_access',
575 581 __( 'Attempt to access hidden wp-login.php', 'vigilante' ),
@@ -606,9 +612,9 @@
606 612 return;
607 613 }
608 614
609 615 if ( $this->activity_log ) {
610 - $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
616 + $request_uri = Vigilante_IP_Utils::request_uri();
611 617 $this->activity_log->log(
612 618 'login',
613 619 'hidden_login_access',
614 620 __( 'Attempt to access hidden wp-login.php', 'vigilante' ),
@@ -622,12 +628,15 @@
622 628
623 629 /**
624 630 * Get the request path without query string
625 631 *
632 + * As the client sent it, percent-encoding included: is_custom_login_request()
633 + * is the one that decodes, because it is the one that compares.
634 + *
626 635 * @return string
627 636 */
628 637 private function get_request_path() {
629 - $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
638 + $request = Vigilante_IP_Utils::request_uri();
630 639
631 640 // Remove query string
632 641 if ( false !== strpos( $request, '?' ) ) {
633 642 $request = strstr( $request, '?', true );
@@ -648,13 +657,23 @@
648 657
649 658 /**
650 659 * Check if this is a request to our custom login URL
651 660 *
661 + * Both sides are percent-decoded before they are compared. The slug is what
662 + * sanitize_title() stored, and in a non-Latin script that is lower-case
663 + * percent-encoded octets (%d0%b2%d1%85%d0%be%d0%b4 for the Cyrillic word
664 + * for "login"), while a browser sends the same address with upper-case hex
665 + * (%D0%B2...) and some tools with raw UTF-8: three spellings of one
666 + * address. The request used to lose its %XX to sanitize_text_field(), so
667 + * the comparison could never succeed: with such a slug the login page was
668 + * hidden and its own address answered 404 (measured on 1 oct 2026 against
669 + * 3.0.1), which locks an administrator out of a site they are signed out of.
670 + *
652 671 * @param string $request The request path.
653 672 * @return bool
654 673 */
655 674 private function is_custom_login_request( $request ) {
656 - return $request === $this->custom_login_slug;
675 + return rawurldecode( $request ) === rawurldecode( $this->custom_login_slug );
657 676 }
658 677
659 678 /**
660 679 * Filter the login URL
@@ -1609,9 +1628,9 @@
1609 1628 require_once VIGILANTE_INCLUDES_DIR . 'class-settings.php';
1610 1629 require_once VIGILANTE_INCLUDES_DIR . 'class-database.php';
1611 1630 require_once VIGILANTE_INCLUDES_DIR . 'class-activity-log.php';
1612 1631
1613 - $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
1632 + $request = Vigilante_IP_Utils::request_uri();
1614 1633
1615 1634 $activity_log = new Vigilante_Activity_Log( new Vigilante_Settings(), new Vigilante_Database() );
1616 1635 $activity_log->log(
1617 1636 'login',