Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/password-protect-page/admin/class-ppw-admin.php
+++ b/password-protect-page/admin/class-ppw-admin.php
@@ -134,7 +134,8 @@
*/
public function ppw_free_set_password() {
$setting_keys = array( 'save_password', 'id_page_post', 'is_role_selected', 'ppwp_multiple_password' );
- if ( ppw_free_error_before_create_password( $_REQUEST, $setting_keys ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We handle nonce verification in this function
+ // phpcs:disable
+ if ( ppw_free_error_before_create_password( $_REQUEST, $setting_keys ) ) {
wp_send_json(
array(
'is_error' => true,
@@ -144,11 +145,8 @@
);
wp_die();
}
- // phpcs:disable WordPress.Security.NonceVerification.Recommended
- // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
- if ( ! isset( $_REQUEST['settings'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We handle nonce verification above.
+ if ( ! isset( $_REQUEST['settings'] ) ) {
wp_send_json(
array(
'is_error' => true,
@@ -163,10 +161,21 @@
$data_settings = wp_unslash( $_REQUEST['settings'] );
$new_role_password = $data_settings['save_password'];
- $id = $data_settings['id_page_post'];
+ $id = absint( $data_settings['id_page_post'] );
$role_selected = $data_settings['is_role_selected'];
$new_global_passwords = is_array( $data_settings['ppwp_multiple_password'] ) ? $data_settings['ppwp_multiple_password'] : array();
+ if ( ! current_user_can( 'edit_post', $id ) ) {
+ wp_send_json(
+ array(
+ 'is_error' => true,
+ 'message' => PPW_Constants::BAD_REQUEST_MESSAGE,
+ ),
+ 403
+ );
+ wp_die();
+ }
+
$free_services = new PPW_Password_Services();
$current_roles_password = $free_services->create_new_password( $id, $role_selected, $new_global_passwords, $new_role_password );
wp_send_json( $current_roles_password );
@@ -280,6 +289,17 @@
);
wp_die();
}
+ $post_id = isset( $_request['postId'] ) ? absint( $_request['postId'] ) : 0;
+ if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
+ wp_send_json(
+ array(
+ 'is_error' => true,
+ 'message' => PPW_Constants::BAD_REQUEST_MESSAGE,
+ ),
+ 403
+ );
+ wp_die();
+ }
return $this->free_services->update_post_status( $_request );
}
--- a/password-protect-page/includes/class-ppw-api.php
+++ b/password-protect-page/includes/class-ppw-api.php
@@ -836,7 +836,10 @@
public function get_pcp_settings( $request ) {
$ppwp_db = new PPW_Repository_Passwords();
- $post_id = $request->get_param( 'id' );
+ $post_id = absint( $request->get_param( 'id' ) );
+ if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
+ return new WP_REST_Response( array( 'success' => false ), 403 );
+ }
$passwords = $ppwp_db->get_passwords_with_type_and_post_id( PPW_Content_Protection::PASSWORD_GLOBAL_TYPE, $post_id, 'password' );
$setting = array();
if ( count( $passwords ) > 0 ) {
@@ -860,9 +863,11 @@
public function update_pcp_settings( $request ) {
$ppwp_db = new PPW_Repository_Passwords();
$ppwp_area = new PPW_Content_Protection();
- $post_id = $request->get_param( 'id' );
+ $post_id = absint( $request->get_param( 'id' ) );
$passwords = $request->get_param( 'passwords' );
-
+ if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
+ return new WP_REST_Response( array( 'success' => false ), 403 );
+ }
$post = get_post( $post_id );
if ( ! $ppwp_area->check_area_exist( $post ) ) {
return new WP_REST_Response(
--- a/password-protect-page/wp-protect-password.php
+++ b/password-protect-page/wp-protect-password.php
@@ -1,186 +1,187 @@
-<?php
-/**
- * The plugin bootstrap file
- *
- * This file is read by WordPress to generate the plugin information in the plugin
- * admin area. This file also includes all of the dependencies used by the plugin,
- * registers the activation and deactivation functions, and defines a function
- * that starts the plugin.
- *
- * @link https://passwordprotectwp.com?utm_source=user-website&utm_medium=pluginsite_link&utm_campaign=ppwp
- * @since 1.7.6
- * @package Password_Protect_Page
- *
- * @wordpress-plugin
- * Plugin Name: Password Protect WordPress Lite
- * Plugin URI: https://passwordprotectwp.com?utm_source=user-website&utm_medium=pluginsite_link&utm_campaign=ppwp_lite
- * Description: Password protect the entire WordPress site, unlimited pages and posts by user roles. This plugin is required for our Pro version to work properly.
- * Version: 1.9.19
- * Author: BWPS
- * Author URI: https://passwordprotectwp.com
- * License: GPL-2.0+
- * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
- * Text Domain: password-protect-page
- * Domain Path: /languages
- */
-
-// If this file is called directly, abort.
-if ( ! defined( 'WPINC' ) ) {
- die;
-}
-
-/**
- * Currently plugin version.
- * Start at version 1.1.2 and use SemVer - https://semver.org
- * Rename this for your plugin and update it as you release new versions.
- */
-// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals
-define( 'PPW_VERSION', '1.9.19' );
-
-if ( ! defined( 'PPW_DIR_PATH' ) ) {
- define( 'PPW_DIR_PATH', plugin_dir_path( __FILE__ ) );
-}
-
-if ( ! defined( 'PPW_DIR_URL' ) ) {
- define( 'PPW_DIR_URL', plugin_dir_url( __FILE__ ) );
-}
-
-if ( ! defined( 'PPW_VIEW_URL' ) ) {
- define( 'PPW_VIEW_URL', plugin_dir_url( __FILE__ ) . 'includes/views/' );
-}
-
-if ( ! defined( 'PPW_PLUGIN_NAME' ) ) {
- define( 'PPW_PLUGIN_NAME', 'Password Protect WordPress Lite' );
-}
-
-if ( ! defined( 'PPW_PLUGIN_BASE_NAME' ) ) {
- define( 'PPW_PLUGIN_BASE_NAME', plugin_basename( __FILE__ ) );
-}
-
-if ( ! defined( 'PPWP_SIDEBAR_API' ) ) {
- define( 'PPWP_SIDEBAR_API', 'https://preventdirectaccess.com/wp-json/pda-fss-ppwp/v1/content' );
-}
-
-/**
- * The code that runs during plugin activation.
- * This action is documented in includes/class-ppw-activator.php
- */
-function activate_password_protect_page() {
- require_once plugin_dir_path( __FILE__ ) . 'includes/class-ppw-activator.php';
- PPW_Activator::activate();
-}
-
-/**
- * The code that runs during plugin deactivation.
- * This action is documented in includes/class-ppw-deactivator.php
- */
-function deactivate_password_protect_page() {
- require_once plugin_dir_path( __FILE__ ) . 'includes/class-ppw-deactivator.php';
- PPW_Deactivator::deactivate();
-}
-
-/**
- * The code that runs when uninstall plugin.
- */
-function uninstall_password_protect_page() {
- require_once plugin_dir_path( __FILE__ ) . 'includes/class-ppw-uninstall.php';
- PPW_Uninstall::uninstall();
-}
-
-register_activation_hook( __FILE__, 'activate_password_protect_page' );
-register_deactivation_hook( __FILE__, 'deactivate_password_protect_page' );
-register_uninstall_hook( __FILE__, 'uninstall_password_protect_page' );
-
-/**
- * The core plugin class that is used to define internationalization,
- * admin-specific hooks, and public-facing site hooks.
- */
-require plugin_dir_path( __FILE__ ) . 'includes/class-ppw.php';
-
-/**
- * Begins execution of the plugin
- *
- * Since everything within the plugin is registered via hooks,
- * then kicking off the plugin from this point in the file does
- * not affect the page life cycle.
- *
- * @since 1.1.2
- */
-function run_password_protect_page() {
- $plugin = new Password_Protect_Page();
- $plugin->run();
-}
-
-do_action( 'ppw_free/loaded' );
-
-add_action( 'plugins_loaded', 'ppw_check_environment_compatibility' );
-
-/**
- * Check PHP & WordPress version compatibility.
- */
-function ppw_check_environment_compatibility() {
- if ( ! version_compare( PHP_VERSION, '5.6', '>=' ) ) {
- add_action( 'admin_notices', 'ppw_fail_php_version' );
- } elseif ( ! version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) {
- add_action( 'admin_notices', 'ppw_fail_wp_version' );
- }
-}
-run_password_protect_page();
-
-
-add_action( 'plugins_loaded', 'ppw_free_load_plugin' );
-
-/**
- * Load migration service
- */
-function ppw_free_load_plugin() {
- global $migration_free_service;
- $migration_free_service = new PPW_Default_PW_Manager_Services();
- global $password_recovery_service;
- $password_recovery_service = new PPW_Password_Recovery_Manager();
-}
-
-/**
- * Function to check when PHP version is not supported.
- */
-function ppw_fail_php_version() {
- /* translators: %s: PHP version */
- $message = sprintf( esc_html__( 'Password Protect WordPress requires PHP version %s+, plugin is currently NOT WORKING.', 'password-protect-page' ), '5.6' );
- $html_message = sprintf( '<div class="error">%s</div>', wpautop( $message ) );
- echo wp_kses_post( $html_message );
-}
-
-/**
- * Function to check when WP version is not supported.
- */
-function ppw_fail_wp_version() {
- /* translators: %s: PHP version */
- $message = sprintf( esc_html__( 'Password Protect WordPress requires WordPress version %s+. Because you are using an earlier version, the plugin is currently NOT WORKING.', 'password-protect-page' ), '4.7' );
- $html_message = sprintf( '<div class="error">%s</div>', wpautop( $message ) );
- echo wp_kses_post( $html_message );
-}
-
-/* Plugin Analytics Data */
-function wpfolio_ppwp_analytics_load() {
-
- require_once dirname( __FILE__ ) . '/wpfolio-analytics/wpfolio-analytics.php';
-
- $wpfolio_analytics = wpfolio_ppwp_anylc_init_module( array(
- 'id' => 11,
- 'file' => plugin_basename( __FILE__ ),
- 'name' => 'Password Protect WordPress',
- 'slug' => 'wp_protect_password_options',
- 'tempslug' => 'wp_protect_password_options_optin',
- 'type' => 'plugin',
- 'menu' => 'wp_protect_password_options',
- 'redirect_page' => 'wp_protect_password_options',
- 'text_domain' => 'password-protect-page',
- ));
-
- return $wpfolio_analytics;
-}
-
-// Init Analytics
-wpfolio_ppwp_analytics_load();
-
+<?php
+/**
+ * The plugin bootstrap file
+ *
+ * This file is read by WordPress to generate the plugin information in the plugin
+ * admin area. This file also includes all of the dependencies used by the plugin,
+ * registers the activation and deactivation functions, and defines a function
+ * that starts the plugin.
+ *
+ * @link https://passwordprotectwp.com?utm_source=user-website&utm_medium=pluginsite_link&utm_campaign=ppwp
+ * @since 1.7.6
+ * @package Password_Protect_Page
+ *
+ * @wordpress-plugin
+ * Plugin Name: Password Protect WordPress Lite
+ * Plugin URI: https://passwordprotectwp.com?utm_source=user-website&utm_medium=pluginsite_link&utm_campaign=ppwp_lite
+ * Description: Password protect the entire WordPress site, unlimited pages and posts by user roles. This plugin is required for our Pro version to work properly.
+ * Version: 1.9.20
+ * Author: BWPS
+ * Author URI: https://passwordprotectwp.com
+ * License: GPL-2.0+
+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
+ * Text Domain: password-protect-page
+ * Domain Path: /languages
+ */
+
+// If this file is called directly, abort.
+if ( ! defined( 'WPINC' ) ) {
+ die;
+}
+
+/**
+ * Currently plugin version.
+ * Start at version 1.1.2 and use SemVer - https://semver.org
+ * Rename this for your plugin and update it as you release new versions.
+ */
+// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals
+
+define( 'PPW_VERSION', '1.9.20' );
+
+if ( ! defined( 'PPW_DIR_PATH' ) ) {
+ define( 'PPW_DIR_PATH', plugin_dir_path( __FILE__ ) );
+}
+
+if ( ! defined( 'PPW_DIR_URL' ) ) {
+ define( 'PPW_DIR_URL', plugin_dir_url( __FILE__ ) );
+}
+
+if ( ! defined( 'PPW_VIEW_URL' ) ) {
+ define( 'PPW_VIEW_URL', plugin_dir_url( __FILE__ ) . 'includes/views/' );
+}
+
+if ( ! defined( 'PPW_PLUGIN_NAME' ) ) {
+ define( 'PPW_PLUGIN_NAME', 'Password Protect WordPress Lite' );
+}
+
+if ( ! defined( 'PPW_PLUGIN_BASE_NAME' ) ) {
+ define( 'PPW_PLUGIN_BASE_NAME', plugin_basename( __FILE__ ) );
+}
+
+if ( ! defined( 'PPWP_SIDEBAR_API' ) ) {
+ define( 'PPWP_SIDEBAR_API', 'https://preventdirectaccess.com/wp-json/pda-fss-ppwp/v1/content' );
+}
+
+/**
+ * The code that runs during plugin activation.
+ * This action is documented in includes/class-ppw-activator.php
+ */
+function activate_password_protect_page() {
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-ppw-activator.php';
+ PPW_Activator::activate();
+}
+
+/**
+ * The code that runs during plugin deactivation.
+ * This action is documented in includes/class-ppw-deactivator.php
+ */
+function deactivate_password_protect_page() {
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-ppw-deactivator.php';
+ PPW_Deactivator::deactivate();
+}
+
+/**
+ * The code that runs when uninstall plugin.
+ */
+function uninstall_password_protect_page() {
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-ppw-uninstall.php';
+ PPW_Uninstall::uninstall();
+}
+
+register_activation_hook( __FILE__, 'activate_password_protect_page' );
+register_deactivation_hook( __FILE__, 'deactivate_password_protect_page' );
+register_uninstall_hook( __FILE__, 'uninstall_password_protect_page' );
+
+/**
+ * The core plugin class that is used to define internationalization,
+ * admin-specific hooks, and public-facing site hooks.
+ */
+require plugin_dir_path( __FILE__ ) . 'includes/class-ppw.php';
+
+/**
+ * Begins execution of the plugin
+ *
+ * Since everything within the plugin is registered via hooks,
+ * then kicking off the plugin from this point in the file does
+ * not affect the page life cycle.
+ *
+ * @since 1.1.2
+ */
+function run_password_protect_page() {
+ $plugin = new Password_Protect_Page();
+ $plugin->run();
+}
+
+do_action( 'ppw_free/loaded' );
+
+add_action( 'plugins_loaded', 'ppw_check_environment_compatibility' );
+
+/**
+ * Check PHP & WordPress version compatibility.
+ */
+function ppw_check_environment_compatibility() {
+ if ( ! version_compare( PHP_VERSION, '5.6', '>=' ) ) {
+ add_action( 'admin_notices', 'ppw_fail_php_version' );
+ } elseif ( ! version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) {
+ add_action( 'admin_notices', 'ppw_fail_wp_version' );
+ }
+}
+run_password_protect_page();
+
+
+add_action( 'plugins_loaded', 'ppw_free_load_plugin' );
+
+/**
+ * Load migration service
+ */
+function ppw_free_load_plugin() {
+ global $migration_free_service;
+ $migration_free_service = new PPW_Default_PW_Manager_Services();
+ global $password_recovery_service;
+ $password_recovery_service = new PPW_Password_Recovery_Manager();
+}
+
+/**
+ * Function to check when PHP version is not supported.
+ */
+function ppw_fail_php_version() {
+ /* translators: %s: PHP version */
+ $message = sprintf( esc_html__( 'Password Protect WordPress requires PHP version %s+, plugin is currently NOT WORKING.', 'password-protect-page' ), '5.6' );
+ $html_message = sprintf( '<div class="error">%s</div>', wpautop( $message ) );
+ echo wp_kses_post( $html_message );
+}
+
+/**
+ * Function to check when WP version is not supported.
+ */
+function ppw_fail_wp_version() {
+ /* translators: %s: PHP version */
+ $message = sprintf( esc_html__( 'Password Protect WordPress requires WordPress version %s+. Because you are using an earlier version, the plugin is currently NOT WORKING.', 'password-protect-page' ), '4.7' );
+ $html_message = sprintf( '<div class="error">%s</div>', wpautop( $message ) );
+ echo wp_kses_post( $html_message );
+}
+
+/* Plugin Analytics Data */
+function wpfolio_ppwp_analytics_load() {
+
+ require_once dirname( __FILE__ ) . '/wpfolio-analytics/wpfolio-analytics.php';
+
+ $wpfolio_analytics = wpfolio_ppwp_anylc_init_module( array(
+ 'id' => 11,
+ 'file' => plugin_basename( __FILE__ ),
+ 'name' => 'Password Protect WordPress',
+ 'slug' => 'wp_protect_password_options',
+ 'tempslug' => 'wp_protect_password_options_optin',
+ 'type' => 'plugin',
+ 'menu' => 'wp_protect_password_options',
+ 'redirect_page' => 'wp_protect_password_options',
+ 'text_domain' => 'password-protect-page',
+ ));
+
+ return $wpfolio_analytics;
+}
+
+// Init Analytics
+wpfolio_ppwp_analytics_load();
+
require_once __DIR__ . '/admin/class-ppw-deactivation-feedback.php';
No newline at end of file