PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.0.3
Jetpack – WP Security, Backup, Speed, & Growth v12.0.3
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 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / comments / base.php
jetpack / modules / comments Last commit date
admin.php 3 years ago base.php 4 years ago comments.php 3 years ago
base.php
327 lines
1 <?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Jetpack comments base file - where the code shared between WP.com Highlander and Jetpack Highlander is defined
4 *
5 * @package automattic/jetpack
6 */
7 /**
8 * All the code shared between WP.com Highlander and Jetpack Highlander
9 */
10 class Highlander_Comments_Base {
11
12 /**
13 * Constructor
14 */
15 public function __construct() {
16 $this->setup_globals();
17 $this->setup_actions();
18 $this->setup_filters();
19 }
20
21 /**
22 * Set any global variables or class variables
23 *
24 * @since JetpackComments (1.4)
25 */
26 protected function setup_globals() {}
27
28 /**
29 * Setup actions for methods in this class
30 *
31 * @since JetpackComments (1.4)
32 */
33 protected function setup_actions() {
34 // Before a comment is posted.
35 add_action( 'pre_comment_on_post', array( $this, 'allow_logged_out_user_to_comment_as_external' ) );
36
37 // After a comment is posted.
38 add_action( 'comment_post', array( $this, 'set_comment_cookies' ) );
39 }
40
41 /**
42 * Setup filters for methods in this class
43 *
44 * @since JetpackComments (1.4)
45 */
46 protected function setup_filters() {
47 add_filter( 'comments_array', array( $this, 'comments_array' ) );
48 add_filter( 'preprocess_comment', array( $this, 'allow_logged_in_user_to_comment_as_guest' ), 0 );
49 }
50
51 /**
52 * Is this a Highlander POST request?
53 * Optionally restrict to one or more credentials slug (facebook, twitter, ...)
54 *
55 * @param mixed ...$args Comments credentials slugs.
56 * @return false|string false if it's not a Highlander POST request. The matching credentials slug if it is.
57 */
58 public function is_highlander_comment_post( ...$args ) {
59
60 // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification should happen in Jetpack_Comments::pre_comment_on_post(). Internal ref for details: p1645643468937519/1645189749.180299-slack-C02HQGKMFJ8
61 if ( empty( $_POST['hc_post_as'] ) ) {
62 return false;
63 }
64 $hc_post_as = wp_unslash( $_POST['hc_post_as'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized here by comparing against known values.
65 // phpcs:enable WordPress.Security.NonceVerification.Missing
66
67 if ( $args ) {
68 foreach ( $args as $id_source ) {
69 if ( $id_source === $hc_post_as ) {
70 return $id_source;
71 }
72 }
73 return false;
74 }
75 return is_string( $hc_post_as ) && in_array( $hc_post_as, $this->id_sources, true ) ? $hc_post_as : false;
76 }
77
78 /**
79 * Signs an array of scalars with the self-hosted blog's Jetpack Token
80 *
81 * If parameter values are not scalars a WP_Error is returned, otherwise a keyed hash value is returned using the HMAC method.
82 *
83 * @param array $parameters Comment parameters.
84 * @param string $key Key used for generating the HMAC variant of the message digest.
85 * @return string HMAC
86 */
87 public static function sign_remote_comment_parameters( $parameters, $key ) {
88 unset(
89 $parameters['sig'], // Don't sign the signature.
90 $parameters['replytocom'] // This parameter is unsigned - it changes dynamically as the comment form moves from parent comment to parent comment.
91 );
92
93 ksort( $parameters );
94
95 $signing = array();
96 foreach ( $parameters as $k => $v ) {
97 if ( ! is_scalar( $v ) ) {
98 return new WP_Error( 'invalid_input', __( 'Invalid request', 'jetpack' ), array( 'status' => 400 ) );
99 }
100
101 $signing[] = "{$k}={$v}";
102 }
103
104 return hash_hmac( 'sha1', implode( ':', $signing ), $key );
105 }
106
107 /**
108 * Adds comment author email and whether the comment is approved to the comments array
109 *
110 * After commenting as a guest while logged in, the user needs to see both:
111 * ( user_id = blah AND comment_approved = 0 )
112 * and ( comment_author_email = blah AND comment_approved = 0 )
113 * Core only does the first since the user is logged in, so this adds the second to the comments array.
114 *
115 * @param array $comments All comment data.
116 * @return array A modified array of comment data.
117 */
118 public function comments_array( $comments ) {
119 global $wpdb, $post;
120
121 $commenter = $this->get_current_commenter();
122
123 if ( ! $commenter['user_id'] ) {
124 return $comments;
125 }
126
127 if ( ! $commenter['comment_author'] ) {
128 return $comments;
129 }
130
131 $in_moderation_comments = $wpdb->get_results(
132 $wpdb->prepare(
133 "SELECT * FROM `$wpdb->comments` WHERE `comment_post_ID` = %d AND `user_id` = 0 AND `comment_author` = %s AND `comment_author_email` = %s AND `comment_approved` = '0' ORDER BY `comment_date_gmt` /* Highlander_Comments_Base::comments_array() */",
134 $post->ID,
135 wp_specialchars_decode( $commenter['comment_author'], ENT_QUOTES ),
136 $commenter['comment_author_email']
137 )
138 );
139
140 if ( ! $in_moderation_comments ) {
141 return $comments;
142 }
143
144 // @todo ZOMG this is a bad idea
145 $comments = array_merge( $comments, $in_moderation_comments );
146 usort( $comments, array( $this, 'sort_comments_by_comment_date_gmt' ) );
147
148 return $comments;
149 }
150
151 /**
152 * Comment sort comparator: comment_date_gmt
153 *
154 * @since JetpackComments (1.4)
155 * @param object $a The first comment to compare dates with.
156 * @param object $b The second comment to compare dates with.
157 * @return int
158 */
159 public function sort_comments_by_comment_date_gmt( $a, $b ) {
160 if ( $a->comment_date_gmt === $b->comment_date_gmt ) {
161 return 0;
162 }
163
164 return $a->comment_date_gmt < $b->comment_date_gmt ? -1 : 1;
165 }
166
167 /**
168 * Get the current commenter's information from their cookie
169 *
170 * @since JetpackComments (1.4)
171 * @return array Commenters information from cookie
172 */
173 protected function get_current_commenter() {
174 // Defaults.
175 $user_id = 0;
176 $comment_author = '';
177 $comment_author_email = '';
178 $comment_author_url = '';
179
180 if ( isset( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) ) {
181 $comment_author = sanitize_text_field( wp_unslash( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) );
182 }
183
184 if ( isset( $_COOKIE[ 'comment_author_email_' . COOKIEHASH ] ) ) {
185 $comment_author_email = sanitize_email( wp_unslash( $_COOKIE[ 'comment_author_email_' . COOKIEHASH ] ) );
186 }
187
188 if ( isset( $_COOKIE[ 'comment_author_url_' . COOKIEHASH ] ) ) {
189 $comment_author_url = esc_url_raw( wp_unslash( $_COOKIE[ 'comment_author_url_' . COOKIEHASH ] ) );
190 }
191
192 if ( is_user_logged_in() ) {
193 $user = wp_get_current_user();
194 $user_id = $user->ID;
195 }
196
197 return compact( 'comment_author', 'comment_author_email', 'comment_author_url', 'user_id' );
198 }
199
200 /**
201 * Allows a logged out user to leave a comment as a facebook or twitter credentialed user.
202 * Overrides WordPress' core comment_registration option to treat these commenters as "registered" (verified) users.
203 *
204 * @since JetpackComments (1.4)
205 */
206 public function allow_logged_out_user_to_comment_as_external() {
207 if ( ! $this->is_highlander_comment_post( 'facebook', 'twitter' ) ) {
208 return;
209 }
210
211 add_filter( 'pre_option_comment_registration', '__return_zero' );
212 add_filter( 'pre_option_require_name_email', '__return_zero' );
213 }
214
215 /**
216 * Allow a logged in user to post as a guest, FB, or twitter credentialed request.
217 * Bypasses WordPress' core overrides that force a logged in user to comment as that user.
218 * Respects comment_registration option.
219 *
220 * @since JetpackComments (1.4)
221 * @param array $comment_data All data for a specific comment.
222 * @return array Modified comment data, or an error if the required fields or a valid email address are not entered.
223 */
224 public function allow_logged_in_user_to_comment_as_guest( $comment_data ) {
225 // Bail if user registration is allowed.
226 if ( get_option( 'comment_registration' ) ) {
227 return $comment_data;
228 }
229
230 // Bail if user is not logged in or not a post request.
231 if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) || ! is_user_logged_in() ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- simple comparison
232 return $comment_data;
233 }
234
235 // Bail if this is not a guest or external service credentialed request.
236 if ( ! $this->is_highlander_comment_post( 'guest', 'facebook', 'twitter' ) ) {
237 return $comment_data;
238 }
239
240 $user = wp_get_current_user();
241
242 foreach ( array(
243 'comment_author' => 'display_name',
244 'comment_author_email' => 'user_email',
245 'comment_author_url' => 'user_url',
246 ) as $comment_field => $user_field ) {
247 if ( addslashes( $user->$user_field ) !== $comment_data[ $comment_field ] ) {
248 return $comment_data; // some other plugin already did something funky.
249 }
250 }
251
252 // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification should happen in Jetpack_Comments::pre_comment_on_post()
253 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization too
254 if ( get_option( 'require_name_email' ) ) {
255 if ( isset( $_POST['email'] ) && 6 > strlen( wp_unslash( $_POST['email'] ) ) || empty( $_POST['author'] ) ) {
256 wp_die( esc_html__( 'Error: please fill the required fields (name, email).', 'jetpack' ), 400 );
257 } elseif ( ! isset( $_POST['email'] ) || ! is_email( wp_unslash( $_POST['email'] ) ) ) {
258 wp_die( esc_html__( 'Error: please enter a valid email address.', 'jetpack' ), 400 );
259 }
260 }
261
262 $author_change = false;
263 foreach ( array(
264 'comment_author' => 'author',
265 'comment_author_email' => 'email',
266 'comment_author_url' => 'url',
267 ) as $comment_field => $post_field ) {
268 if ( ( ! isset( $_POST[ $post_field ] ) || $comment_data[ $comment_field ] !== $_POST[ $post_field ] ) && 'url' !== $post_field ) {
269 $author_change = true;
270 }
271 $comment_data[ $comment_field ] = isset( $_POST[ $post_field ] ) ? wp_unslash( $_POST[ $post_field ] ) : null;
272 }
273 // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
274
275 // Mark as guest comment if name or email were changed.
276 if ( $author_change ) {
277 $comment_data['user_ID'] = 0;
278 $comment_data['user_id'] = $comment_data['user_ID'];
279 }
280
281 return $comment_data;
282 }
283
284 /**
285 * Set the comment cookies or bail if comment is invalid
286 *
287 * @since JetpackComments (1.4)
288 * @param int $comment_id The comment ID.
289 */
290 public function set_comment_cookies( $comment_id ) {
291 // Get comment and bail if it's invalid somehow.
292 $comment = get_comment( $comment_id );
293 if ( empty( $comment ) || is_wp_error( $comment ) ) {
294 return;
295 }
296
297 $id_source = $this->is_highlander_comment_post();
298 if ( empty( $id_source ) ) {
299 return;
300 }
301
302 // Set comment author cookies.
303 // phpcs:ignore WordPress.WP.CapitalPDangit
304 if ( ( 'wordpress' !== $id_source ) && is_user_logged_in() ) {
305 /** This filter is already documented in core/wp-includes/comment-functions.php */
306 $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
307 setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
308 setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
309 setcookie( 'comment_author_url_' . COOKIEHASH, esc_url( $comment->comment_author_url ), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
310 }
311 }
312
313 /**
314 * Get an avatar from Photon
315 *
316 * @since JetpackComments (1.4)
317 * @param string $url The avatar URL.
318 * @param int $size The avatar size.
319 * @return string
320 */
321 protected function photon_avatar( $url, $size ) {
322 $size = (int) $size;
323
324 return jetpack_photon_url( $url, array( 'resize' => "$size,$size" ) );
325 }
326 }
327