diff --git a/CHANGELOG.md b/CHANGELOG.md index 11b3155941..d317d531e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,14 @@ All notable changes to this project will be documented in this file, per [the Ke ### Security --> +## [5.1.2] - 2024-06-11 + +**This is a security release affecting all previous versions of ElasticPress.** + +### Security +* Missing nonce verification for the sync triggered during activation of some features. Props [@felipeelia](https://github.com/felipeelia) and [@dhakalananda](https://github.com/dhakalananda) via [#3929](https://github.com/10up/ElasticPress/pull/3929). +* Missing nonce verification for retrying the EP connection and fixed PHPCS linting rules. Props [@felipeelia](https://github.com/felipeelia) via [#3932](https://github.com/10up/ElasticPress/pull/3932). + ## [5.1.1] - 2024-05-27 ### Changed @@ -2115,6 +2123,7 @@ This is a bug fix release with some filter additions. - Initial plugin release [Unreleased]: https://github.com/10up/ElasticPress/compare/trunk...develop +[5.1.2]: https://github.com/10up/ElasticPress/compare/5.1.1...5.1.2 [5.1.1]: https://github.com/10up/ElasticPress/compare/5.1.0...5.1.1 [5.1.0]: https://github.com/10up/ElasticPress/compare/5.0.2...5.1.0 [5.0.2]: https://github.com/10up/ElasticPress/compare/5.0.1...5.0.2 diff --git a/CREDITS.md b/CREDITS.md index 763ca4ee8d..e9ea18d65a 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -238,6 +238,7 @@ Thank you to all the people who have already contributed to this repository via [Maarten Bruna (@ictbeheer)](https://github.com/ictbeheer), [Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh), [Lucas Grzegorczyk (@furai)](https://github.com/furai), +[Ananda Dhakal (@dhakalananda)](https://github.com/dhakalananda), and [@qazaqstan2025](https://github.com/qazaqstan2025). diff --git a/SECURITY.md b/SECURITY.md index 156de9e095..83300d094e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ The following versions of this project are currently being supported with securi | Version | Supported | | ------- | ------------------ | -| 4.5.0 | :white_check_mark: | -| <4.4.1 | :x: | +| 5.1.2 | :white_check_mark: | +| <5.1.1 | :x: | ## Reporting a Vulnerability @@ -38,5 +38,5 @@ Past security advisories, if any, are listed below. | Advisory Number | Type | Versions affected | Reported by | Additional Information | |-----------------|--------------------|:-----------------:|-----------------------|-----------------------------| +| CVE-2024-35684 | CSRF | n/a - 5.1.1 | Patchstack Team | [CVE link](https://www.cve.org/CVERecord?id=CVE-2024-35684) | | EP-2021-02-11 | CSRF Nonce Bypass | 3.5.2 - 3.5.3 | WordPress.org Plugin Review Team | [WPScan link](https://wpscan.com/vulnerability/ce655810-bd08-4042-ac3d-63def5c76994) | -| - | - | - | - | - | 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/elasticpress.php b/elasticpress.php index d43d91c3aa..c4a0f8e27a 100644 --- a/elasticpress.php +++ b/elasticpress.php @@ -3,7 +3,7 @@ * Plugin Name: ElasticPress * Plugin URI: https://github.com/10up/ElasticPress * Description: A fast and flexible search and query engine for WordPress. - * Version: 5.1.1 + * Version: 5.1.2 * Requires at least: 6.0 * Requires PHP: 7.4 * Author: 10up @@ -32,7 +32,7 @@ define( 'EP_URL', plugin_dir_url( __FILE__ ) ); define( 'EP_PATH', plugin_dir_path( __FILE__ ) ); define( 'EP_FILE', plugin_basename( __FILE__ ) ); -define( 'EP_VERSION', '5.1.1' ); +define( 'EP_VERSION', '5.1.2' ); define( 'EP_PHP_VERSION_MIN', '7.4' ); diff --git a/includes/classes/AdminNotices.php b/includes/classes/AdminNotices.php index 119b8ff396..34e57121bf 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; } @@ -595,10 +589,17 @@ protected function process_host_error_notice() { $response_error = get_transient( 'ep_es_info_response_error' ); } + $retry_url = add_query_arg( + [ + 'ep-retry' => 1, + 'ep_retry_nonce' => wp_create_nonce( 'ep_retry_nonce' ), + ] + ); + $html = sprintf( /* translators: 1. Current URL with retry parameter; 2. Settings Page URL */ __( 'There is a problem with connecting to your Elasticsearch host. ElasticPress can try your host again, or you may need to change your settings.', 'elasticpress' ), - esc_url( add_query_arg( 'ep-retry', 1 ) ), + esc_url( $retry_url ), esc_url( $url ) ); diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php index 849820c4bd..8c705acdca 100644 --- a/includes/classes/Feature/WooCommerce/Orders.php +++ b/includes/classes/Feature/WooCommerce/Orders.php @@ -515,9 +515,9 @@ public function __call( $method_name, $arguments ) { if ( in_array( $method_name, $orders_autosuggest_methods, true ) ) { _deprecated_function( - "\ElasticPress\Feature\WooCommerce\WooCommerce\Orders::{$method_name}", // phpcs:ignore + "\ElasticPress\Feature\WooCommerce\WooCommerce\Orders::{$method_name}", // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '4.7.0', - "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders_autosuggest->{$method_name}()" // phpcs:ignore + "\ElasticPress\Features::factory()->get_registered_feature( 'woocommerce' )->orders_autosuggest->{$method_name}()" // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); if ( $this->woocommerce->orders_autosuggest->is_enabled() && method_exists( $this->woocommerce->orders_autosuggest, $method_name ) ) { diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php index 6799dd7df5..312f80f136 100644 --- a/includes/classes/Feature/WooCommerce/Products.php +++ b/includes/classes/Feature/WooCommerce/Products.php @@ -466,7 +466,7 @@ public function translate_args_admin_products_list( $query ) { // WooCommerce unsets the search term right after using it to fetch product IDs. Here we add it back. $search_term = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification if ( ! empty( $search_term ) ) { - $query->set( 's', sanitize_text_field( $search_term ) ); // phpcs:ignore WordPress.Security.NonceVerification + $query->set( 's', sanitize_text_field( $search_term ) ); /** * Filter the fields used in WooCommerce Admin Product Search. @@ -973,7 +973,7 @@ protected function maybe_set_orderby( \WP_Query $query ) { */ if ( ! empty( $_GET['orderby'] ) && $query->is_main_query() ) { // phpcs:ignore WordPress.Security.NonceVerification $orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification - switch ( $orderby ) { // phpcs:ignore WordPress.Security.NonceVerification + switch ( $orderby ) { case 'popularity': $query->set( 'orderby', $this->get_orderby_meta_mapping( 'total_sales' ) ); $query->set( 'order', 'DESC' ); diff --git a/includes/classes/Installer.php b/includes/classes/Installer.php index 0b17e475bc..c5a54bb4f5 100644 --- a/includes/classes/Installer.php +++ b/includes/classes/Installer.php @@ -76,7 +76,7 @@ public function calculate_install_status() { $host = Utils\get_host(); - if ( empty( $host ) && empty( $_POST['ep_host'] ) ) { // phpcs:ignore + if ( empty( $host ) && empty( $_POST['ep_host'] ) ) { $this->install_status = 2; return; 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/classes/StatusReport/FailedQueries.php b/includes/classes/StatusReport/FailedQueries.php index d087f37785..fc90fc2b1d 100644 --- a/includes/classes/StatusReport/FailedQueries.php +++ b/includes/classes/StatusReport/FailedQueries.php @@ -132,7 +132,7 @@ public function get_actions() : array { * If a nonce is present, clear the logs */ protected function maybe_clear_logs() { - if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'ep-clear-logged-queries' ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'ep-clear-logged-queries' ) ) { return; } diff --git a/includes/dashboard.php b/includes/dashboard.php index 8c83a6937c..6288d52fb8 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -187,7 +187,7 @@ function maybe_skip_install() { return; } - if ( empty( $_GET['ep-skip-install'] ) || empty( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['nonce'] ), 'ep-skip-install' ) || ! in_array( Screen::factory()->get_current_screen(), [ 'install' ], true ) ) { // phpcs:ignore WordPress.Security.NonceVerification + if ( empty( $_GET['ep-skip-install'] ) || empty( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['nonce'] ), 'ep-skip-install' ) || ! in_array( Screen::factory()->get_current_screen(), [ 'install' ], true ) ) { return; } @@ -221,7 +221,11 @@ function maybe_clear_es_info_cache() { return; } - if ( empty( $_GET['ep-retry'] ) && ! in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'settings', 'install' ], true ) ) { // phpcs:ignore WordPress.Security.NonceVerification + $isset_retry = ! empty( $_GET['ep-retry'] ) && + ! empty( $_GET['ep_retry_nonce'] ) && + wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['ep_retry_nonce'] ) ), 'ep_retry_nonce' ); + + if ( ! $isset_retry && ! in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'settings', 'install' ], true ) ) { return; } @@ -231,8 +235,8 @@ function maybe_clear_es_info_cache() { delete_transient( 'ep_es_info' ); } - if ( ! empty( $_GET['ep-retry'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification - wp_safe_redirect( remove_query_arg( 'ep-retry' ) ); + if ( $isset_retry ) { + wp_safe_redirect( remove_query_arg( [ 'ep-retry', 'ep_retry_nonce' ] ) ); exit(); } } @@ -531,9 +535,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/lang/elasticpress.pot b/lang/elasticpress.pot index fedf899347..2edd6225bb 100644 --- a/lang/elasticpress.pot +++ b/lang/elasticpress.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPL v2 or later. msgid "" msgstr "" -"Project-Id-Version: ElasticPress 5.1.1\n" +"Project-Id-Version: ElasticPress 5.1.2\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/elasticpress\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-05-24T15:51:06+00:00\n" +"POT-Creation-Date: 2024-06-11T12:43:16+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.8.1\n" "X-Domain: elasticpress\n" @@ -41,99 +41,99 @@ msgstr "" msgid "ElasticPress requires PHP version %s or later. Please upgrade PHP or disable the plugin." msgstr "" -#: includes/classes/AdminNotices.php:126 +#: includes/classes/AdminNotices.php:120 msgid "Autosuggest feature is enabled. If documents feature is enabled, your media will also become searchable in the frontend." msgstr "" #. translators: Feature name -#: includes/classes/AdminNotices.php:195 +#: includes/classes/AdminNotices.php:189 msgid "Dashboard sync is disabled. The ElasticPress %s feature has been auto-activated! You will need to reindex using WP-CLI for it to work." msgstr "" #. translators: 1. Feature name; 2: Sync page URL -#: includes/classes/AdminNotices.php:201 +#: includes/classes/AdminNotices.php:195 msgid "The ElasticPress %1$s feature has been auto-activated! You will need to run a sync for it to work." msgstr "" -#: includes/classes/AdminNotices.php:266 +#: includes/classes/AdminNotices.php:260 msgid "Dashboard sync is disabled. The new version of ElasticPress requires that you delete all data and start a fresh sync using WP-CLI." msgstr "" #. translators: Sync Page URL -#: includes/classes/AdminNotices.php:270 +#: includes/classes/AdminNotices.php:264 msgid "The new version of ElasticPress requires that you delete all data and start a fresh sync." msgstr "" -#: includes/classes/AdminNotices.php:275 +#: includes/classes/AdminNotices.php:269 msgid "Please note that some ElasticPress functionality may be impaired and/or content may not be searchable until the full sync has been performed." msgstr "" -#: includes/classes/AdminNotices.php:330 +#: includes/classes/AdminNotices.php:324 msgid "Dashboard sync is disabled, but ElasticPress is almost ready to go. Trigger a sync from WP-CLI." msgstr "" #. translators: Sync Page URL -#: includes/classes/AdminNotices.php:334 +#: includes/classes/AdminNotices.php:328 msgid "ElasticPress is almost ready to go. You just need to sync your content." msgstr "" #. translators: Sync Page URL -#: includes/classes/AdminNotices.php:386 +#: includes/classes/AdminNotices.php:380 msgid "ElasticPress is almost ready to go. You just need to enter your settings." msgstr "" #. translators: 1. Current Elasticsearch version; 2. Minimum required ES version -#: includes/classes/AdminNotices.php:443 +#: includes/classes/AdminNotices.php:437 msgid "Your Elasticsearch version %1$s is below the minimum required Elasticsearch version %2$s. ElasticPress may or may not work properly." msgstr "" #. translators: 1. Current Elasticsearch version; 2. Maximum supported ES version -#: includes/classes/AdminNotices.php:495 +#: includes/classes/AdminNotices.php:489 msgid "Your Elasticsearch version %1$s is above the maximum required Elasticsearch version %2$s. ElasticPress may or may not work properly." msgstr "" #. translators: Document page URL -#: includes/classes/AdminNotices.php:539 +#: includes/classes/AdminNotices.php:533 msgid "Your server software is not supported. To learn more about server compatibility please visit our documentation." msgstr "" #. translators: 1. Current URL with retry parameter; 2. Settings Page URL -#: includes/classes/AdminNotices.php:600 +#: includes/classes/AdminNotices.php:601 msgid "There is a problem with connecting to your Elasticsearch host. ElasticPress can try your host again, or you may need to change your settings." msgstr "" #. translators: Response Code Number -#: includes/classes/AdminNotices.php:608 +#: includes/classes/AdminNotices.php:609 msgid "Response Code: %s" msgstr "" #. translators: Response Code Message -#: includes/classes/AdminNotices.php:613 +#: includes/classes/AdminNotices.php:614 msgid "Response error: %s" msgstr "" #. translators: 1. ; 2. -#: includes/classes/AdminNotices.php:675 +#: includes/classes/AdminNotices.php:676 msgid "It seems the mapping data in your index does not match the Elasticsearch version used. We recommend to reindex your content using the sync button on the top of the screen or through wp-cli by adding the %1$s--setup%2$s flag" msgstr "" #. translators: 1. Current mapping file; 2. Mapping file that should be used -#: includes/classes/AdminNotices.php:682 +#: includes/classes/AdminNotices.php:683 msgid "Current mapping: %1$s. Expected mapping: %2$s" msgstr "" #. translators: Index Health URL -#: includes/classes/AdminNotices.php:740 +#: includes/classes/AdminNotices.php:741 msgid "It looks like one or more of your indices are running on a single node. While this won't prevent you from using ElasticPress, depending on your site's specific needs this can represent a performance issue. Please check the Index Health page where you can check the health of all of your indices." msgstr "" #. translators: Elasticsearch or ElasticPress.io; 2. Link to article; 3. Link to article -#: includes/classes/AdminNotices.php:796 +#: includes/classes/AdminNotices.php:797 msgid "Your website content has more public custom fields than %1$s is able to store. Check our articles about Elasticsearch field limitations and how to index just the custom fields you need before trying to sync." msgstr "" -#: includes/classes/AdminNotices.php:797 -#: includes/classes/AdminNotices.php:813 +#: includes/classes/AdminNotices.php:798 +#: includes/classes/AdminNotices.php:814 #: includes/classes/ElasticsearchErrorInterpreter.php:93 #: includes/classes/StatusReport/ElasticPressIo.php:30 #: assets/js/sync/index.js:269 @@ -141,8 +141,8 @@ msgstr "" msgid "ElasticPress.io" msgstr "" -#: includes/classes/AdminNotices.php:797 -#: includes/classes/AdminNotices.php:813 +#: includes/classes/AdminNotices.php:798 +#: includes/classes/AdminNotices.php:814 #: includes/classes/ElasticsearchErrorInterpreter.php:93 #: assets/js/sync/index.js:270 #: dist/js/sync-script.js:1 @@ -150,7 +150,7 @@ msgid "Elasticsearch" msgstr "" #. translators: Elasticsearch or ElasticPress.io; 2. Link to article; 3. Link to article -#: includes/classes/AdminNotices.php:812 +#: includes/classes/AdminNotices.php:813 msgid "Your website content seems to have more public custom fields than %1$s is able to store. Check our articles about Elasticsearch field limitations and how to index just the custom fields you need if you receive any errors while syncing." msgstr "" @@ -489,7 +489,7 @@ msgstr "" #: includes/classes/Feature.php:392 #: includes/classes/StatusReport/ElasticPress.php:79 -#: includes/dashboard.php:648 +#: includes/dashboard.php:650 #: includes/partials/settings-page.php:34 #: assets/js/blocks/facets/common/edit.js:87 #: assets/js/blocks/facets/date/edit.js:23 @@ -2213,53 +2213,53 @@ msgstr "" msgid "Feature registration API" msgstr "" -#: includes/dashboard.php:284 +#: includes/dashboard.php:288 msgid "Dashboard" msgstr "" -#: includes/dashboard.php:638 +#: includes/dashboard.php:640 msgid "ElasticPress Features" msgstr "" -#: includes/dashboard.php:639 +#: includes/dashboard.php:641 #: assets/js/features/index.js:34 #: dist/js/features-script.js:1 msgid "Features" msgstr "" -#: includes/dashboard.php:647 +#: includes/dashboard.php:649 msgid "ElasticPress Settings" msgstr "" -#: includes/dashboard.php:656 -#: includes/dashboard.php:657 -#: assets/js/features/apps/features.js:82 +#: includes/dashboard.php:658 +#: includes/dashboard.php:659 +#: assets/js/features/apps/features.js:83 #: assets/js/synonyms/apps/synonyms-settings.js:61 #: dist/js/features-script.js:1 #: dist/js/synonyms-script.js:1 msgid "Sync" msgstr "" -#: includes/dashboard.php:665 +#: includes/dashboard.php:667 msgid "ElasticPress Index Health" msgstr "" -#: includes/dashboard.php:666 +#: includes/dashboard.php:668 #: includes/partials/stats-page.php:35 msgid "Index Health" msgstr "" -#: includes/dashboard.php:674 +#: includes/dashboard.php:676 msgid "ElasticPress Status Report" msgstr "" -#: includes/dashboard.php:675 +#: includes/dashboard.php:677 #: assets/js/status-report/index.js:21 #: dist/js/status-report-script.js:1 msgid "Status Report" msgstr "" -#: includes/dashboard.php:873 +#: includes/dashboard.php:875 msgid "ElasticPress Indexing" msgstr "" @@ -2603,57 +2603,57 @@ msgstr "" msgid "It looks like you’re trying to use ElasticPress’s advanced features only. If you’d like to activate basic search, please select Cancel and activate the Post Search Feature. Otherwise, please click Ok to configure advanced features." msgstr "" -#: assets/js/features/apps/features.js:54 +#: assets/js/features/apps/features.js:55 #: dist/js/features-script.js:1 msgid "Could not save feature settings. Please try again." msgstr "" -#: assets/js/features/apps/features.js:62 +#: assets/js/features/apps/features.js:63 #: dist/js/features-script.js:1 msgid "View sync status" msgstr "" -#: assets/js/features/apps/features.js:69 +#: assets/js/features/apps/features.js:70 #: dist/js/features-script.js:1 msgid "Cannot save settings while a sync is in progress." msgstr "" -#: assets/js/features/apps/features.js:74 +#: assets/js/features/apps/features.js:75 #: dist/js/features-script.js:1 msgid "Changes to feature settings discarded." msgstr "" -#: assets/js/features/apps/features.js:89 +#: assets/js/features/apps/features.js:90 #: dist/js/features-script.js:1 msgid "If you choose to sync later some settings changes may not take effect until the sync is performed. Save and sync later?" msgstr "" -#: assets/js/features/apps/features.js:97 +#: assets/js/features/apps/features.js:98 #: dist/js/features-script.js:1 msgid "Saving these settings will begin re-syncing your content. Save and sync now?" msgstr "" -#: assets/js/features/apps/features.js:105 +#: assets/js/features/apps/features.js:106 #: dist/js/features-script.js:1 msgid "Feature settings saved. Starting sync…" msgstr "" -#: assets/js/features/apps/features.js:110 +#: assets/js/features/apps/features.js:111 #: dist/js/features-script.js:1 msgid "Feature settings saved." msgstr "" -#: assets/js/features/apps/features.js:142 +#: assets/js/features/apps/features.js:143 #: dist/js/features-script.js:1 msgid "ElasticPress: Could not save feature settings." msgstr "" -#: assets/js/features/apps/features.js:244 +#: assets/js/features/apps/features.js:245 #: dist/js/features-script.js:1 msgid "Save and sync now" msgstr "" -#: assets/js/features/apps/features.js:245 +#: assets/js/features/apps/features.js:246 #: assets/js/synonyms/apps/synonyms-settings.js:174 #: assets/js/synonyms/components/common/edit-panel.js:95 #: assets/js/weighting/apps/weighting.js:59 @@ -2663,12 +2663,12 @@ msgstr "" msgid "Save changes" msgstr "" -#: assets/js/features/apps/features.js:257 +#: assets/js/features/apps/features.js:258 #: dist/js/features-script.js:1 msgid "Save and sync later" msgstr "" -#: assets/js/features/apps/features.js:264 +#: assets/js/features/apps/features.js:265 #: dist/js/features-script.js:1 msgid "Discard changes" msgstr "" diff --git a/package-lock.json b/package-lock.json index fca0a7710b..ca17aaa2b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "elasticpress", - "version": "5.1.1", + "version": "5.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "elasticpress", - "version": "5.1.1", + "version": "5.1.2", "license": "GPL-2.0-or-later", "dependencies": { "@10up/component-tooltip": "^2.0.0", diff --git a/package.json b/package.json index 7e5f46d8f3..aa23fe3b94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "elasticpress", - "version": "5.1.1", + "version": "5.1.2", "license": "GPL-2.0-or-later", "description": "A fast and flexible search and query engine for WordPress.", "devDependencies": { diff --git a/readme.txt b/readme.txt index 8e7ea18f59..448c1538ec 100644 --- a/readme.txt +++ b/readme.txt @@ -2,7 +2,7 @@ Contributors: 10up, tlovett1, vhauri, tott, felipeelia, oscarssanchez, cmmarslender Tags: performance, search, elasticsearch, fuzzy, related posts Tested up to: 6.5 -Stable tag: 5.1.1 +Stable tag: 5.1.2 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -79,6 +79,15 @@ For sure! Feel free to submit ideas or feedback in general to our [GitHub repo]( == Changelog == += 5.1.2 - 2024-06-11 = + +**This is a security release affecting all previous versions of ElasticPress.** + +__Security:__ + +* Missing nonce verification for the sync triggered during activation of some features. Props [@felipeelia](https://github.com/felipeelia) and [@dhakalananda](https://github.com/dhakalananda). +* Missing nonce verification for retrying the EP connection and fixed PHPCS linting rules. Props [@felipeelia](https://github.com/felipeelia). + = 5.1.1 - 2024-05-27 = __Changed:__ 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 ); } }