Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/metronet-profile-picture/js/index.php
+++ b/metronet-profile-picture/js/index.php
@@ -0,0 +1,2 @@
+<?php // phpcs:ignore
+// no direct access.
--- a/metronet-profile-picture/metronet-profile-picture.php
+++ b/metronet-profile-picture/metronet-profile-picture.php
@@ -4,15 +4,21 @@
Plugin URI: http://wordpress.org/plugins/metronet-profile-picture/
Description: Use the native WP uploader on your user profile page.
Author: Cozmoslabs
-Version: 2.6.3
-Requires at least: 4.6
+Version: 2.6.4
+Requires at least: 5.0
Author URI: https://www.cozmoslabs.com
Contributors: ronalfy
Text Domain: metronet-profile-picture
Domain Path: /languages
+License: GPLv2 or later
+License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/
-define( 'METRONET_PROFILE_PICTURE_VERSION', '2.6.3' );
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
+define( 'METRONET_PROFILE_PICTURE_VERSION', '2.6.4' );
define( 'METRONET_PROFILE_PICTURE_PLUGIN_NAME', 'User Profile Picture' );
define( 'METRONET_PROFILE_PICTURE_DIR', plugin_dir_path( __FILE__ ) );
define( 'METRONET_PROFILE_PICTURE_URL', plugins_url( '/', __FILE__ ) );
@@ -208,7 +214,7 @@
<td>
<input type="hidden" name="options['disable_image_sizes']" value="off" />
<input id="mpp-display-image-sizes" type="checkbox" value="on" name="options[disable_image_sizes]" <?php checked( 'on', $options['disable_image_sizes'] ); ?> /> <label for="mpp-display-image-sizes"><?php esc_html_e( 'Disable Image Sizes', 'metronet-profile-picture' ); ?></label>
- <p class="description"><?php esc_html_e( 'Select this option to disable the four image sizes User Profile Picture Creates.' ); ?></p>
+ <p class="description"><?php esc_html_e( 'Select this option to disable the four image sizes User Profile Picture Creates.', 'metronet-profile-picture' ); ?></p>
</td>
</tr>
<?php
@@ -326,6 +332,22 @@
}
check_ajax_referer( "mt-update-post_$user_id" );
+ // Ensure the current user is allowed to edit this user's profile (prevents IDOR).
+ if ( ! current_user_can( 'edit_user', $user_id ) ) {
+ die( '' );
+ }
+
+ // Ensure the profile-picture post actually belongs to this user.
+ $profile_post = get_post( $post_id );
+ if ( ! $profile_post || (int) $profile_post->post_author !== $user_id ) {
+ die( '' );
+ }
+
+ // Ensure the selected media is a real attachment.
+ if ( 'attachment' !== get_post_type( $thumbnail_id ) ) {
+ die( '' );
+ }
+
// Save user meta.
update_user_option( $user_id, 'metronet_post_id', $post_id );
update_user_option( $user_id, 'metronet_image_id', $thumbnail_id ); // Added via this thread (Props Solinx) - https://wordpress.org/support/topic/storing-image-id-directly-as-user-meta-data.
@@ -376,11 +398,18 @@
$user_id = isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0;
$post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
check_ajax_referer( "mt-update-post_$user_id" );
- $post = get_post( $post_id );
- $user_id = 0;
- if ( $post ) {
- $user_id = $post->post_author;
+
+ // Ensure the current user is allowed to view this user's profile (prevents IDOR).
+ if ( ! current_user_can( 'edit_user', $user_id ) ) {
+ die( '' );
+ }
+
+ // Ensure the post is this user's profile-picture post.
+ $post = get_post( $post_id );
+ if ( ! $post || 'mt_pp' !== $post->post_type || (int) $post->post_author !== $user_id ) {
+ die( '' );
}
+ $user_id = (int) $post->post_author;
if ( has_post_thumbnail( $post_id ) ) {
$thumb_src = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'thumbnail', false, '' );
@@ -436,6 +465,17 @@
}
check_ajax_referer( "mt-update-post_$user_id" );
+ // Ensure the current user is allowed to edit this user's profile (prevents IDOR).
+ if ( ! current_user_can( 'edit_user', $user_id ) ) {
+ die( '' );
+ }
+
+ // Ensure the post is this user's profile-picture post before removing its thumbnail.
+ $profile_post = get_post( $post_id );
+ if ( ! $profile_post || 'mt_pp' !== $profile_post->post_type || (int) $profile_post->post_author !== $user_id ) {
+ die( '' );
+ }
+
$thumb_html = '<a style="display:block" href="#" class="mpp_add_media default-image">';
$thumb_html .= sprintf( '<img style="display:block" src="%s" width="150" height="150" title="%s" />', self::get_plugin_url( 'img/mystery.png' ), esc_attr__( 'Upload or Change Profile Picture', 'metronet-profile-picture' ) );
$thumb_html .= sprintf( '<div id="metronet-click-edit">%s</div>', esc_html__( 'Click to Edit', 'metronet-profile-picture' ) );
@@ -983,7 +1023,9 @@
array(
'methods' => 'POST',
'callback' => array( $this, 'rest_api_put_profile' ),
- 'permission_callback' => '__return_true',
+ 'permission_callback' => function() {
+ return current_user_can( 'upload_files' );
+ },
)
);
register_rest_route(
@@ -1117,14 +1159,19 @@
$user_id = (int) $request['user_id'];
$media_id = (int) $request['media_id'];
- if ( ! $user_id ) {
+ if ( ! $user_id || ! get_user_by( 'id', $user_id ) ) {
return new WP_Error( 'mpp_no_user', __( 'User not found.', 'metronet-profile-picture' ), array( 'status' => 403 ) );
}
- if ( ! current_user_can( 'upload_files', $user_id ) ) {
+ // Ensure the current user is allowed to edit the targeted user (prevents IDOR).
+ if ( ! current_user_can( 'edit_user', $user_id ) || ! current_user_can( 'upload_files' ) ) {
return new WP_Error( 'mpp_insufficient_privs', __( 'You must be able to upload files.', 'metronet-profile-picture' ), array( 'status' => 403 ) );
}
+ if ( 'attachment' !== get_post_type( $media_id ) ) {
+ return new WP_Error( 'mpp_invalid_media', __( 'Invalid media.', 'metronet-profile-picture' ), array( 'status' => 400 ) );
+ }
+
$post_id = $this->get_post_id( $user_id );
// Save user meta.
@@ -1167,7 +1214,10 @@
if ( ! current_user_can( 'edit_others_posts', $user_id ) ) {
return new WP_Error( 'mpp_not_privs', __( 'You must have a role of editor or above to set a new profile image.', 'metronet-profile-picture' ), array( 'status' => 403 ) );
}
- $is_post_owner = ( get_post( $media_id )->post_author === $user_id ) ? true : false;
+ if ( 'attachment' !== get_post_type( $media_id ) ) {
+ return new WP_Error( 'mpp_invalid_media', __( 'Invalid media.', 'metronet-profile-picture' ), array( 'status' => 400 ) );
+ }
+ $is_post_owner = ( (int) get_post( $media_id )->post_author === $user_id ) ? true : false;
if ( ! $is_post_owner && ! current_user_can( 'edit_others_posts', $user_id ) ) {
return new WP_Error( 'mpp_not_owner', __( 'User not owner.', 'metronet-profile-picture' ), array( 'status' => 403 ) );
}
--- a/metronet-profile-picture/profile-builder-transition.php
+++ b/metronet-profile-picture/profile-builder-transition.php
@@ -1,5 +1,8 @@
<?php
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
if( !class_exists('PB_Handle_Transition') ){
class PB_Handle_Transition{
@@ -66,7 +69,7 @@
?>
<div class="upp-transition-notice upp-notice notice notice-success is-dismissible">
<p>
- <?php echo apply_filters( 'upp_plugin_activation_success_message', esc_html__('Plugin activated.', 'profile-builder') ); ?>
+ <?php echo esc_html( apply_filters( 'upp_plugin_activation_success_message', esc_html__('Plugin activated.', 'metronet-profile-picture') ) ); ?>
</p>
</div>
<?php
@@ -74,7 +77,7 @@
?>
<div class="upp-transition-notice upp-notice notice notice-error is-dismissible">
<p>
- <?php echo wp_kses( sprintf( apply_filters( 'upp_plugin_activation_fail_message', __('Could not install. Try again from the <a href="%s" >Plugins Dashboard.</a>', 'profile-builder') ), apply_filters( 'upp_plugin_activation_fail_link', admin_url('plugins.php') ) ), array('a' => array( 'href' => array() ) ) ); ?>
+ <?php /* translators: %s: URL of the Plugins dashboard. */ echo wp_kses( sprintf( apply_filters( 'upp_plugin_activation_fail_message', __('Could not install. Try again from the <a href="%s" >Plugins Dashboard.</a>', 'metronet-profile-picture') ), esc_url( apply_filters( 'upp_plugin_activation_fail_link', admin_url('plugins.php') ) ) ), array('a' => array( 'href' => array() ) ) ); ?>
</p>
</div>
<?php
@@ -89,16 +92,16 @@
</p>
<p style="margin-top: 16px; font-size: 15px;">
<?php
- printf( apply_filters( 'upp_transition_notice_part_1', esc_html__( 'The User Profile Picture functionality has been migrated into Profile Builder as an add-on. Please install and activate the Profile Builder plugin to use this new add-on.', 'profile-builder' ) ) );
+ echo esc_html( apply_filters( 'upp_transition_notice_part_1', esc_html__( 'The User Profile Picture functionality has been migrated into Profile Builder as an add-on. Please install and activate the Profile Builder plugin to use this new add-on.', 'metronet-profile-picture' ) ) );
?>
</p>
<p style="margin-top: 16px; font-size: 15px;">
<?php
- printf( apply_filters( 'upp_transition_notice_part_2', esc_html__( 'This plugin will continue to function as it is now, but it will not receive further updates. You can read more about this transition in', 'profile-builder' ) ) );
+ echo esc_html( apply_filters( 'upp_transition_notice_part_2', esc_html__( 'This plugin will continue to function as it is now, but it will not receive further updates. You can read more about this transition in', 'metronet-profile-picture' ) ) );
echo ' ';
- echo '<a href="' . apply_filters( 'upp_transition_notice_link_target', "https://www.cozmoslabs.com/docs/profile-builder/add-ons/user-profile-picture/" ) . '" target="_blank" rel="noopener noreferrer">' . apply_filters( 'upp_transition_notice_link_text', esc_html__( 'this', 'profile-builder' ) ) . '</a>';
+ echo '<a href="' . esc_url( apply_filters( 'upp_transition_notice_link_target', "https://www.cozmoslabs.com/docs/profile-builder/add-ons/user-profile-picture/" ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( apply_filters( 'upp_transition_notice_link_text', esc_html__( 'this', 'metronet-profile-picture' ) ) ) . '</a>';
echo ' ';
- wp_kses( printf( apply_filters( 'upp_transition_notice_part_3', esc_html__( "section of Profile Builder's Documentation.", 'profile-builder' ) ) ), array('a' => array( 'href' => array() ) ) );
+ echo esc_html( apply_filters( 'upp_transition_notice_part_3', esc_html__( "section of Profile Builder's Documentation.", 'metronet-profile-picture' ) ) );
?>
</p>
</div>
@@ -108,22 +111,22 @@
<div>
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'pb_install_pb_plugin', 'nonce' => wp_create_nonce( 'pb_install_pb_plugin' ) ), get_dashboard_url( $current_user -> ID, "plugins.php" ) ) ); ?>"
class="button-primary" style="margin-right: 20px">
- <?php echo apply_filters( 'upp_transition_notice_button_text', esc_html__( 'Install & Activate', 'profile-builder' ) ); ?>
+ <?php echo esc_html( apply_filters( 'upp_transition_notice_button_text', esc_html__( 'Install & Activate', 'metronet-profile-picture' ) ) ); ?>
</a>
</div>
<div>
- <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId)) ) ?>"
+ <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId, '_wpnonce' => wp_create_nonce( $this->notificationId ))) ) ?>"
style="height: 30px;" class="button-secondary">
- <?php esc_html_e('Not now', 'profile-builder'); ?>
+ <?php esc_html_e('Not now', 'metronet-profile-picture'); ?>
</a>
</div>
</div>
- <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId)) ) ?>"
+ <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId, '_wpnonce' => wp_create_nonce( $this->notificationId ))) ) ?>"
type="button" class="notice-dismiss" style="text-decoration: none;">
<span class="screen-reader-text">
- <?php esc_html_e('Dismiss this notice.', 'profile-builder'); ?>
+ <?php esc_html_e('Dismiss this notice.', 'metronet-profile-picture'); ?>
</span>
</a>
</div>
@@ -133,7 +136,7 @@
?>
<div class="upp-transition-notice upp-notice notice notice-info is-dismissible">
<p>
- <?php echo apply_filters( 'upp_transition_notice_update_pb', esc_html__('The User Profile Picture functionality has been migrated into Profile Builder as an add-on. Please update the Profile Builder plugin to at least version 3.12.0 to make use of this new add-on.', 'profile-builder') ); ?>
+ <?php echo esc_html( apply_filters( 'upp_transition_notice_update_pb', esc_html__('The User Profile Picture functionality has been migrated into Profile Builder as an add-on. Please update the Profile Builder plugin to at least version 3.12.0 to make use of this new add-on.', 'metronet-profile-picture') ) ); ?>
</p>
</div>
<?php
@@ -147,16 +150,16 @@
</p>
<p style="margin-top: 16px; font-size: 15px;">
<?php
- printf( apply_filters( 'upp_transition_notice_enable_add_on_part_1', esc_html__( 'The User Profile Picture functionality has been migrated into Profile Builder as an add-on. Do you wish to enable this new add-on and deactivate the User Profile Picture plugin?', 'profile-builder' ) ) );
+ echo esc_html( apply_filters( 'upp_transition_notice_enable_add_on_part_1', esc_html__( 'The User Profile Picture functionality has been migrated into Profile Builder as an add-on. Do you wish to enable this new add-on and deactivate the User Profile Picture plugin?', 'metronet-profile-picture' ) ) );
?>
</p>
<p style="margin-top: 16px; font-size: 15px;">
<?php
- printf( apply_filters( 'upp_transition_notice_enable_add_on_part_2', esc_html__( 'This plugin will continue to function as it is now, but it will not receive further updates. You can read more about this transition in', 'profile-builder' ) ) );
+ echo esc_html( apply_filters( 'upp_transition_notice_enable_add_on_part_2', esc_html__( 'This plugin will continue to function as it is now, but it will not receive further updates. You can read more about this transition in', 'metronet-profile-picture' ) ) );
echo ' ';
- echo '<a href="' . apply_filters( 'upp_transition_notice_enable_add_on_link_target', "https://www.cozmoslabs.com/docs/profile-builder/add-ons/user-profile-picture/" ) . '" target="_blank" rel="noopener noreferrer">' . apply_filters( 'upp_transition_notice_enable_add_on_link_text', esc_html__( 'this', 'profile-builder' ) ) . '</a>';
+ echo '<a href="' . esc_url( apply_filters( 'upp_transition_notice_enable_add_on_link_target', "https://www.cozmoslabs.com/docs/profile-builder/add-ons/user-profile-picture/" ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( apply_filters( 'upp_transition_notice_enable_add_on_link_text', esc_html__( 'this', 'metronet-profile-picture' ) ) ) . '</a>';
echo ' ';
- wp_kses( printf( apply_filters( 'upp_transition_notice_enable_add_on_part_3', esc_html__( "section of Profile Builder's Documentation.", 'profile-builder' ) ) ), array('a' => array( 'href' => array() ) ) );
+ echo esc_html( apply_filters( 'upp_transition_notice_enable_add_on_part_3', esc_html__( "section of Profile Builder's Documentation.", 'metronet-profile-picture' ) ) );
?>
</p>
</div>
@@ -166,22 +169,22 @@
<div>
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'pb_install_pb_plugin', 'nonce' => wp_create_nonce( 'pb_install_pb_plugin' ) ), get_dashboard_url( $current_user -> ID, "plugins.php" ) ) ); ?>"
class="button-primary" style="margin-right: 20px">
- <?php echo apply_filters( 'upp_transition_notice_enable_add_on_button_text', esc_html__( 'Activate the add-on', 'profile-builder' ) ); ?>
+ <?php echo esc_html( apply_filters( 'upp_transition_notice_enable_add_on_button_text', esc_html__( 'Activate the add-on', 'metronet-profile-picture' ) ) ); ?>
</a>
</div>
<div>
- <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId)) ) ?>"
+ <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId, '_wpnonce' => wp_create_nonce( $this->notificationId ))) ) ?>"
style="height: 30px;" class="button-secondary">
- <?php esc_html_e('Not now', 'profile-builder'); ?>
+ <?php esc_html_e('Not now', 'metronet-profile-picture'); ?>
</a>
</div>
</div>
- <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId)) ) ?>"
+ <a href="<?php echo esc_url( add_query_arg(array($this->query_arg => $this->notificationId, '_wpnonce' => wp_create_nonce( $this->notificationId ))) ) ?>"
type="button" class="notice-dismiss" style="text-decoration: none;">
<span class="screen-reader-text">
- <?php esc_html_e('Dismiss this notice.', 'profile-builder'); ?>
+ <?php esc_html_e('Dismiss this notice.', 'metronet-profile-picture'); ?>
</span>
</a>
</div>
@@ -195,12 +198,17 @@
// Function that saves the notification dismissal to the user meta
public function dismiss_notification() {
- global $current_user;
+ if ( ! current_user_can( 'manage_options' ) ) {
+ return;
+ }
- $user_id = $current_user->ID;
+ global $current_user;
// If user clicks to ignore the notice, add that to their user meta
- if ( isset( $_GET[$this->query_arg] ) && $this->notificationId === $_GET[$this->query_arg] ) {
+ if ( isset( $_GET[ $this->query_arg ], $_GET['_wpnonce'] )
+ && $this->notificationId === $_GET[ $this->query_arg ]
+ && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), $this->notificationId )
+ ) {
do_action( $this->notificationId.'_before_notification_dismissed', $current_user );
update_option( 'upp_transition_notice_counter', 0 );
do_action( $this->notificationId.'_after_notification_dismissed', $current_user );
@@ -217,7 +225,7 @@
isset( $_REQUEST['action'] ) && !empty($_REQUEST['nonce']) && $_REQUEST['action'] === 'pb_install_pb_plugin' &&
!isset( $_REQUEST['upp_install_pb_plugin_success']) &&
current_user_can( 'manage_options' ) &&
- wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'pb_install_pb_plugin' )
+ wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ), 'pb_install_pb_plugin' )
) {
$plugin_slug = 'profile-builder/index.php';