PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.5.2
Jetpack – WP Security, Backup, Speed, & Growth v12.5.2
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 / wpcom-block-editor / class-jetpack-wpcom-block-editor.php
jetpack / modules / wpcom-block-editor Last commit date
class-jetpack-wpcom-block-editor.php 3 years ago functions.editor-type.php 4 years ago
class-jetpack-wpcom-block-editor.php
622 lines
1 <?php
2 /**
3 * WordPress.com Block Editor
4 * Allow new block editor posts to be composed on WordPress.com.
5 * This is auto-loaded as of Jetpack v7.4 for sites connected to WordPress.com only.
6 *
7 * @package automattic/jetpack
8 */
9
10 use Automattic\Jetpack\Connection\Tokens;
11 use Automattic\Jetpack\Status\Host;
12
13 /**
14 * WordPress.com Block editor for Jetpack
15 */
16 class Jetpack_WPCOM_Block_Editor {
17 /**
18 * ID of the user who signed the nonce.
19 *
20 * @var int
21 */
22 private $nonce_user_id;
23
24 /**
25 * An array to store auth cookies until we can determine if they should be sent
26 *
27 * @var array
28 */
29 private $set_cookie_args;
30
31 /**
32 * Singleton
33 */
34 public static function init() {
35 static $instance = false;
36
37 if ( ! $instance ) {
38 $instance = new Jetpack_WPCOM_Block_Editor();
39 }
40
41 return $instance;
42 }
43
44 /**
45 * Jetpack_WPCOM_Block_Editor constructor.
46 */
47 private function __construct() {
48 $this->set_cookie_args = array();
49 add_action( 'init', array( $this, 'init_actions' ) );
50 }
51
52 /**
53 * Add in all hooks.
54 */
55 public function init_actions() {
56 // Bail early if Jetpack's block editor extensions are disabled on the site.
57 /* This filter is documented in class.jetpack-gutenberg.php */
58 if ( ! apply_filters( 'jetpack_gutenberg', true ) ) {
59 return;
60 }
61
62 if ( $this->is_iframed_block_editor() ) {
63 add_action( 'admin_init', array( $this, 'disable_send_frame_options_header' ), 9 );
64 add_filter( 'admin_body_class', array( $this, 'add_iframed_body_class' ) );
65 }
66
67 require_once __DIR__ . '/functions.editor-type.php';
68 add_action( 'edit_form_top', 'Jetpack\EditorType\remember_classic_editor' );
69 add_action( 'login_init', array( $this, 'allow_block_editor_login' ), 1 );
70 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ), 9 );
71 add_action( 'enqueue_block_assets', array( $this, 'enqueue_block_assets' ) );
72 add_filter( 'mce_external_plugins', array( $this, 'add_tinymce_plugins' ) );
73 add_filter( 'block_editor_settings_all', 'Jetpack\EditorType\remember_block_editor', 10, 2 );
74
75 $this->enable_cross_site_auth_cookies();
76 }
77
78 /**
79 * Checks if we are embedding the block editor in an iframe in WordPress.com.
80 *
81 * @return bool Whether the current request is from the iframed block editor.
82 */
83 public function is_iframed_block_editor() {
84 global $pagenow;
85
86 // phpcs:ignore WordPress.Security.NonceVerification
87 return ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) && ! empty( $_GET['frame-nonce'] );
88 }
89
90 /**
91 * Prevents frame options header from firing if this is a allowed iframe request.
92 */
93 public function disable_send_frame_options_header() {
94 // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
95 if ( isset( $_GET['frame-nonce'] ) && $this->framing_allowed( $_GET['frame-nonce'] ) ) {
96 remove_action( 'admin_init', 'send_frame_options_header' );
97 }
98 }
99
100 /**
101 * Adds custom admin body class if this is a allowed iframe request.
102 *
103 * @param string $classes Admin body classes.
104 * @return string
105 */
106 public function add_iframed_body_class( $classes ) {
107 // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
108 if ( isset( $_GET['frame-nonce'] ) && $this->framing_allowed( $_GET['frame-nonce'] ) ) {
109 $classes .= ' is-iframed ';
110 }
111
112 return $classes;
113 }
114
115 /**
116 * Checks to see if cookie can be set in current context. If 3rd party cookie blocking
117 * is enabled the editor can't load in iFrame, so emiting X-Frame-Options: DENY will
118 * force the editor to break out of the iFrame.
119 */
120 private function check_iframe_cookie_setting() {
121 if ( ! isset( $_SERVER['QUERY_STRING'] ) || ! strpos( filter_var( wp_unslash( $_SERVER['QUERY_STRING'] ) ), 'calypsoify%3D1%26block-editor' ) || isset( $_COOKIE['wordpress_test_cookie'] ) ) {
122 return;
123 }
124
125 if ( isset( $_SERVER['REQUEST_URI'] ) && empty( $_GET['calypsoify_cookie_check'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
126 header( 'Location: ' . esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) . '&calypsoify_cookie_check=true' ) );
127 exit;
128 }
129
130 header( 'X-Frame-Options: DENY' );
131 exit;
132 }
133
134 /**
135 * Allows to iframe the login page if a user is logged out
136 * while trying to access the block editor from wordpress.com.
137 */
138 public function allow_block_editor_login() {
139 // phpcs:ignore WordPress.Security.NonceVerification
140 if ( empty( $_REQUEST['redirect_to'] ) ) {
141 return;
142 }
143 // phpcs:ignore WordPress.Security.NonceVerification
144 $redirect_to = esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) );
145
146 $this->check_iframe_cookie_setting();
147
148 $query = wp_parse_url( urldecode( $redirect_to ), PHP_URL_QUERY );
149 $args = wp_parse_args( $query );
150
151 // Check nonce and make sure this is a Gutenframe request.
152 if ( ! empty( $args['frame-nonce'] ) && $this->framing_allowed( $args['frame-nonce'] ) ) {
153
154 // If SSO is active, we'll let WordPress.com handle authentication...
155 if ( Jetpack::is_module_active( 'sso' ) ) {
156 // ...but only if it's not an Atomic site. They already do that.
157 if ( ! ( new Host() )->is_woa_site() ) {
158 add_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_true' );
159 }
160 } else {
161 $_REQUEST['interim-login'] = true;
162 add_action( 'wp_login', array( $this, 'do_redirect' ) );
163 add_action( 'login_form', array( $this, 'add_login_html' ) );
164 add_filter( 'wp_login_errors', array( $this, 'add_login_message' ) );
165 remove_action( 'login_init', 'send_frame_options_header' );
166 wp_add_inline_style( 'login', '.interim-login #login{padding-top:8%}' );
167 }
168 }
169 }
170
171 /**
172 * Adds a login message.
173 *
174 * Intended to soften the expectation mismatch of ending up with a login screen rather than the editor.
175 *
176 * @param WP_Error $errors WP Error object.
177 * @return \WP_Error
178 */
179 public function add_login_message( $errors ) {
180 $errors->remove( 'expired' );
181 $errors->add( 'info', __( 'Before we continue, please log in to your Jetpack site.', 'jetpack' ), 'message' );
182
183 return $errors;
184 }
185
186 /**
187 * Maintains the `redirect_to` parameter in login form links.
188 * Adds visual feedback of login in progress.
189 */
190 public function add_login_html() {
191 ?>
192 <input type="hidden" name="redirect_to" value="<?php echo isset( $_REQUEST['redirect_to'] ) ? esc_url( wp_unslash( $_REQUEST['redirect_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ?>" />
193 <script type="application/javascript">
194 document.getElementById( 'loginform' ).addEventListener( 'submit' , function() {
195 document.getElementById( 'wp-submit' ).setAttribute( 'disabled', 'disabled' );
196 document.getElementById( 'wp-submit' ).value = '<?php echo esc_js( __( 'Logging In...', 'jetpack' ) ); ?>';
197 } );
198 </script>
199 <?php
200 }
201
202 /**
203 * Does the redirect to the block editor.
204 */
205 public function do_redirect() {
206 wp_safe_redirect( $GLOBALS['redirect_to'] );
207 exit;
208 }
209
210 /**
211 * Checks whether this is an allowed iframe request.
212 *
213 * @param string $nonce Nonce to verify.
214 * @return bool
215 */
216 public function framing_allowed( $nonce ) {
217 $verified = $this->verify_frame_nonce( $nonce, 'frame-' . Jetpack_Options::get_option( 'id' ) );
218
219 if ( is_wp_error( $verified ) ) {
220 wp_die( $verified ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
221 }
222
223 if ( $verified && ! defined( 'IFRAME_REQUEST' ) ) {
224 define( 'IFRAME_REQUEST', true );
225 }
226
227 return (bool) $verified;
228 }
229
230 /**
231 * Verify that correct nonce was used with time limit.
232 *
233 * The user is given an amount of time to use the token, so therefore, since the
234 * UID and $action remain the same, the independent variable is the time.
235 *
236 * @param string $nonce Nonce that was used in the form to verify.
237 * @param string $action Should give context to what is taking place and be the same when nonce was created.
238 * @return boolean|WP_Error Whether the nonce is valid.
239 */
240 public function verify_frame_nonce( $nonce, $action ) {
241 if ( empty( $nonce ) ) {
242 return false;
243 }
244
245 list( $expiration, $user_id, $hash ) = explode( ':', $nonce, 3 );
246
247 $this->nonce_user_id = (int) $user_id;
248 if ( ! $this->nonce_user_id ) {
249 return false;
250 }
251
252 $token = ( new Tokens() )->get_access_token( $this->nonce_user_id );
253 if ( ! $token ) {
254 return false;
255 }
256
257 /*
258 * Failures must return `false` (blocking the iframe) prior to the
259 * signature verification.
260 */
261
262 add_filter( 'salt', array( $this, 'filter_salt' ), 10, 2 );
263 $expected_hash = wp_hash( "$expiration|$action|{$this->nonce_user_id}", 'jetpack_frame_nonce' );
264 remove_filter( 'salt', array( $this, 'filter_salt' ) );
265
266 if ( ! hash_equals( $hash, $expected_hash ) ) {
267 return false;
268 }
269
270 /*
271 * Failures may return `WP_Error` (showing an error in the iframe) after the
272 * signature verification passes.
273 */
274
275 if ( time() > $expiration ) {
276 return new WP_Error( 'nonce_invalid_expired', 'Expired nonce.', array( 'status' => 401 ) );
277 }
278
279 // Check if it matches the current user, unless they're trying to log in.
280 if ( get_current_user_id() !== $this->nonce_user_id && ! doing_action( 'login_init' ) ) {
281 return new WP_Error( 'nonce_invalid_user_mismatch', 'User ID mismatch.', array( 'status' => 401 ) );
282 }
283
284 return true;
285 }
286
287 /**
288 * Filters the WordPress salt.
289 *
290 * @param string $salt Salt for the given scheme.
291 * @param string $scheme Authentication scheme.
292 * @return string
293 */
294 public function filter_salt( $salt, $scheme ) {
295 if ( 'jetpack_frame_nonce' === $scheme ) {
296 $token = ( new Tokens() )->get_access_token( $this->nonce_user_id );
297
298 if ( $token ) {
299 $salt = $token->secret;
300 }
301 }
302
303 return $salt;
304 }
305
306 /**
307 * Enqueues the WordPress.com block editor integration assets for the editor.
308 */
309 public function enqueue_block_editor_assets() {
310 global $pagenow;
311
312 // Bail if we're not in the post editor, but on the widget settings screen.
313 if ( is_customize_preview() || 'widgets.php' === $pagenow ) {
314 return;
315 }
316
317 $debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
318 $version = gmdate( 'Ymd' );
319
320 wp_enqueue_script(
321 'wpcom-block-editor-default-editor-script',
322 $debug
323 ? '//widgets.wp.com/wpcom-block-editor/default.editor.js?minify=false'
324 : '//widgets.wp.com/wpcom-block-editor/default.editor.min.js',
325 array(
326 'jquery',
327 'lodash',
328 'wp-annotations',
329 'wp-compose',
330 'wp-data',
331 'wp-editor',
332 'wp-element',
333 'wp-rich-text',
334 ),
335 $version,
336 true
337 );
338
339 wp_localize_script(
340 'wpcom-block-editor-default-editor-script',
341 'wpcomGutenberg',
342 array(
343 'richTextToolbar' => array(
344 'justify' => __( 'Justify', 'jetpack' ),
345 'underline' => __( 'Underline', 'jetpack' ),
346 ),
347 )
348 );
349
350 if ( ( new Host() )->is_woa_site() ) {
351 wp_enqueue_script(
352 'wpcom-block-editor-wpcom-editor-script',
353 $debug
354 ? '//widgets.wp.com/wpcom-block-editor/wpcom.editor.js?minify=false'
355 : '//widgets.wp.com/wpcom-block-editor/wpcom.editor.min.js',
356 array(
357 'lodash',
358 'wp-blocks',
359 'wp-data',
360 'wp-dom-ready',
361 'wp-plugins',
362 ),
363 $version,
364 true
365 );
366 wp_enqueue_style(
367 'wpcom-block-editor-wpcom-editor-styles',
368 $debug
369 ? '//widgets.wp.com/wpcom-block-editor/wpcom.editor.css?minify=false'
370 : '//widgets.wp.com/wpcom-block-editor/wpcom.editor.min.css',
371 array(),
372 $version
373 );
374 }
375
376 if ( $this->is_iframed_block_editor() ) {
377 wp_enqueue_script(
378 'wpcom-block-editor-calypso-editor-script',
379 $debug
380 ? '//widgets.wp.com/wpcom-block-editor/calypso.editor.js?minify=false'
381 : '//widgets.wp.com/wpcom-block-editor/calypso.editor.min.js',
382 array(
383 'calypsoify_wpadminmods_js',
384 'jquery',
385 'lodash',
386 'react',
387 'wp-blocks',
388 'wp-data',
389 'wp-hooks',
390 'wp-tinymce',
391 'wp-url',
392 ),
393 $version,
394 true
395 );
396
397 wp_enqueue_style(
398 'wpcom-block-editor-calypso-editor-styles',
399 $debug
400 ? '//widgets.wp.com/wpcom-block-editor/calypso.editor.css?minify=false'
401 : '//widgets.wp.com/wpcom-block-editor/calypso.editor.min.css',
402 array(),
403 $version
404 );
405 }
406 }
407
408 /**
409 * Enqueues the WordPress.com block editor integration assets for both editor and front-end.
410 */
411 public function enqueue_block_assets() {
412 // These styles are manually copied from //widgets.wp.com/wpcom-block-editor/default.view.css in order to
413 // improve the performance by avoiding an extra network request to download the CSS file on every page.
414 wp_add_inline_style( 'wp-block-library', '.has-text-align-justify{text-align:justify;}' );
415 }
416
417 /**
418 * Determines if the current $post contains a justified paragraph block.
419 *
420 * @return boolean true if justified paragraph is found, false otherwise.
421 */
422 public function has_justified_block() {
423 global $post;
424 if ( ! $post instanceof WP_Post ) {
425 return false;
426 }
427
428 if ( ! has_blocks( $post ) ) {
429 return false;
430 }
431
432 return false !== strpos( $post->post_content, '<!-- wp:paragraph {"align":"justify"' );
433 }
434
435 /**
436 * Register the Tiny MCE plugins for the WordPress.com block editor integration.
437 *
438 * @param array $plugin_array An array of external Tiny MCE plugins.
439 * @return array External TinyMCE plugins.
440 */
441 public function add_tinymce_plugins( $plugin_array ) {
442 if ( $this->is_iframed_block_editor() ) {
443 $debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
444
445 $plugin_array['gutenberg-wpcom-iframe-media-modal'] = add_query_arg(
446 'v',
447 gmdate( 'YW' ),
448 $debug
449 ? '//widgets.wp.com/wpcom-block-editor/calypso.tinymce.js?minify=false'
450 : '//widgets.wp.com/wpcom-block-editor/calypso.tinymce.min.js'
451 );
452 }
453
454 return $plugin_array;
455 }
456
457 /**
458 * Ensures the authentication cookies are designated for cross-site access.
459 */
460 private function enable_cross_site_auth_cookies() {
461 /**
462 * Allow plugins to disable the cross-site auth cookies.
463 *
464 * @since 8.1.1
465 *
466 * @param false bool Whether auth cookies should be disabled for cross-site access. False by default.
467 */
468 if ( apply_filters( 'jetpack_disable_cross_site_auth_cookies', false ) ) {
469 return;
470 }
471
472 add_action( 'set_auth_cookie', array( $this, 'set_samesite_auth_cookies' ), 10, 5 );
473 add_action( 'set_logged_in_cookie', array( $this, 'set_samesite_logged_in_cookies' ), 10, 4 );
474 add_filter( 'send_auth_cookies', array( $this, 'maybe_send_cookies' ), 9999 );
475 }
476
477 /**
478 * Checks if we've stored any cookies to send and then sends them
479 * if the send_auth_cookies value is true.
480 *
481 * @param bool $send_cookies The filtered value that determines whether to send auth cookies.
482 */
483 public function maybe_send_cookies( $send_cookies ) {
484
485 if ( ! empty( $this->set_cookie_args ) && $send_cookies ) {
486 array_map(
487 function ( $cookie ) {
488 call_user_func_array( 'jetpack_shim_setcookie', $cookie );
489 },
490 $this->set_cookie_args
491 );
492 $this->set_cookie_args = array();
493 return false;
494 }
495
496 return $send_cookies;
497 }
498
499 /**
500 * Gets the SameSite attribute to use in auth cookies.
501 *
502 * @param bool $secure Whether the connection is secure.
503 * @return string SameSite attribute to use on auth cookies.
504 */
505 public function get_samesite_attr_for_auth_cookies( $secure ) {
506 $samesite = $secure ? 'None' : 'Lax';
507 /**
508 * Filters the SameSite attribute to use in auth cookies.
509 *
510 * @param string $samesite SameSite attribute to use in auth cookies.
511 *
512 * @since 8.1.1
513 */
514 $samesite = apply_filters( 'jetpack_auth_cookie_samesite', $samesite );
515
516 return $samesite;
517 }
518
519 /**
520 * Generates cross-site auth cookies so they can be accessed by WordPress.com.
521 *
522 * @param string $auth_cookie Authentication cookie value.
523 * @param int $expire The time the login grace period expires as a UNIX timestamp.
524 * Default is 12 hours past the cookie's expiration time.
525 * @param int $expiration The time when the authentication cookie expires as a UNIX timestamp.
526 * Default is 14 days from now.
527 * @param int $user_id User ID.
528 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'.
529 */
530 public function set_samesite_auth_cookies( $auth_cookie, $expire, $expiration, $user_id, $scheme ) {
531 if ( wp_startswith( $scheme, 'secure_' ) ) {
532 $secure = true;
533 $auth_cookie_name = SECURE_AUTH_COOKIE;
534 } else {
535 $secure = false;
536 $auth_cookie_name = AUTH_COOKIE;
537 }
538 $samesite = $this->get_samesite_attr_for_auth_cookies( $secure );
539
540 $this->set_cookie_args[] = array(
541 $auth_cookie_name,
542 $auth_cookie,
543 array(
544 'expires' => $expire,
545 'path' => PLUGINS_COOKIE_PATH,
546 'domain' => COOKIE_DOMAIN,
547 'secure' => $secure,
548 'httponly' => true,
549 'samesite' => $samesite,
550 ),
551 );
552
553 $this->set_cookie_args[] = array(
554 $auth_cookie_name,
555 $auth_cookie,
556 array(
557 'expires' => $expire,
558 'path' => ADMIN_COOKIE_PATH,
559 'domain' => COOKIE_DOMAIN,
560 'secure' => $secure,
561 'httponly' => true,
562 'samesite' => $samesite,
563 ),
564 );
565 }
566
567 /**
568 * Generates cross-site logged in cookies so they can be accessed by WordPress.com.
569 *
570 * @param string $logged_in_cookie The logged-in cookie value.
571 * @param int $expire The time the login grace period expires as a UNIX timestamp.
572 * Default is 12 hours past the cookie's expiration time.
573 * @param int $expiration The time when the logged-in cookie expires as a UNIX timestamp.
574 * Default is 14 days from now.
575 * @param int $user_id User ID.
576 */
577 public function set_samesite_logged_in_cookies( $logged_in_cookie, $expire, $expiration, $user_id ) {
578 $secure = is_ssl();
579
580 // Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS.
581 $secure_logged_in_cookie = $secure && 'https' === wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME );
582
583 /** This filter is documented in core/src/wp-includes/pluggable.php */
584 $secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
585
586 /** This filter is documented in core/src/wp-includes/pluggable.php */
587 $secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
588
589 $samesite = $this->get_samesite_attr_for_auth_cookies( $secure_logged_in_cookie );
590
591 $this->set_cookie_args[] = array(
592 LOGGED_IN_COOKIE,
593 $logged_in_cookie,
594 array(
595 'expires' => $expire,
596 'path' => COOKIEPATH,
597 'domain' => COOKIE_DOMAIN,
598 'secure' => $secure_logged_in_cookie,
599 'httponly' => true,
600 'samesite' => $samesite,
601 ),
602 );
603
604 if ( COOKIEPATH !== SITECOOKIEPATH ) {
605 $this->set_cookie_args[] = array(
606 LOGGED_IN_COOKIE,
607 $logged_in_cookie,
608 array(
609 'expires' => $expire,
610 'path' => SITECOOKIEPATH,
611 'domain' => COOKIE_DOMAIN,
612 'secure' => $secure_logged_in_cookie,
613 'httponly' => true,
614 'samesite' => $samesite,
615 ),
616 );
617 }
618 }
619 }
620
621 Jetpack_WPCOM_Block_Editor::init();
622