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-sync/src/modules/class-themes.php +37 -31 12.1.3 → 16.3-beta View file →
@@ -6,8 +6,12 @@
6 6 */
7 7
8 8 namespace Automattic\Jetpack\Sync\Modules;
9 9
10 +if ( ! defined( 'ABSPATH' ) ) {
11 + exit( 0 );
12 +}
13 +
10 14 /**
11 15 * Class to handle sync for themes.
12 16 */
13 17 class Themes extends Module {
@@ -62,10 +66,8 @@
62 66 * Sync handler for a widget edit.
63 67 *
64 68 * @access public
65 69 *
66 - * @todo Implement nonce verification
67 - *
68 70 * @param array $instance The current widget instance's settings.
69 71 * @param array $new_instance Array of new widget settings.
70 72 * @param array $old_instance Array of old widget settings.
71 73 * @param \WP_Widget $widget_object The current widget instance.
@@ -76,9 +78,9 @@
76 78 return $instance;
77 79 }
78 80
79 81 // Don't trigger sync action if this is an ajax request, because Customizer makes them during preview before saving changes.
80 - // phpcs:disable WordPress.Security.NonceVerification.Missing
82 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Not doing anything with or in response to the $_POST data. We're only using $_POST['customized'] to early return if this is an ajax request from Customizer.
81 83 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['customized'] ) ) {
82 84 return $instance;
83 85 }
84 86
@@ -84,9 +86,9 @@
84 86
85 87 $widget = array(
86 88 'name' => $widget_object->name,
87 89 'id' => $widget_object->id,
88 - 'title' => isset( $new_instance['title'] ) ? $new_instance['title'] : '',
90 + 'title' => $new_instance['title'] ?? '',
89 91 );
90 92 /**
91 93 * Trigger action to alert $callable sync listener that a widget was edited.
92 94 *
@@ -111,11 +113,12 @@
111 113 * @param int $network_id ID of the network.
112 114 */
113 115 public function sync_network_allowed_themes_change( $option, $value, $old_value, $network_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
114 116 $all_enabled_theme_slugs = array_keys( $value );
117 + $old_value_count = is_countable( $old_value ) ? count( $old_value ) : 0;
118 + $value_count = is_countable( $value ) ? count( $value ) : 0;
115 119
116 - if ( count( $old_value ) > count( $value ) ) {
117 -
120 + if ( $old_value_count > $value_count ) {
118 121 // Suppress jetpack_network_disabled_themes sync action when theme is deleted.
119 122 $delete_theme_call = $this->get_delete_theme_call();
120 123 if ( ! empty( $delete_theme_call ) ) {
121 124 return;
@@ -183,9 +186,9 @@
183 186 public function detect_theme_edit( $redirect_url ) {
184 187 $url = wp_parse_url( admin_url( $redirect_url ) );
185 188 $theme_editor_url = wp_parse_url( admin_url( 'theme-editor.php' ) );
186 189
187 - if ( $theme_editor_url['path'] !== $url['path'] ) {
190 + if ( ! isset( $url['query'] ) || $theme_editor_url['path'] !== $url['path'] ) {
188 191 return $redirect_url;
189 192 }
190 193
191 194 $query_params = array();
@@ -190,9 +193,9 @@
190 193
191 194 $query_params = array();
192 195 wp_parse_str( $url['query'], $query_params );
193 196 if (
194 - ! isset( $_POST['newcontent'] ) ||
197 + ! isset( $_POST['newcontent'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing -- 'wp_redirect' gets fired for a lot of things. We're only using $_POST['newcontent'] to limit action to redirects from theme edits, we're not doing anything with or in response to the post itself.
195 198 ! isset( $query_params['file'] ) ||
196 199 ! isset( $query_params['theme'] ) ||
197 200 ! isset( $query_params['updated'] )
198 201 ) {
@@ -224,31 +227,30 @@
224 227 *
225 228 * @todo Refactor to use WP_Filesystem instead of fopen()/fclose().
226 229 */
227 230 public function theme_edit_ajax() {
228 - $args = wp_unslash( $_POST );
229 -
230 - if ( empty( $args['theme'] ) ) {
231 + // This validation is based on wp_edit_theme_plugin_file().
232 + if ( empty( $_POST['theme'] ) ) {
231 233 return;
232 234 }
233 235
234 - if ( empty( $args['file'] ) ) {
236 + if ( empty( $_POST['file'] ) ) {
235 237 return;
236 238 }
237 - $file = $args['file'];
239 + $file = wp_unslash( $_POST['file'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
238 240 if ( 0 !== validate_file( $file ) ) {
239 241 return;
240 242 }
241 243
242 - if ( ! isset( $args['newcontent'] ) ) {
244 + if ( ! isset( $_POST['newcontent'] ) ) {
243 245 return;
244 246 }
245 247
246 - if ( ! isset( $args['nonce'] ) ) {
248 + if ( ! isset( $_POST['nonce'] ) ) {
247 249 return;
248 250 }
249 251
250 - $stylesheet = $args['theme'];
252 + $stylesheet = wp_unslash( $_POST['theme'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after.
251 253 if ( 0 !== validate_file( $stylesheet ) ) {
252 254 return;
253 255 }
254 256
@@ -260,9 +262,9 @@
260 262 if ( ! $theme->exists() ) {
261 263 return;
262 264 }
263 265
264 - if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) {
266 + if ( ! wp_verify_nonce( $_POST['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WP core doesn't pre-sanitize nonces either.
265 267 return;
266 268 }
267 269
268 270 if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) {
@@ -306,10 +308,10 @@
306 308 return;
307 309 }
308 310 }
309 311
310 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writeable
311 - if ( ! is_writeable( $real_file ) ) {
312 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
313 + if ( ! is_writable( $real_file ) ) {
312 314 return;
313 315 }
314 316
315 317 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
@@ -381,8 +383,9 @@
381 383 return;
382 384 }
383 385
384 386 if ( 'install' === $details['action'] ) {
387 + // @phan-suppress-next-line PhanUndeclaredMethod -- Checked above. See also https://github.com/phan/phan/issues/1204.
385 388 $theme = $upgrader->theme_info();
386 389 if ( ! $theme instanceof \WP_Theme ) {
387 390 return;
388 391 }
@@ -509,19 +512,25 @@
509 512 *
510 513 * @access public
511 514 *
512 515 * @param array $config Full sync configuration for this sync module.
516 + * @param array $status This module Full Sync status.
513 517 * @param int $send_until The timestamp until the current request can send.
514 - * @param array $state This module Full Sync status.
518 + * @param int $started The timestamp when the full sync started.
515 519 *
516 520 * @return array This module Full Sync status.
517 521 */
518 - public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
522 + public function send_full_sync_actions( $config, $status, $send_until, $started ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
519 523 // we call this instead of do_action when sending immediately.
520 - $this->send_action( 'jetpack_full_sync_theme_data', array( true ) );
524 + $result = $this->send_action( 'jetpack_full_sync_theme_data', array( true ) );
521 525
522 - // The number of actions enqueued, and next module state (true == done).
523 - return array( 'finished' => true );
526 + if ( is_wp_error( $result ) ) {
527 + $status['error'] = true;
528 + return $status;
529 + }
530 + $status['finished'] = true;
531 + $status['sent'] = $status['total'];
532 + return $status;
524 533 }
525 534
526 535 /**
527 536 * Retrieve an estimated number of actions that will be enqueued.
@@ -528,9 +537,9 @@
528 537 *
529 538 * @access public
530 539 *
531 540 * @param array $config Full sync configuration for this sync module.
532 - * @return array Number of items yet to be enqueued.
541 + * @return int Number of items yet to be enqueued.
533 542 */
534 543 public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
535 544 return 1;
536 545 }
@@ -735,22 +744,20 @@
735 744 }
736 745
737 746 $moved_to_inactive_ids = array();
738 747 $moved_to_sidebar = array();
748 + $new_inactive_widgets = $new_value['wp_inactive_widgets'] ?? array();
739 749
740 750 foreach ( $new_value as $sidebar => $new_widgets ) {
741 751 if ( in_array( $sidebar, array( 'array_version', 'wp_inactive_widgets' ), true ) ) {
742 752 continue;
743 753 }
744 - $old_widgets = isset( $old_value[ $sidebar ] )
745 - ? $old_value[ $sidebar ]
746 - : array();
754 + $old_widgets = $old_value[ $sidebar ] ?? array();
747 755
748 756 if ( ! is_array( $new_widgets ) ) {
749 757 $new_widgets = array();
750 758 }
751 -
752 - $moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_value['wp_inactive_widgets'] );
759 + $moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_inactive_widgets );
753 760 $moved_to_inactive_ids = array_merge( $moved_to_inactive_ids, $moved_to_inactive_recently );
754 761
755 762 $moved_to_sidebar_recently = $this->sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar );
756 763 $moved_to_sidebar = array_merge( $moved_to_sidebar, $moved_to_sidebar_recently );
@@ -871,6 +878,5 @@
871 878 }
872 879
873 880 return array( $this->get_theme_info() );
874 881 }
875 -
876 882 }