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 -30 12.0.3 → 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.
@@ -124,9 +133,8 @@
124 133 array(
125 134 'dependencies' => array( 'jp-tracks' ),
126 135 'enqueue' => $enqueue,
127 136 'in_footer' => true,
128 - 'nonmin_path' => 'js/tracks-callables.js',
129 137 )
130 138 );
131 139 }
132 140
@@ -141,9 +149,8 @@
141 149 array(
142 150 'dependencies' => array( 'jquery' ),
143 151 'enqueue' => true,
144 152 'in_footer' => true,
145 - 'nonmin_path' => 'js/tracks-ajax.js',
146 153 )
147 154 );
148 155
149 156 wp_localize_script(
@@ -160,9 +167,9 @@
160 167 * Send an event in Tracks.
161 168 *
162 169 * @param string $event_type Type of the event.
163 170 * @param array $data Data to send with the event.
164 - * @param mixed $user Username, user_id, or WP_user object.
171 + * @param mixed $user Username, user_id, or WP_User object.
165 172 * @param bool $use_product_prefix Whether to use the object's product name as a prefix to the event type. If
166 173 * set to false, the prefix will be 'jetpack_'.
167 174 */
168 175 public function record_user_event( $event_type, $data = array(), $user = null, $use_product_prefix = true ) {
@@ -170,11 +177,11 @@
170 177 $user = wp_get_current_user();
171 178 }
172 179 $site_url = get_option( 'siteurl' );
173 180
174 - $data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? filter_var( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
175 - $data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
176 - $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'] ) ) : '';
177 184 $data['blog_url'] = $site_url;
178 185 $data['blog_id'] = \Jetpack_Options::get_option( 'id' );
179 186
180 187 // Top level events should not be namespaced.
@@ -190,9 +197,9 @@
190 197
191 198 /**
192 199 * Record an event in Tracks - this is the preferred way to record events from PHP.
193 200 *
194 - * @param mixed $user username, user_id, or WP_user object.
201 + * @param mixed $user username, user_id, or WP_User object.
195 202 * @param string $event_name The name of the event.
196 203 * @param array $properties Custom properties to send with the event.
197 204 * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
198 205 *
@@ -222,10 +229,10 @@
222 229
223 230 /**
224 231 * Determines whether tracking should be enabled.
225 232 *
226 - * @param Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
227 - * @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.
228 235 *
229 236 * @return boolean True if tracking should be enabled, else false.
230 237 */
231 238 public function should_enable_tracking( $terms_of_service, $status ) {
@@ -239,12 +246,12 @@
239 246 /**
240 247 * Procedurally build a Tracks Event Object.
241 248 * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
242 249 *
243 - * @param WP_user $user WP_user object.
244 - * @param string $event_name The name of the event.
245 - * @param array $properties Custom properties to send with the event.
246 - * @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.
247 254 *
248 255 * @return \Jetpack_Tracks_Event|\WP_Error
249 256 */
250 257 private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
@@ -252,9 +259,10 @@
252 259
253 260 $properties['user_lang'] = $user->get( 'WPLANG' );
254 261
255 262 $blog_details = array(
256 - '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' ),
257 265 );
258 266
259 267 $timestamp = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
260 268 $timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
@@ -280,11 +288,11 @@
280 288 * @return array $identity
281 289 */
282 290 public function tracks_get_identity( $user_id ) {
283 291
284 - // Meta is set, and user is still connected. Use WPCOM ID.
292 + // Meta is set, and user is still connected. Use WPCOM ID.
285 293 $wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
286 - 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 ) ) {
287 295 return array(
288 296 '_ut' => 'wpcom:user_id',
289 297 '_ui' => $wpcom_id,
290 298 );
@@ -289,17 +297,21 @@
289 297 '_ui' => $wpcom_id,
290 298 );
291 299 }
292 300
293 - // 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.
294 302 if ( $this->connection->is_user_connected( $user_id ) ) {
295 303 $wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
296 - update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
304 + $wpcom_id = $wpcom_user_data['ID'] ?? null;
297 305
298 - return array(
299 - '_ut' => 'wpcom:user_id',
300 - '_ui' => $wpcom_user_data['ID'],
301 - );
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 + }
302 314 }
303 315
304 316 // User isn't linked at all. Fall back to anonymous ID.
305 317 $anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );