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 / wpcom-block-editor / class-jetpack-wpcom-block-editor.php
jetpack / modules / wpcom-block-editor Last commit date
class-jetpack-wpcom-block-editor.php 4 years ago functions.editor-type.php 4 years ago
class-jetpack-wpcom-block-editor.php
614 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 }
367
368 if ( $this->is_iframed_block_editor() ) {
369 wp_enqueue_script(
370 'wpcom-block-editor-calypso-editor-script',
371 $debug
372 ? '//widgets.wp.com/wpcom-block-editor/calypso.editor.js?minify=false'
373 : '//widgets.wp.com/wpcom-block-editor/calypso.editor.min.js',
374 array(
375 'calypsoify_wpadminmods_js',
376 'jquery',
377 'lodash',
378 'react',
379 'wp-blocks',
380 'wp-data',
381 'wp-hooks',
382 'wp-tinymce',
383 'wp-url',
384 ),
385 $version,
386 true
387 );
388
389 wp_enqueue_style(
390 'wpcom-block-editor-calypso-editor-styles',
391 $debug
392 ? '//widgets.wp.com/wpcom-block-editor/calypso.editor.css?minify=false'
393 : '//widgets.wp.com/wpcom-block-editor/calypso.editor.min.css',
394 array(),
395 $version
396 );
397 }
398 }
399
400 /**
401 * Enqueues the WordPress.com block editor integration assets for both editor and front-end.
402 */
403 public function enqueue_block_assets() {
404 // These styles are manually copied from //widgets.wp.com/wpcom-block-editor/default.view.css in order to
405 // improve the performance by avoiding an extra network request to download the CSS file on every page.
406 wp_add_inline_style( 'wp-block-library', '.has-text-align-justify{text-align:justify;}' );
407 }
408
409 /**
410 * Determines if the current $post contains a justified paragraph block.
411 *
412 * @return boolean true if justified paragraph is found, false otherwise.
413 */
414 public function has_justified_block() {
415 global $post;
416 if ( ! $post instanceof WP_Post ) {
417 return false;
418 }
419
420 if ( ! has_blocks( $post ) ) {
421 return false;
422 }
423
424 return false !== strpos( $post->post_content, '<!-- wp:paragraph {"align":"justify"' );
425 }
426
427 /**
428 * Register the Tiny MCE plugins for the WordPress.com block editor integration.
429 *
430 * @param array $plugin_array An array of external Tiny MCE plugins.
431 * @return array External TinyMCE plugins.
432 */
433 public function add_tinymce_plugins( $plugin_array ) {
434 if ( $this->is_iframed_block_editor() ) {
435 $debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
436
437 $plugin_array['gutenberg-wpcom-iframe-media-modal'] = add_query_arg(
438 'v',
439 gmdate( 'YW' ),
440 $debug
441 ? '//widgets.wp.com/wpcom-block-editor/calypso.tinymce.js?minify=false'
442 : '//widgets.wp.com/wpcom-block-editor/calypso.tinymce.min.js'
443 );
444 }
445
446 return $plugin_array;
447 }
448
449 /**
450 * Ensures the authentication cookies are designated for cross-site access.
451 */
452 private function enable_cross_site_auth_cookies() {
453 /**
454 * Allow plugins to disable the cross-site auth cookies.
455 *
456 * @since 8.1.1
457 *
458 * @param false bool Whether auth cookies should be disabled for cross-site access. False by default.
459 */
460 if ( apply_filters( 'jetpack_disable_cross_site_auth_cookies', false ) ) {
461 return;
462 }
463
464 add_action( 'set_auth_cookie', array( $this, 'set_samesite_auth_cookies' ), 10, 5 );
465 add_action( 'set_logged_in_cookie', array( $this, 'set_samesite_logged_in_cookies' ), 10, 4 );
466 add_filter( 'send_auth_cookies', array( $this, 'maybe_send_cookies' ), 9999 );
467 }
468
469 /**
470 * Checks if we've stored any cookies to send and then sends them
471 * if the send_auth_cookies value is true.
472 *
473 * @param bool $send_cookies The filtered value that determines whether to send auth cookies.
474 */
475 public function maybe_send_cookies( $send_cookies ) {
476
477 if ( ! empty( $this->set_cookie_args ) && $send_cookies ) {
478 array_map(
479 function ( $cookie ) {
480 call_user_func_array( 'jetpack_shim_setcookie', $cookie );
481 },
482 $this->set_cookie_args
483 );
484 $this->set_cookie_args = array();
485 return false;
486 }
487
488 return $send_cookies;
489 }
490
491 /**
492 * Gets the SameSite attribute to use in auth cookies.
493 *
494 * @param bool $secure Whether the connection is secure.
495 * @return string SameSite attribute to use on auth cookies.
496 */
497 public function get_samesite_attr_for_auth_cookies( $secure ) {
498 $samesite = $secure ? 'None' : 'Lax';
499 /**
500 * Filters the SameSite attribute to use in auth cookies.
501 *
502 * @param string $samesite SameSite attribute to use in auth cookies.
503 *
504 * @since 8.1.1
505 */
506 $samesite = apply_filters( 'jetpack_auth_cookie_samesite', $samesite );
507
508 return $samesite;
509 }
510
511 /**
512 * Generates cross-site auth cookies so they can be accessed by WordPress.com.
513 *
514 * @param string $auth_cookie Authentication cookie value.
515 * @param int $expire The time the login grace period expires as a UNIX timestamp.
516 * Default is 12 hours past the cookie's expiration time.
517 * @param int $expiration The time when the authentication cookie expires as a UNIX timestamp.
518 * Default is 14 days from now.
519 * @param int $user_id User ID.
520 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'.
521 */
522 public function set_samesite_auth_cookies( $auth_cookie, $expire, $expiration, $user_id, $scheme ) {
523 if ( wp_startswith( $scheme, 'secure_' ) ) {
524 $secure = true;
525 $auth_cookie_name = SECURE_AUTH_COOKIE;
526 } else {
527 $secure = false;
528 $auth_cookie_name = AUTH_COOKIE;
529 }
530 $samesite = $this->get_samesite_attr_for_auth_cookies( $secure );
531
532 $this->set_cookie_args[] = array(
533 $auth_cookie_name,
534 $auth_cookie,
535 array(
536 'expires' => $expire,
537 'path' => PLUGINS_COOKIE_PATH,
538 'domain' => COOKIE_DOMAIN,
539 'secure' => $secure,
540 'httponly' => true,
541 'samesite' => $samesite,
542 ),
543 );
544
545 $this->set_cookie_args[] = array(
546 $auth_cookie_name,
547 $auth_cookie,
548 array(
549 'expires' => $expire,
550 'path' => ADMIN_COOKIE_PATH,
551 'domain' => COOKIE_DOMAIN,
552 'secure' => $secure,
553 'httponly' => true,
554 'samesite' => $samesite,
555 ),
556 );
557 }
558
559 /**
560 * Generates cross-site logged in cookies so they can be accessed by WordPress.com.
561 *
562 * @param string $logged_in_cookie The logged-in cookie value.
563 * @param int $expire The time the login grace period expires as a UNIX timestamp.
564 * Default is 12 hours past the cookie's expiration time.
565 * @param int $expiration The time when the logged-in cookie expires as a UNIX timestamp.
566 * Default is 14 days from now.
567 * @param int $user_id User ID.
568 */
569 public function set_samesite_logged_in_cookies( $logged_in_cookie, $expire, $expiration, $user_id ) {
570 $secure = is_ssl();
571
572 // Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS.
573 $secure_logged_in_cookie = $secure && 'https' === wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME );
574
575 /** This filter is documented in core/src/wp-includes/pluggable.php */
576 $secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
577
578 /** This filter is documented in core/src/wp-includes/pluggable.php */
579 $secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
580
581 $samesite = $this->get_samesite_attr_for_auth_cookies( $secure_logged_in_cookie );
582
583 $this->set_cookie_args[] = array(
584 LOGGED_IN_COOKIE,
585 $logged_in_cookie,
586 array(
587 'expires' => $expire,
588 'path' => COOKIEPATH,
589 'domain' => COOKIE_DOMAIN,
590 'secure' => $secure_logged_in_cookie,
591 'httponly' => true,
592 'samesite' => $samesite,
593 ),
594 );
595
596 if ( COOKIEPATH !== SITECOOKIEPATH ) {
597 $this->set_cookie_args[] = array(
598 LOGGED_IN_COOKIE,
599 $logged_in_cookie,
600 array(
601 'expires' => $expire,
602 'path' => SITECOOKIEPATH,
603 'domain' => COOKIE_DOMAIN,
604 'secure' => $secure_logged_in_cookie,
605 'httponly' => true,
606 'samesite' => $samesite,
607 ),
608 );
609 }
610 }
611 }
612
613 Jetpack_WPCOM_Block_Editor::init();
614