From 833379c5b14d8f300032759f920c18e8830e46a1 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Mon, 10 Jun 2024 16:48:20 -0300 Subject: [PATCH] Check nonce when do_sync is passed --- assets/js/features/apps/features.js | 3 ++- assets/js/features/config.js | 4 ++-- assets/js/sync/src/utilities.js | 1 + includes/classes/AdminNotices.php | 12 +++--------- includes/classes/Screen.php | 14 +++++++------- includes/classes/Screen/Features.php | 1 + includes/classes/Screen/Sync.php | 4 ++-- includes/dashboard.php | 4 +--- includes/utils.php | 12 +++++++++++- tests/php/TestScreen.php | 9 +++++++-- tests/php/TestUtils.php | 4 ++-- 11 files changed, 39 insertions(+), 29 deletions(-) diff --git a/assets/js/features/apps/features.js b/assets/js/features/apps/features.js index dd239c7fab..35feb2754b 100644 --- a/assets/js/features/apps/features.js +++ b/assets/js/features/apps/features.js @@ -9,7 +9,7 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies. */ import { useSettingsScreen } from '../../settings-screen'; -import { syncUrl } from '../config'; +import { syncUrl, syncNonce } from '../config'; import { useFeatureSettings } from '../provider'; import Feature from '../components/feature'; import Tab from '../components/tab'; @@ -44,6 +44,7 @@ export default () => { const url = new URL(syncUrl); url.searchParams.append('do_sync', 'features'); + url.searchParams.append('ep_sync_nonce', syncNonce); return url.toString(); }, []); diff --git a/assets/js/features/config.js b/assets/js/features/config.js index 0ef95bfe9b..346eb1c79d 100644 --- a/assets/js/features/config.js +++ b/assets/js/features/config.js @@ -1,7 +1,7 @@ /** * Window dependencies. */ -const { apiUrl, epioLogoUrl, features, indexMeta, settings, settingsDraft, syncUrl } = +const { apiUrl, epioLogoUrl, features, indexMeta, settings, settingsDraft, syncUrl, syncNonce } = window.epDashboard; -export { apiUrl, epioLogoUrl, features, indexMeta, settings, settingsDraft, syncUrl }; +export { apiUrl, epioLogoUrl, features, indexMeta, settings, settingsDraft, syncUrl, syncNonce }; diff --git a/assets/js/sync/src/utilities.js b/assets/js/sync/src/utilities.js index 5d89cde5a9..db0162ec17 100644 --- a/assets/js/sync/src/utilities.js +++ b/assets/js/sync/src/utilities.js @@ -6,6 +6,7 @@ export const clearSyncParam = () => { const url = new URL(document.location.href); url.searchParams.delete('do_sync'); + url.searchParams.delete('ep_sync_nonce'); window.history.replaceState({}, document.title, url); }; diff --git a/includes/classes/AdminNotices.php b/includes/classes/AdminNotices.php index 119b8ff396..724842177c 100644 --- a/includes/classes/AdminNotices.php +++ b/includes/classes/AdminNotices.php @@ -116,12 +116,6 @@ protected function process_using_autosuggest_defaults_notice() { return false; } - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { - $url = admin_url( 'network/admin.php?page=elasticpress&do_sync' ); - } else { - $url = admin_url( 'admin.php?page=elasticpress&do_sync' ); - } - return [ 'html' => sprintf( esc_html__( 'Autosuggest feature is enabled. If documents feature is enabled, your media will also become searchable in the frontend.', 'elasticpress' ) ), 'type' => 'info', @@ -177,7 +171,7 @@ protected function process_auto_activate_sync_notice() { return false; } - if ( isset( $_GET['do_sync'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( Utils\isset_do_sync_parameter() ) { return false; } @@ -252,7 +246,7 @@ protected function process_upgrade_sync_notice() { return false; } - if ( isset( $_GET['do_sync'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( Utils\isset_do_sync_parameter() ) { return false; } @@ -316,7 +310,7 @@ protected function process_no_sync_notice() { return false; } - if ( isset( $_GET['do_sync'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( Utils\isset_do_sync_parameter() ) { return false; } diff --git a/includes/classes/Screen.php b/includes/classes/Screen.php index 14d66e1e51..bf4fb4416a 100644 --- a/includes/classes/Screen.php +++ b/includes/classes/Screen.php @@ -101,7 +101,7 @@ public function determine_screen() { $this->screen = 'install'; if ( 'elasticpress' === $_GET['page'] ) { - if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || isset( $_GET['do_sync'] ) ) ) { + if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || Utils\isset_do_sync_parameter() ) ) { if ( Utils\is_top_level_admin_context() ) { $this->screen = 'dashboard'; } else { @@ -109,27 +109,27 @@ public function determine_screen() { } } } elseif ( 'elasticpress-settings' === $_GET['page'] ) { - if ( true === $install_status || 2 === $install_status || isset( $_GET['do_sync'] ) ) { + if ( true === $install_status || 2 === $install_status || Utils\isset_do_sync_parameter() ) { $this->screen = 'settings'; } } elseif ( 'elasticpress-health' === $_GET['page'] ) { - if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || isset( $_GET['do_sync'] ) ) ) { + if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || Utils\isset_do_sync_parameter() ) ) { $this->screen = 'health'; } } elseif ( 'elasticpress-weighting' === $_GET['page'] ) { - if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || isset( $_GET['do_sync'] ) ) ) { + if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || Utils\isset_do_sync_parameter() ) ) { $this->screen = 'weighting'; } } elseif ( 'elasticpress-synonyms' === $_GET['page'] ) { - if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || isset( $_GET['do_sync'] ) ) ) { + if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || Utils\isset_do_sync_parameter() ) ) { $this->screen = 'synonyms'; } } elseif ( 'elasticpress-sync' === $_GET['page'] ) { - if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || isset( $_GET['do_sync'] ) ) ) { + if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || Utils\isset_do_sync_parameter() ) ) { $this->screen = 'sync'; } } elseif ( 'elasticpress-status-report' === $_GET['page'] ) { - if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || isset( $_GET['do_sync'] ) ) ) { + if ( ! isset( $_GET['install_complete'] ) && ( true === $install_status || Utils\isset_do_sync_parameter() ) ) { $this->screen = 'status-report'; } } diff --git a/includes/classes/Screen/Features.php b/includes/classes/Screen/Features.php index 5dab17a3d8..06adc0c8a9 100644 --- a/includes/classes/Screen/Features.php +++ b/includes/classes/Screen/Features.php @@ -78,6 +78,7 @@ public function admin_enqueue_scripts() { 'settings' => $store->get_feature_settings(), 'settingsDraft' => $store->get_feature_settings_draft(), 'syncUrl' => $sync_url, + 'syncNonce' => wp_create_nonce( 'ep_sync_nonce' ), ]; wp_localize_script( 'ep_features_script', 'epDashboard', $data ); diff --git a/includes/classes/Screen/Sync.php b/includes/classes/Screen/Sync.php index c2fc69f7e3..d169e77505 100644 --- a/includes/classes/Screen/Sync.php +++ b/includes/classes/Screen/Sync.php @@ -75,14 +75,14 @@ public function admin_enqueue_scripts() { $data = [ 'apiUrl' => rest_url( 'elasticpress/v1/sync' ), - 'autoIndex' => isset( $_GET['do_sync'] ) && ( ! defined( 'EP_DASHBOARD_SYNC' ) || EP_DASHBOARD_SYNC ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended + 'autoIndex' => Utils\isset_do_sync_parameter() && ( ! defined( 'EP_DASHBOARD_SYNC' ) || EP_DASHBOARD_SYNC ), 'indexMeta' => Utils\get_indexing_status(), 'indexables' => array_map( fn( $indexable) => [ $indexable->slug, $indexable->labels['plural'] ], $indexables ), 'isEpio' => Utils\is_epio(), 'nonce' => wp_create_nonce( 'wp_rest' ), 'postTypes' => array_map( fn( $post_type ) => [ $post_type, get_post_type_object( $post_type )->labels->name ], $post_types ), 'syncHistory' => $sync_history, - 'syncTrigger' => ! empty( $_GET['do_sync'] ) ? sanitize_text_field( wp_unslash( $_GET['do_sync'] ) ) : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended + 'syncTrigger' => Utils\isset_do_sync_parameter() ? sanitize_text_field( wp_unslash( $_GET['do_sync'] ) ) : null, // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated ]; wp_localize_script( 'ep_sync_scripts', 'epDash', $data ); diff --git a/includes/dashboard.php b/includes/dashboard.php index 8c83a6937c..0b59a5d801 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -531,9 +531,7 @@ function action_admin_enqueue_dashboard_scripts() { wp_set_script_translations( 'ep_dashboard_scripts', 'elasticpress' ); - $sync_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? - network_admin_url( 'admin.php?page=elasticpress-sync&do_sync' ) : - admin_url( 'admin.php?page=elasticpress-sync&do_sync' ); + $sync_url = Utils\get_sync_url( true ); $skip_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? network_admin_url( 'admin.php?page=elasticpress' ) : diff --git a/includes/utils.php b/includes/utils.php index be0f5bc7d7..b4f9ffb45b 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -773,13 +773,23 @@ function get_asset_info( $slug, $attribute = null ) { function get_sync_url( bool $do_sync = false ) : string { $page = 'admin.php?page=elasticpress-sync'; if ( $do_sync ) { - $page .= '&do_sync'; + $page .= '&do_sync&ep_sync_nonce=' . wp_create_nonce( 'ep_sync_nonce' ); } return ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? network_admin_url( $page ) : admin_url( $page ); } +/** + * Check if the `do_sync` parameter is set and the nonce is valid. + * + * @since 5.1.2 + * @return boolean + */ +function isset_do_sync_parameter() : bool { + return isset( $_GET['do_sync'] ) && ! empty( $_GET['ep_sync_nonce'] ) && wp_verify_nonce( sanitize_key( $_GET['ep_sync_nonce'] ), 'ep_sync_nonce' ); +} + /** * Generate a common prefix to be used while generating a request ID. * diff --git a/tests/php/TestScreen.php b/tests/php/TestScreen.php index 514b000ca6..03eabb9c49 100644 --- a/tests/php/TestScreen.php +++ b/tests/php/TestScreen.php @@ -63,6 +63,10 @@ public function tear_down() { if ( isset( $_GET['do_sync'] ) ) { unset( $_GET['do_sync'] ); } + + if ( isset( $_GET['ep_sync_nonce'] ) ) { + unset( $_GET['ep_sync_nonce'] ); + } // phpcs:enable } @@ -254,8 +258,9 @@ public function testDetermineScreenDashboardInstall3DoSync() { add_filter( 'ep_install_status', $set_install_status ); - $_GET['page'] = 'elasticpress'; - $_GET['do_sync'] = 1; + $_GET['page'] = 'elasticpress'; + $_GET['do_sync'] = 1; + $_GET['ep_sync_nonce'] = wp_create_nonce( 'ep_sync_nonce' ); ElasticPress\Installer::factory()->calculate_install_status(); ElasticPress\Screen::factory()->determine_screen(); diff --git a/tests/php/TestUtils.php b/tests/php/TestUtils.php index 67ab8d6ca3..2ffad0141d 100644 --- a/tests/php/TestUtils.php +++ b/tests/php/TestUtils.php @@ -206,9 +206,9 @@ public function testGetSyncUrl() { */ $sync_url = ElasticPress\Utils\get_sync_url( true ); if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { - $this->assertStringContainsString( 'wp-admin/network/admin.php?page=elasticpress-sync&do_sync', $sync_url ); + $this->assertStringContainsString( 'wp-admin/network/admin.php?page=elasticpress-sync&do_sync&ep_sync_nonce=', $sync_url ); } else { - $this->assertStringContainsString( 'wp-admin/admin.php?page=elasticpress-sync&do_sync', $sync_url ); + $this->assertStringContainsString( 'wp-admin/admin.php?page=elasticpress-sync&do_sync&ep_sync_nonce=', $sync_url ); } }