| @@ -2,9 +2,11 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * IP matching utilities |
| 4 | 4 | * |
| 5 | 5 | * Shared IP/pattern matching for the firewall and login modules. Supports |
| 6 | - * exact addresses, CIDR ranges and wildcards, for both IPv4 and IPv6. | |
| 6 | + * exact addresses, CIDR ranges and wildcards, for both IPv4 and IPv6. It is | |
| 7 | + * also where the plugin reads what the client sends about the request: the | |
| 8 | + * client address (get_client_ip()) and the request target (request_uri()). | |
| 7 | 9 | * |
| 8 | 10 | * @package Vigilante |
| 9 | 11 | */ |
| 10 | 12 | |
| @@ -866,6 +868,42 @@ | ||
| 866 | 868 | } |
| 867 | 869 | } |
| 868 | 870 | |
| 869 | 871 | return self::resolve_client_ip( $server, $trusted, self::trusted_proxies() ); |
| 872 | + } | |
| 873 | + | |
| 874 | + /** | |
| 875 | + * The request target as the client wrote it: path and query, with the | |
| 876 | + * percent-encoding and the UTF-8 bytes intact. | |
| 877 | + * | |
| 878 | + * The one reader of REQUEST_URI for everything that is not the firewall's | |
| 879 | + * pattern haystack. sanitize_text_field() is the wrong tool for it: it | |
| 880 | + * deletes every %XX octet instead of decoding it (_sanitize_text_fields(), | |
| 881 | + * wp-includes/formatting.php), so /categor%C3%ADa/ came out as /categora/ | |
| 882 | + * and a path in Cyrillic as //. The redirects built from that value landed | |
| 883 | + * on a 404, the hidden login address written in a non-Latin script never | |
| 884 | + * matched, and the activity log kept the evidence with the evidence removed | |
| 885 | + * (measured on 1 oct 2026 against 3.0.1). | |
| 886 | + * | |
| 887 | + * esc_url_raw() keeps the %XX octets and the raw UTF-8, writes a space as | |
| 888 | + * %20, drops what a URL cannot carry (< > " { } ^ ` and control characters) | |
| 889 | + * and leaves a target that starts with a slash alone. Its liberties, none of | |
| 890 | + * which matters to the callers: [ ] become %5B %5D, a ;// becomes ://, and a | |
| 891 | + * target that does not start with a slash (OPTIONS *, an absolute-form | |
| 892 | + * request line) comes back with a scheme added or empty, which is why a | |
| 893 | + * caller that needs a path checks for the leading slash itself. | |
| 894 | + * | |
| 895 | + * Not for the firewall's pattern checks, which need the bytes untouched and | |
| 896 | + * read their own raw copy (Vigilante_Firewall::gather_request_data()). | |
| 897 | + * | |
| 898 | + * @since 3.0.2 | |
| 899 | + * | |
| 900 | + * @return string Path and query as sent, or '' when the server gives none. | |
| 901 | + */ | |
| 902 | + public static function request_uri() { | |
| 903 | + if ( ! isset( $_SERVER['REQUEST_URI'] ) || ! is_string( $_SERVER['REQUEST_URI'] ) ) { | |
| 904 | + return ''; | |
| 905 | + } | |
| 906 | + | |
| 907 | + return esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); | |
| 870 | 908 | } |
| 871 | 909 | } |