PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.8.3
Jetpack – WP Security, Backup, Speed, & Growth v12.8.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 2 years ago base.php 2 years ago comments.php 2 years ago
base.php
331 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 use Automattic\Jetpack\Image_CDN\Image_CDN_Core;
9
10 /**
11 * All the code shared between WP.com Highlander and Jetpack Highlander
12 */
13 class Highlander_Comments_Base {
14
15 /**
16 * Constructor
17 */
18 public function __construct() {
19 $this->setup_globals();
20 $this->setup_actions();
21 $this->setup_filters();
22 }
23
24 /**
25 * Set any global variables or class variables
26 *
27 * @since 1.4
28 */
29 protected function setup_globals() {}
30
31 /**
32 * Setup actions for methods in this class
33 *
34 * @since 1.4
35 */
36 protected function setup_actions() {
37 // Before a comment is posted.
38 add_action( 'pre_comment_on_post', array( $this, 'allow_logged_out_user_to_comment_as_external' ) );
39
40 // After a comment is posted.
41 add_action( 'comment_post', array( $this, 'set_comment_cookies' ) );
42 }
43
44 /**
45 * Setup filters for methods in this class
46 *
47 * @since 1.4
48 */
49 protected function setup_filters() {
50 add_filter( 'comments_array', array( $this, 'comments_array' ) );
51 add_filter( 'preprocess_comment', array( $this, 'allow_logged_in_user_to_comment_as_guest' ), 0 );
52 }
53
54 /**
55 * Is this a Highlander POST request?
56 * Optionally restrict to one or more credentials slug (facebook, ...)
57 *
58 * @param mixed ...$args Comments credentials slugs.
59 * @return false|string false if it's not a Highlander POST request. The matching credentials slug if it is.
60 */
61 public function is_highlander_comment_post( ...$args ) {
62
63 // 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
64 if ( empty( $_POST['hc_post_as'] ) ) {
65 return false;
66 }
67 $hc_post_as = wp_unslash( $_POST['hc_post_as'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized here by comparing against known values.
68 // phpcs:enable WordPress.Security.NonceVerification.Missing
69
70 if ( $args ) {
71 foreach ( $args as $id_source ) {
72 if ( $id_source === $hc_post_as ) {
73 return $id_source;
74 }
75 }
76 return false;
77 }
78 return is_string( $hc_post_as ) && in_array( $hc_post_as, $this->id_sources, true ) ? $hc_post_as : false;
79 }
80
81 /**
82 * Signs an array of scalars with the self-hosted blog's Jetpack Token
83 *
84 * If parameter values are not scalars a WP_Error is returned, otherwise a keyed hash value is returned using the HMAC method.
85 *
86 * @param array $parameters Comment parameters.
87 * @param string $key Key used for generating the HMAC variant of the message digest.
88 * @return string HMAC
89 */
90 public static function sign_remote_comment_parameters( $parameters, $key ) {
91 unset(
92 $parameters['sig'], // Don't sign the signature.
93 $parameters['replytocom'] // This parameter is unsigned - it changes dynamically as the comment form moves from parent comment to parent comment.
94 );
95
96 ksort( $parameters );
97
98 $signing = array();
99 foreach ( $parameters as $k => $v ) {
100 if ( ! is_scalar( $v ) ) {
101 return new WP_Error( 'invalid_input', __( 'Invalid request', 'jetpack' ), array( 'status' => 400 ) );
102 }
103
104 $signing[] = "{$k}={$v}";
105 }
106
107 return hash_hmac( 'sha1', implode( ':', $signing ), $key );
108 }
109
110 /**
111 * Adds comment author email and whether the comment is approved to the comments array
112 *
113 * After commenting as a guest while logged in, the user needs to see both:
114 * ( user_id = blah AND comment_approved = 0 )
115 * and ( comment_author_email = blah AND comment_approved = 0 )
116 * Core only does the first since the user is logged in, so this adds the second to the comments array.
117 *
118 * @param array $comments All comment data.
119 * @return array A modified array of comment data.
120 */
121 public function comments_array( $comments ) {
122 global $wpdb, $post;
123
124 $commenter = $this->get_current_commenter();
125
126 if ( ! $commenter['user_id'] ) {
127 return $comments;
128 }
129
130 if ( ! $commenter['comment_author'] ) {
131 return $comments;
132 }
133
134 $in_moderation_comments = $wpdb->get_results(
135 $wpdb->prepare(
136 "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() */",
137 $post->ID,
138 wp_specialchars_decode( $commenter['comment_author'], ENT_QUOTES ),
139 $commenter['comment_author_email']
140 )
141 );
142
143 if ( ! $in_moderation_comments ) {
144 return $comments;
145 }
146
147 // @todo ZOMG this is a bad idea
148 $comments = array_merge( $comments, $in_moderation_comments );
149 usort( $comments, array( $this, 'sort_comments_by_comment_date_gmt' ) );
150
151 return $comments;
152 }
153
154 /**
155 * Comment sort comparator: comment_date_gmt
156 *
157 * @since 1.4
158 * @param object $a The first comment to compare dates with.
159 * @param object $b The second comment to compare dates with.
160 * @return int
161 */
162 public function sort_comments_by_comment_date_gmt( $a, $b ) {
163 if ( $a->comment_date_gmt === $b->comment_date_gmt ) {
164 return 0;
165 }
166
167 return $a->comment_date_gmt < $b->comment_date_gmt ? -1 : 1;
168 }
169
170 /**
171 * Get the current commenter's information from their cookie
172 *
173 * @since 1.4
174 * @return array Commenters information from cookie
175 */
176 protected function get_current_commenter() {
177 // Defaults.
178 $user_id = 0;
179 $comment_author = '';
180 $comment_author_email = '';
181 $comment_author_url = '';
182
183 if ( isset( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) ) {
184 $comment_author = sanitize_text_field( wp_unslash( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) );
185 }
186
187 if ( isset( $_COOKIE[ 'comment_author_email_' . COOKIEHASH ] ) ) {
188 $comment_author_email = sanitize_email( wp_unslash( $_COOKIE[ 'comment_author_email_' . COOKIEHASH ] ) );
189 }
190
191 if ( isset( $_COOKIE[ 'comment_author_url_' . COOKIEHASH ] ) ) {
192 $comment_author_url = esc_url_raw( wp_unslash( $_COOKIE[ 'comment_author_url_' . COOKIEHASH ] ) );
193 }
194
195 if ( is_user_logged_in() ) {
196 $user = wp_get_current_user();
197 $user_id = $user->ID;
198 }
199
200 return compact( 'comment_author', 'comment_author_email', 'comment_author_url', 'user_id' );
201 }
202
203 /**
204 * Allows a logged out user to leave a comment as a facebook/wp.com credentialed user.
205 * Overrides WordPress' core comment_registration option to treat these commenters as "registered" (verified) users.
206 *
207 * @since 1.4
208 */
209 public function allow_logged_out_user_to_comment_as_external() {
210 // phpcs:ignore WordPress.WP.CapitalPDangit.MisspelledInText
211 if ( ! $this->is_highlander_comment_post( 'facebook', 'wordpress' ) ) {
212 return;
213 }
214
215 add_filter( 'pre_option_comment_registration', '__return_zero' );
216 add_filter( 'pre_option_require_name_email', '__return_zero' );
217 }
218
219 /**
220 * Allow a logged in user to post as a guest, or FB credentialed request.
221 * Bypasses WordPress' core overrides that force a logged in user to comment as that user.
222 * Respects comment_registration option.
223 *
224 * @since 1.4
225 * @param array $comment_data All data for a specific comment.
226 * @return array Modified comment data, or an error if the required fields or a valid email address are not entered.
227 */
228 public function allow_logged_in_user_to_comment_as_guest( $comment_data ) {
229 // Bail if user registration is allowed.
230 if ( get_option( 'comment_registration' ) ) {
231 return $comment_data;
232 }
233
234 // Bail if user is not logged in or not a post request.
235 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
236 return $comment_data;
237 }
238
239 // Bail if this is not a guest or external service credentialed request.
240 if ( ! $this->is_highlander_comment_post( 'guest', 'facebook' ) ) {
241 return $comment_data;
242 }
243
244 $user = wp_get_current_user();
245
246 foreach ( array(
247 'comment_author' => 'display_name',
248 'comment_author_email' => 'user_email',
249 'comment_author_url' => 'user_url',
250 ) as $comment_field => $user_field ) {
251 if ( addslashes( $user->$user_field ) !== $comment_data[ $comment_field ] ) {
252 return $comment_data; // some other plugin already did something funky.
253 }
254 }
255
256 // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification should happen in Jetpack_Comments::pre_comment_on_post()
257 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization too
258 if ( get_option( 'require_name_email' ) ) {
259 if ( isset( $_POST['email'] ) && 6 > strlen( wp_unslash( $_POST['email'] ) ) || empty( $_POST['author'] ) ) {
260 wp_die( esc_html__( 'Error: please fill the required fields (name, email).', 'jetpack' ), 400 );
261 } elseif ( ! isset( $_POST['email'] ) || ! is_email( wp_unslash( $_POST['email'] ) ) ) {
262 wp_die( esc_html__( 'Error: please enter a valid email address.', 'jetpack' ), 400 );
263 }
264 }
265
266 $author_change = false;
267 foreach ( array(
268 'comment_author' => 'author',
269 'comment_author_email' => 'email',
270 'comment_author_url' => 'url',
271 ) as $comment_field => $post_field ) {
272 if ( ( ! isset( $_POST[ $post_field ] ) || $comment_data[ $comment_field ] !== $_POST[ $post_field ] ) && 'url' !== $post_field ) {
273 $author_change = true;
274 }
275 $comment_data[ $comment_field ] = isset( $_POST[ $post_field ] ) ? wp_unslash( $_POST[ $post_field ] ) : null;
276 }
277 // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
278
279 // Mark as guest comment if name or email were changed.
280 if ( $author_change ) {
281 $comment_data['user_ID'] = 0;
282 $comment_data['user_id'] = $comment_data['user_ID'];
283 }
284
285 return $comment_data;
286 }
287
288 /**
289 * Set the comment cookies or bail if comment is invalid
290 *
291 * @since 1.4
292 * @param int $comment_id The comment ID.
293 */
294 public function set_comment_cookies( $comment_id ) {
295 // Get comment and bail if it's invalid somehow.
296 $comment = get_comment( $comment_id );
297 if ( empty( $comment ) || is_wp_error( $comment ) ) {
298 return;
299 }
300
301 $id_source = $this->is_highlander_comment_post();
302 if ( empty( $id_source ) ) {
303 return;
304 }
305
306 // Set comment author cookies.
307 // phpcs:ignore WordPress.WP.CapitalPDangit
308 if ( ( 'wordpress' !== $id_source ) && is_user_logged_in() ) {
309 /** This filter is already documented in core/wp-includes/comment-functions.php */
310 $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
311 setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
312 setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
313 setcookie( 'comment_author_url_' . COOKIEHASH, esc_url( $comment->comment_author_url ), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
314 }
315 }
316
317 /**
318 * Get an avatar from Photon
319 *
320 * @since 1.4
321 * @param string $url The avatar URL.
322 * @param int $size The avatar size.
323 * @return string
324 */
325 protected function photon_avatar( $url, $size ) {
326 $size = (int) $size;
327
328 return Image_CDN_Core::cdn_url( $url, array( 'resize' => "$size,$size" ) );
329 }
330 }
331