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 6111f82d9..a8cd4a7d2 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 30a6792c7..1cd2c2783 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 8d933f24d..a6ccb460e 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 a36e80f64..1bff80865 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 42a1fc249..277d4ed76 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 07559090d..c39c0bf90 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+