diff --git a/changes.md b/changes.md index 3f3449d..c663fac 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,7 @@ +### 1.3.5 + +* Security hardening of the admin notice dismissal AJAX handler: require a valid nonce, require the `manage_options` capability, and restrict writable option keys to the `dismiss_*_notice` pattern. + ### 1.3.4 * Fix notice about translations loaded too soon diff --git a/lib/classes/class-bootstrap.php b/lib/classes/class-bootstrap.php index f395058..00c6423 100644 --- a/lib/classes/class-bootstrap.php +++ b/lib/classes/class-bootstrap.php @@ -595,25 +595,43 @@ public function define_license_manager() { } public function ud_bootstrap_dismiss_notice() { + check_ajax_referer( 'ud_bootstrap_dismiss_notice', 'nonce' ); + $response = array( 'success' => '0', 'error' => __( 'There was an error in request.', $this->domain ), ); $error = false; - if( empty( $_POST['key'] ) || + //** Dismissing a notice writes global state, so it requires the same + //** capability as managing the product itself. */ + if( ! current_user_can( 'manage_options' ) ) { + $response['error'] = __( 'You are not allowed to do this action.', $this->domain ); + $error = true; + } + + //** Only the notice-dismissal options this instance actually reads back + //** (dismiss___notice) may be written - never an arbitrary key. */ + $option_key = isset( $_POST['key'] ) ? sanitize_key( $_POST['key'] ) : ''; + + if( ! $error && ( strpos( $option_key, 'dismiss_' ) !== 0 || substr( $option_key, -7 ) !== '_notice' ) ) { + $response['error'] = __( 'Invalid key', $this->domain ); + $error = true; + } + + if( ! $error && ( empty( $_POST['slug'] ) || empty( $_POST['type'] ) || empty( $_POST['version'] ) - ) { + ) ) { $response['error'] = __( 'Invalid values', $this->domain ); $error = true; } - if ( ! $error && update_option( ( $_POST['key'] ), array( - 'slug' => $_POST['slug'], - 'type' => $_POST['type'], - 'version' => $_POST['version'], + if ( ! $error && update_option( $option_key, array( + 'slug' => sanitize_key( $_POST['slug'] ), + 'type' => sanitize_key( $_POST['type'] ), + 'version' => sanitize_text_field( $_POST['version'] ), ) ) ) { $response['success'] = '1'; }