From e61210f3529a19ef235805d34e82f100282aec7a Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 21 Aug 2024 12:17:16 -0400 Subject: [PATCH] release v4.0.2 --- classes/class-network.php | 67 +++++++++++++++++--------------------- classes/class-plugin.php | 2 +- classes/class-settings.php | 61 +++++++++++++++++++++------------- docker-compose.yml | 2 ++ readme.txt | 8 ++++- stream.php | 2 +- 6 files changed, 78 insertions(+), 64 deletions(-) diff --git a/classes/class-network.php b/classes/class-network.php index 6111f82d..a8cd4a7d 100644 --- a/classes/class-network.php +++ b/classes/class-network.php @@ -27,11 +27,11 @@ class Network { public $network_settings_page_slug = 'wp_stream_network_settings'; /** - * Default setting page slug + * The option name for the network settings. * * @var string */ - public $default_settings_page_slug = 'wp_stream_default_settings'; + public $network_settings_option = 'wp_stream_network'; /** * Class constructor @@ -225,13 +225,8 @@ public function settings_form_description( $description ) { $current_page = wp_stream_filter_input( INPUT_GET, 'page' ); - switch ( $current_page ) { - case $this->network_settings_page_slug: - $description = __( 'These settings apply to all sites on the network.', 'stream' ); - break; - case $this->default_settings_page_slug: - $description = __( 'These default settings will apply to new sites created on the network. These settings do not alter existing sites.', 'stream' ); - break; + if ( $this->network_settings_page_slug === $current_page ) { + $description = __( 'These settings apply to all sites on the network.', 'stream' ); } return $description; @@ -351,46 +346,42 @@ public function get_settings_translations( $labels ) { * Wrapper for the settings API to work on the network settings page */ public function network_options_action() { - $allowed_referrers = array( - $this->network_settings_page_slug, - $this->default_settings_page_slug, - ); - // @codingStandardsIgnoreLine - if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referrers, true ) ) { + // Check the nonce. + if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], sprintf( '%s-options', $this->network_settings_option ) ) ) { + return; + } + + // Check the user capability. + if ( ! current_user_can( $this->plugin->admin->settings_cap ) ) { + return; + } + + // Check the action. + if ( ! isset( $_GET['action'] ) || $this->network_settings_page_slug !== $_GET['action'] ) { return; } - // @codingStandardsIgnoreLine - $options = isset( $_POST['option_page'] ) ? explode( ',', stripslashes( $_POST['option_page'] ) ) : null; + $option = ! empty( $_POST['option_page'] ) ? $_POST['option_page'] : false; - if ( $options ) { + if ( $option && $this->network_settings_option === $option ) { - foreach ( $options as $option ) { - $option = trim( $option ); - $value = null; - $sections = $this->plugin->settings->get_fields(); + $value = array(); + $sections = $this->plugin->settings->get_fields(); - foreach ( $sections as $section_name => $section ) { - foreach ( $section['fields'] as $field_idx => $field ) { - $option_key = $section_name . '_' . $field['name']; + foreach ( $sections as $section_name => $section ) { + foreach ( $section['fields'] as $field_idx => $field ) { + $option_key = $section_name . '_' . $field['name']; - // @codingStandardsIgnoreStart - if ( isset( $_POST[ $option ][ $option_key ] ) ) { - $value[ $option_key ] = $_POST[ $option ][ $option_key ]; - } else { - $value[ $option_key ] = false; - } - // @codingStandardsIgnoreEnd + if ( isset( $_POST[ $option ][ $option_key ] ) ) { + $value[ $option_key ] = $this->plugin->settings->sanitize_setting_by_field_type( $_POST[ $option ][ $option_key ], $field['type'] ); + } else { + $value[ $option_key ] = false; } } - - if ( ! is_array( $value ) ) { - $value = trim( $value ); - } - - update_site_option( $option, $value ); } + + update_site_option( $this->network_settings_option, $value ); } if ( ! count( get_settings_errors() ) ) { diff --git a/classes/class-plugin.php b/classes/class-plugin.php index 30a6792c..1cd2c278 100755 --- a/classes/class-plugin.php +++ b/classes/class-plugin.php @@ -18,7 +18,7 @@ class Plugin { * * @const string */ - const VERSION = '4.0.1'; + const VERSION = '4.0.2'; /** * WP-CLI command diff --git a/classes/class-settings.php b/classes/class-settings.php index 8d933f24..a6ccb460 100644 --- a/classes/class-settings.php +++ b/classes/class-settings.php @@ -544,35 +544,50 @@ public function sanitize_settings( $input ) { continue; } - // Sanitize depending on the type of field. - switch ( $type ) { - case 'number': - $output[ $name ] = is_numeric( $input[ $name ] ) ? intval( trim( $input[ $name ] ) ) : ''; - break; - case 'checkbox': - $output[ $name ] = is_numeric( $input[ $name ] ) ? absint( trim( $input[ $name ] ) ) : ''; - break; - default: - if ( is_array( $input[ $name ] ) ) { - $output[ $name ] = $input[ $name ]; - - // Support all values in multidimentional arrays too. - array_walk_recursive( - $output[ $name ], - function ( &$v ) { - $v = sanitize_text_field( trim( $v ) ); - } - ); - } else { - $output[ $name ] = sanitize_text_field( trim( $input[ $name ] ) ); - } - } + $output[ $name ] = $this->sanitize_setting_by_field_type( $input[ $name ], $type ); } } return $output; } + /** + * Sanitizes a setting value based on the field type. + * + * @param mixed $value The value to be sanitized. + * @param string $field_type The type of field. + * + * @return mixed The sanitized value. + */ + public function sanitize_setting_by_field_type( $value, $field_type ) { + + // Sanitize depending on the type of field. + switch ( $field_type ) { + case 'number': + $sanitized_value = is_numeric( $value ) ? intval( trim( $value ) ) : ''; + break; + case 'checkbox': + $sanitized_value = is_numeric( $value ) ? absint( trim( $value ) ) : ''; + break; + default: + if ( is_array( $value ) ) { + $sanitized_value = $value; + + // Support all values in multidimentional arrays too. + array_walk_recursive( + $sanitized_value, + function ( &$v ) { + $v = sanitize_text_field( trim( $v ) ); + } + ); + } else { + $sanitized_value = sanitize_text_field( trim( $value ) ); + } + } + + return $sanitized_value; + } + /** * Compile HTML needed for displaying the field * diff --git a/docker-compose.yml b/docker-compose.yml index a36e80f6..1bff8086 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,8 @@ services: volumes: - db_data:/var/lib/mysql restart: always + ports: + - "3306:3306" environment: MYSQL_DATABASE: wordpress MYSQL_USER: wordpress diff --git a/readme.txt b/readme.txt index 42a1fc24..277d4ed7 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: xwp Tags: wp stream, stream, activity, logs, track Requires at least: 4.6 Tested up to: 6.6 -Stable tag: 4.0.1 +Stable tag: 4.0.2 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -134,6 +134,12 @@ Use only `$_SERVER['REMOTE_ADDR']` as the client IP address for event logs witho == Changelog == += 4.0.2 - August 22, 2024 = + +**Security update** + +- Fix vulnerability which allowed logged in users to update some site options in certain configurations. Props to [@sybrew](https://github.com/sybrew) for responsibly disclosing this issue. + = 4.0.1 - July 30, 2024 = **Bug fixes** diff --git a/stream.php b/stream.php index 07559090..c39c0bf9 100644 --- a/stream.php +++ b/stream.php @@ -3,7 +3,7 @@ * Plugin Name: Stream * Plugin URI: https://xwp.co/work/stream/ * Description: Stream tracks logged-in user activity so you can monitor every change made on your WordPress site in beautifully organized detail. All activity is organized by context, action and IP address for easy filtering. Developers can extend Stream with custom connectors to log any kind of action. - * Version: 4.0.1 + * Version: 4.0.2 * Author: XWP * Author URI: https://xwp.co * License: GPLv2+