PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
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 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-connection/src/class-tracking.php +42 -28 12.4.2 → 16.3-beta View file →
@@ -6,8 +6,10 @@
6 6 */
7 7
8 8 namespace Automattic\Jetpack;
9 9
10 +use Automattic\Jetpack\IP\Utils as IP_Utils;
11 +
10 12 /**
11 13 * The Tracking class, used to record events in wpcom
12 14 */
13 15 class Tracking {
@@ -37,10 +39,10 @@
37 39
38 40 /**
39 41 * Creates the Tracking object.
40 42 *
41 - * @param String $product_name the slug of the product that we are tracking.
42 - * @param Automattic\Jetpack\Connection\Manager $connection the connection manager object.
43 + * @param string $product_name the slug of the product that we are tracking.
44 + * @param \Automattic\Jetpack\Connection\Manager $connection the connection manager object.
43 45 */
44 46 public function __construct( $product_name = 'jetpack', $connection = null ) {
45 47 $this->product_name = $product_name;
46 48 $this->connection = $connection;
@@ -75,9 +77,10 @@
75 77 || ! wp_verify_nonce( $_REQUEST['tracksNonce'], 'jp-tracks-ajax-nonce' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WP core doesn't pre-sanitize nonces either.
76 78 ) {
77 79 wp_send_json_error(
78 80 __( 'You aren’t authorized to do that.', 'jetpack-connection' ),
79 - 403
81 + 403,
82 + JSON_UNESCAPED_SLASHES
80 83 );
81 84 }
82 85
83 86 if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] ) ) {
@@ -82,24 +85,30 @@
82 85
83 86 if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] ) ) {
84 87 wp_send_json_error(
85 88 __( 'No valid event name or type.', 'jetpack-connection' ),
86 - 403
89 + 403,
90 + JSON_UNESCAPED_SLASHES
87 91 );
92 + exit; // @phan-suppress-current-line PhanPluginUnreachableCode -- @todo Remove when WP 7.1 is the minimum version.
88 93 }
89 94
90 95 $tracks_data = array();
91 96 if ( 'click' === $_REQUEST['tracksEventType'] && isset( $_REQUEST['tracksEventProp'] ) ) {
92 97 if ( is_array( $_REQUEST['tracksEventProp'] ) ) {
93 - $tracks_data = array_map( 'filter_var', wp_unslash( $_REQUEST['tracksEventProp'] ) );
98 + // map_deep() rather than array_map(): the request is client-supplied and may nest,
99 + // and sanitize_text_field() returns an empty string when handed an array.
100 + $tracks_data = map_deep( wp_unslash( $_REQUEST['tracksEventProp'] ), 'sanitize_text_field' );
94 101 } else {
95 - $tracks_data = array( 'clicked' => filter_var( wp_unslash( $_REQUEST['tracksEventProp'] ) ) );
102 + $tracks_data = array( 'clicked' => sanitize_text_field( wp_unslash( $_REQUEST['tracksEventProp'] ) ) );
96 103 }
97 104 }
98 105
99 - $this->record_user_event( filter_var( wp_unslash( $_REQUEST['tracksEventName'] ) ), $tracks_data, null, false );
106 + // Tracks only accepts lowercase alphanumerics and underscores in an event name
107 + // (see Jetpack_Tracks_Event::EVENT_NAME_REGEX), which is what sanitize_key() permits.
108 + $this->record_user_event( sanitize_key( wp_unslash( $_REQUEST['tracksEventName'] ) ), $tracks_data, null, false );
100 109
101 - wp_send_json_success();
110 + wp_send_json_success( null, null, JSON_UNESCAPED_SLASHES );
102 111 }
103 112
104 113 /**
105 114 * Register script necessary for tracking.
@@ -158,9 +167,9 @@
158 167 * Send an event in Tracks.
159 168 *
160 169 * @param string $event_type Type of the event.
161 170 * @param array $data Data to send with the event.
162 - * @param mixed $user Username, user_id, or WP_user object.
171 + * @param mixed $user Username, user_id, or WP_User object.
163 172 * @param bool $use_product_prefix Whether to use the object's product name as a prefix to the event type. If
164 173 * set to false, the prefix will be 'jetpack_'.
165 174 */
166 175 public function record_user_event( $event_type, $data = array(), $user = null, $use_product_prefix = true ) {
@@ -168,11 +177,11 @@
168 177 $user = wp_get_current_user();
169 178 }
170 179 $site_url = get_option( 'siteurl' );
171 180
172 - $data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? filter_var( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
173 - $data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
174 - $data['_lg'] = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? filter_var( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) : '';
181 + $data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
182 + $data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? (string) IP_Utils::clean_ip( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- clean_ip() validates the address.
183 + $data['_lg'] = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) : '';
175 184 $data['blog_url'] = $site_url;
176 185 $data['blog_id'] = \Jetpack_Options::get_option( 'id' );
177 186
178 187 // Top level events should not be namespaced.
@@ -188,9 +197,9 @@
188 197
189 198 /**
190 199 * Record an event in Tracks - this is the preferred way to record events from PHP.
191 200 *
192 - * @param mixed $user username, user_id, or WP_user object.
201 + * @param mixed $user username, user_id, or WP_User object.
193 202 * @param string $event_name The name of the event.
194 203 * @param array $properties Custom properties to send with the event.
195 204 * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
196 205 *
@@ -220,10 +229,10 @@
220 229
221 230 /**
222 231 * Determines whether tracking should be enabled.
223 232 *
224 - * @param Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
225 - * @param Automattic\Jetpack\Status $status A Status object.
233 + * @param \Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
234 + * @param \Automattic\Jetpack\Status $status A Status object.
226 235 *
227 236 * @return boolean True if tracking should be enabled, else false.
228 237 */
229 238 public function should_enable_tracking( $terms_of_service, $status ) {
@@ -237,12 +246,12 @@
237 246 /**
238 247 * Procedurally build a Tracks Event Object.
239 248 * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
240 249 *
241 - * @param WP_user $user WP_user object.
242 - * @param string $event_name The name of the event.
243 - * @param array $properties Custom properties to send with the event.
244 - * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
250 + * @param \WP_User $user WP_User object.
251 + * @param string $event_name The name of the event.
252 + * @param array $properties Custom properties to send with the event.
253 + * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
245 254 *
246 255 * @return \Jetpack_Tracks_Event|\WP_Error
247 256 */
248 257 private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
@@ -250,9 +259,10 @@
250 259
251 260 $properties['user_lang'] = $user->get( 'WPLANG' );
252 261
253 262 $blog_details = array(
254 - 'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
263 + 'blog_lang' => $properties['blog_lang'] ?? get_bloginfo( 'language' ),
264 + 'blog_id' => \Jetpack_Options::get_option( 'id' ),
255 265 );
256 266
257 267 $timestamp = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
258 268 $timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
@@ -278,11 +288,11 @@
278 288 * @return array $identity
279 289 */
280 290 public function tracks_get_identity( $user_id ) {
281 291
282 - // Meta is set, and user is still connected. Use WPCOM ID.
292 + // Meta is set, and user is still connected. Use WPCOM ID.
283 293 $wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
284 - if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
294 + if ( $wpcom_id && is_string( $wpcom_id ) && $this->connection->is_user_connected( $user_id ) ) {
285 295 return array(
286 296 '_ut' => 'wpcom:user_id',
287 297 '_ui' => $wpcom_id,
288 298 );
@@ -287,17 +297,21 @@
287 297 '_ui' => $wpcom_id,
288 298 );
289 299 }
290 300
291 - // User is connected, but no meta is set yet. Use WPCOM ID and set meta.
301 + // User is connected, but no meta is set yet. Use WPCOM ID and set meta.
292 302 if ( $this->connection->is_user_connected( $user_id ) ) {
293 303 $wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
294 - update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
304 + $wpcom_id = $wpcom_user_data['ID'] ?? null;
295 305
296 - return array(
297 - '_ut' => 'wpcom:user_id',
298 - '_ui' => $wpcom_user_data['ID'],
299 - );
306 + if ( is_string( $wpcom_id ) ) {
307 + update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_id );
308 +
309 + return array(
310 + '_ut' => 'wpcom:user_id',
311 + '_ui' => $wpcom_id,
312 + );
313 + }
300 314 }
301 315
302 316 // User isn't linked at all. Fall back to anonymous ID.
303 317 $anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );