PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-status/src/class-visitor.php +73 -5 13.5.2 → 16.3 View file →
@@ -6,8 +6,10 @@
6 6 */
7 7
8 8 namespace Automattic\Jetpack\Status;
9 9
10 +use Automattic\Jetpack\IP\Utils as IP_Utils;
11 +
10 12 /**
11 13 * Visitor class.
12 14 */
13 15 class Visitor {
@@ -14,14 +16,42 @@
14 16
15 17 /**
16 18 * Gets current user IP address.
17 19 *
20 + * Only a value that parses as an IP address is returned. With `$check_all_headers`, the
21 + * forwarded headers are tried in order, a comma-separated list yields its first valid entry,
22 + * and a header holding no valid address is skipped.
23 + *
24 + * A site with a trusted header configured does not use that sweep at all. Brute force
25 + * protection stores which header carries the visitor address, how far back to count in its
26 + * list, and whether that list runs in reverse, all worked out for this site's own proxy
27 + * setup. Once that answer exists it is the answer, including when the request did not carry
28 + * the header — `IP\Utils::get_ip()` falls back to `REMOTE_ADDR` itself in that case. Reading
29 + * the sweep afterwards would let a request that simply omits the trusted header pick its own
30 + * address out of a header it fully controls, and would hand Jetpack two answers for one
31 + * request, since brute force protection resolves the same visitor through `IP\Utils`.
32 + *
33 + * That is a guarantee about which source is consulted, not about the address itself: it
34 + * still assumes the proxy named in the stored configuration rewrites the header. A request
35 + * reaching the origin directly can present that header itself.
36 + *
37 + * The address is normalized by `IP\Utils::clean_ip()`: it is lowercased, anything following an
38 + * " unless " separator is dropped, and a port suffix, IPv6 brackets, or an `::ffff:` IPv4
39 + * mapping are reduced to the bare address. Code comparing this value against a stored or
40 + * configured address should normalize that address the same way.
41 + *
18 42 * @param bool $check_all_headers Check all headers? Default is `false`.
19 43 *
20 - * @return string Current user IP address.
44 + * @return string Current user IP address, or an empty string if no valid address could be determined.
21 45 */
22 46 public function get_ip( $check_all_headers = false ) {
23 47 if ( $check_all_headers ) {
48 + $trusted_header_data = get_site_option( 'trusted_ip_header' );
49 + if ( isset( $trusted_header_data->trusted_header ) ) {
50 + $trusted_ip = IP_Utils::get_ip();
51 + return false !== $trusted_ip ? $trusted_ip : '';
52 + }
53 +
24 54 foreach ( array(
25 55 'HTTP_CF_CONNECTING_IP',
26 56 'HTTP_CLIENT_IP',
27 57 'HTTP_X_FORWARDED_FOR',
@@ -30,16 +60,23 @@
30 60 'HTTP_FORWARDED_FOR',
31 61 'HTTP_FORWARDED',
32 62 'HTTP_VIA',
33 63 ) as $key ) {
34 - if ( ! empty( $_SERVER[ $key ] ) ) {
35 - // @todo Some of these might actually be lists of IPs (e.g. HTTP_X_FORWARDED_FOR) or something else entirely (HTTP_VIA).
36 - return filter_var( wp_unslash( $_SERVER[ $key ] ) );
64 + if ( empty( $_SERVER[ $key ] ) ) {
65 + continue;
37 66 }
67 + // Proxies append to the list, so the leftmost entry is the client.
68 + foreach ( explode( ',', (string) wp_unslash( $_SERVER[ $key ] ) ) as $candidate ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Each entry is validated by clean_ip() below.
69 + $ip = IP_Utils::clean_ip( $candidate );
70 + if ( false !== $ip ) {
71 + return $ip;
72 + }
73 + }
38 74 }
39 75 }
40 76
41 - return ! empty( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
77 + $ip = empty( $_SERVER['REMOTE_ADDR'] ) ? false : IP_Utils::clean_ip( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- clean_ip() validates it.
78 + return false !== $ip ? $ip : '';
42 79 }
43 80
44 81 /**
45 82 * Simple gate check for a11n feature testing purposes using AT_PROXIED_REQUEST constant.
@@ -51,6 +88,37 @@
51 88 * @return bool True if the current request is PROXIED, false otherwise.
52 89 */
53 90 public function is_automattician_feature_flags_only() {
54 91 return ( defined( 'AT_PROXIED_REQUEST' ) && AT_PROXIED_REQUEST );
92 + }
93 +
94 + /**
95 + * Whether the current request should be attributed to an Automattician in analytics.
96 + *
97 + * True for an identified Automattician on WordPress.com Simple, and for A8C-proxied
98 + * requests on both Simple and WoA. Use it to tag Tracks events as internal traffic so
99 + * it can be filtered out of product reporting — this matters most for newly launched
100 + * features, where a small amount of internal testing is a large share of the totals
101 + * and there is no way to separate it after the fact.
102 + *
103 + * IMPORTANT: Reporting signal only, never authorization. A proxied request says
104 + * something about where the request came from, not who the user is.
105 + *
106 + * @since 6.4.0
107 + *
108 + * @return bool True if the request looks like Automattician traffic, false otherwise.
109 + */
110 + public function is_tracking_automattician() {
111 + // Identified Automattician on WordPress.com Simple.
112 + if ( function_exists( 'is_automattician' ) && \is_automattician() ) {
113 + return true;
114 + }
115 +
116 + // Proxied A8C request on WordPress.com Simple.
117 + if ( function_exists( 'wpcom_is_proxied_request' ) && \wpcom_is_proxied_request() ) {
118 + return true;
119 + }
120 +
121 + // Proxied A8C request on WoA.
122 + return $this->is_automattician_feature_flags_only();
55 123 }
56 124 }