--- a/awesome-support/awesome-support.php
+++ b/awesome-support/awesome-support.php
@@ -10,7 +10,7 @@
* Plugin Name: Awesome Support
* Plugin URI: https://getawesomesupport.com
* Description: Awesome Support is a great ticketing system that will help you improve your customer satisfaction by providing a unique customer support experience.
- * Version: 6.3.6
+ * Version: 6.3.7
* Author: Awesome Support Team
* Author URI: https://getawesomesupport.com
* Text Domain: awesome-support
@@ -252,7 +252,7 @@
* @return void
*/
private function setup_constants() {
- define( 'WPAS_VERSION', '6.3.6' );
+ define( 'WPAS_VERSION', '6.3.7' );
define( 'WPAS_DB_VERSION', '1' );
define( 'WPAS_URL', trailingslashit( plugin_dir_url( __FILE__ ) ) );
define( 'WPAS_PATH', trailingslashit( plugin_dir_path( __FILE__ ) ) );
--- a/awesome-support/includes/admin/metaboxes/details.php
+++ b/awesome-support/includes/admin/metaboxes/details.php
@@ -51,8 +51,8 @@
<?php if ( 'post-new.php' != $pagenow ):
wpas_cf_display_status( '', $post->ID );
?>
- <?php else: ?>
- <span><?php _x( 'Creating...', 'Ticket creation', 'awesome-support' ); ?></span>
+ <?php else: ?>
+ <span><?php echo _x( 'Creating...', 'Ticket creation', 'awesome-support' ); ?></span>
<?php endif; ?>
</div>
<div class="wpas-col">
--- a/awesome-support/includes/admin/views/about-tab-change-log.php
+++ b/awesome-support/includes/admin/views/about-tab-change-log.php
@@ -5,6 +5,20 @@
<div class="row">
<div>
<div class="about-body">
+ <h1>What's New In 6.3.7</h1>
+ <h3>6.3.7 Revised plugin deactivation process with feedback.</h3>
+ <ul>
+ <li>Update default fallback rules for the .htaccess file in the ticket folder.</li>
+ <li>Fix Missing Authorization to Unauthenticated Role Demotion.</li>
+ <li>Fix vulnerable issue of Unauthenticated Sensitive Information Disclosure.</li>
+ </ul>
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <div>
+ <div class="about-body">
<h1>What's New In 6.3.6</h1>
<h3>6.3.6 Revised plugin deactivation process with feedback.</h3>
<ul>
--- a/awesome-support/includes/file-uploader/class-file-uploader.php
+++ b/awesome-support/includes/file-uploader/class-file-uploader.php
@@ -136,6 +136,9 @@
add_action( 'wpas_after_close_ticket', array( $this, 'wpas_maybe_delete_attachments_after_close_ticket' ), 11, 3 );
+ // One-time fix for .htaccess files containing 'Deny from all'
+ add_action( 'admin_init', array( $this, 'fix_htaccess_files_once' ), 10 );
+
}
@@ -860,13 +863,9 @@
$filename = $dir . '/.htaccess';
- // Default fallback rules
- $filecontents = wpas_get_option( 'htaccess_contents_for_attachment_folders', '' );
+ $filecontents = wpas_get_option( 'htaccess_contents_for_attachment_folders', 'Options -Indexes' ) ;
if ( empty( $filecontents ) ) {
- $filecontents = "Options -Indexesn";
- $filecontents .= "<FilesMatch ".*">n";
- $filecontents .= "Deny from alln";
- $filecontents .= "</FilesMatch>n";
+ $filecontents = 'Options -Indexes' ;
}
if ( ! file_exists( $filename ) ) {
@@ -921,6 +920,85 @@
}
/**
+ * One-time fix for .htaccess files containing 'Deny from all'
+ *
+ * This function runs once to fix all .htaccess files containing 'Deny from all'
+ * in the attachment folders of all existing tickets.
+ * It replaces the content with the content defined in the options.
+ *
+ * @since 1.0.0
+ * @return void
+ */
+ public function fix_htaccess_files_once() {
+
+ // Check if the fix has already been performed
+ $fix_done = get_option( 'wpas_htaccess_deny_all_fixed', false );
+
+ if ( $fix_done ) {
+ return;
+ }
+
+ global $wp_filesystem;
+
+ // Initialize the filesystem
+ if ( empty( $wp_filesystem ) ) {
+ require_once( ABSPATH . '/wp-admin/includes/file.php' );
+ WP_Filesystem();
+ }
+
+ // Get the correct content for .htaccess files
+ $filecontents = wpas_get_option( 'htaccess_contents_for_attachment_folders', 'Options -Indexes' );
+ if ( empty( $filecontents ) ) {
+ $filecontents = 'Options -Indexes';
+ }
+
+ // Get all tickets
+ $tickets = get_posts( array(
+ 'post_type' => 'ticket',
+ 'post_status' => 'any',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ ) );
+
+ $upload_dir = wp_upload_dir();
+ $base_dir = $upload_dir['basedir'];
+ $fixed_count = 0;
+
+ // Loop through all tickets
+ foreach ( $tickets as $ticket_id ) {
+
+ // Calculate the attachment folder name (same method as in set_upload_dir)
+ $ticket_id_encode = md5( $ticket_id . NONCE_SALT );
+ $ticket_dir = $base_dir . '/awesome-support/ticket_' . $ticket_id_encode;
+ $htaccess_file = $ticket_dir . '/.htaccess';
+
+ // Check if the .htaccess file exists
+ if ( file_exists( $htaccess_file ) ) {
+
+ // Read the current content
+ $current_content = $wp_filesystem->get_contents( $htaccess_file );
+
+ // Check if the file contains 'Deny from all' (case insensitive)
+ if ( $current_content !== false && stripos( $current_content, 'Deny from all' ) !== false ) {
+
+ // Replace the content with the new content
+ $result = $wp_filesystem->put_contents( $htaccess_file, $filecontents, FS_CHMOD_FILE );
+
+ if ( $result !== false ) {
+ $fixed_count++;
+ wpas_write_log( 'file-uploader', '.htaccess file fixed for ticket #' . $ticket_id . ' (' . $htaccess_file . ')' );
+ } else {
+ wpas_write_log( 'file-uploader', 'Unable to fix .htaccess file for ticket #' . $ticket_id . ' (' . $htaccess_file . ')' );
+ }
+ }
+ }
+ }
+
+ // Mark the fix as completed
+ update_option( 'wpas_htaccess_deny_all_fixed', true );
+ }
+
+ /**
* Add dropzone markup.
*
* @return void
--- a/awesome-support/includes/functions-actions.php
+++ b/awesome-support/includes/functions-actions.php
@@ -25,25 +25,26 @@
function wpas_process_actions() {
$nonce = false;
-
- if ( isset( $_POST['wpas-do-nonce'] ) ) {
- $nonce = sanitize_text_field( wp_unslash( $_POST['wpas-do-nonce'] ) );
- } elseif ( isset( $_GET['wpas-do-nonce'] ) ) {
- $nonce = sanitize_text_field( wp_unslash( $_GET['wpas-do-nonce'] ) );
- }
-
- if ( ! $nonce || ! wp_verify_nonce( $nonce, 'trigger_custom_action' ) ) {
- return;
- }
+ $action = '';
+ if ( isset( $_POST['wpas-do-nonce'] ) ) {
+ $nonce = sanitize_text_field( wp_unslash( $_POST['wpas-do-nonce'] ) );
+ $action = isset( $_POST['wpas-do'] ) ? sanitize_text_field( wp_unslash( $_POST['wpas-do'] ) ) : '';
+ } elseif ( isset( $_GET['wpas-do-nonce'] ) ) {
+ $nonce = sanitize_text_field( wp_unslash( $_GET['wpas-do-nonce'] ) );
+ $action = isset( $_GET['wpas-do'] ) ? sanitize_text_field( wp_unslash( $_GET['wpas-do'] ) ) : '';
+ }
+
+ // FIX: Use action-specific nonce verification
+ if ( ! $nonce || ! $action || ! wp_verify_nonce( $nonce, 'wpas_do_' . $action ) ) {
+ return;
+ }
- if ( isset( $_POST['wpas-do'] ) ) {
- $wpas_do = sanitize_text_field( wp_unslash( $_POST['wpas-do'] ) );
- do_action( 'wpas_do_' . $wpas_do, $_POST );
+ if ( isset( $_POST['wpas-do'] ) ) {
+ do_action( 'wpas_do_' . $action, $_POST );
}
- if ( isset( $_GET['wpas-do'] ) ) {
- $wpas_do = sanitize_text_field( wp_unslash( $_GET['wpas-do'] ) );
- do_action( 'wpas_do_' . $wpas_do, $_GET );
+ if ( isset( $_GET['wpas-do'] ) ) {
+ do_action( 'wpas_do_' . $action, $_GET );
}
}
@@ -63,7 +64,7 @@
$field = sprintf( '<input type="hidden" name="%1$s" value="%2$s">', 'wpas-do', $action );
- $field .= wp_nonce_field( 'trigger_custom_action', 'wpas-do-nonce', true, false );
+ $field .= wp_nonce_field( 'wpas_do_' . $action, 'wpas-do-nonce', true, false );
$field = str_replace( 'id="wpas-do-nonce"' , 'id="wpas-do-nonce-' . $action . '"' , $field );
@@ -99,7 +100,7 @@
function wpas_do_url( $url, $action, $args = array() ) {
$args['wpas-do'] = $action;
- $args['wpas-do-nonce'] = wp_create_nonce( 'trigger_custom_action' );
+ $args['wpas-do-nonce'] = wp_create_nonce( 'wpas_do_' . $action );
$url = esc_url( add_query_arg( $args, $url ) );
return $url;
--- a/awesome-support/includes/functions-templating.php
+++ b/awesome-support/includes/functions-templating.php
@@ -1682,3 +1682,57 @@
}
// Add filter to `the_title` hook.
add_filter( 'the_title', 'wpas_single_ticket_title', 10, 1 );
+
+ /**
+ * Alter document title for single ticket to prevent information disclosure.
+ *
+ * This function prevents the ticket title from being leaked in the HTML page title
+ * for unauthenticated users or users who don't have permission to view the ticket.
+ *
+ * @since 6.0.0
+ *
+ * @param array $title_parts The document title parts
+ *
+ * @return array Modified title parts
+ */
+ function wpas_single_ticket_document_title( $title_parts = array() ) {
+
+ global $post;
+
+ $slug = 'ticket';
+
+ /* Don't touch the admin */
+ if ( is_admin() ) {
+ return $title_parts;
+ }
+
+ /* Only apply this on the ticket single. */
+ if ( ! $post || $slug !== $post->post_type ) {
+ return $title_parts;
+ }
+
+ /* Only apply this on the main query. */
+ if ( ! is_main_query() ) {
+ return $title_parts;
+ }
+
+ /* Check if the current user can view the ticket */
+ $can_view = wpas_can_view_ticket( $post->ID );
+
+ /* Check if the ticket is public (wpas_pbtk_flag) - only allow public access if the Public Tickets add-on is active */
+ if ( class_exists( 'AS_Publictickets_Loader' ) && 'public' === get_post_meta( $post->ID, '_wpas_pbtk_flag', true ) ) {
+ $can_view = true;
+ }
+
+ if ( ! $can_view ) {
+ /* Replace the ticket title with a generic title to prevent information disclosure */
+ if ( isset( $title_parts['title'] ) ) {
+ $title_parts['title'] = __( 'No tickets found.', 'awesome-support' );
+ }
+ }
+
+ return $title_parts;
+
+ }
+ // Add filter to `document_title_parts` hook (WordPress 4.4+).
+ add_filter( 'document_title_parts', 'wpas_single_ticket_document_title', 10, 1 );
No newline at end of file
--- a/awesome-support/includes/functions-user.php
+++ b/awesome-support/includes/functions-user.php
@@ -1683,6 +1683,16 @@
if( $user_id ) {
+ // FIX: Add capability check
+ if ( ! current_user_can( 'edit_users' ) ) {
+ wp_die( __( 'You do not have permission to activate users.', 'awesome-support' ), 403 );
+ }
+
+ // FIX: Verify current user can edit the target user
+ if ( ! current_user_can( 'edit_user', $user_id ) ) {
+ wp_die( __( 'You do not have permission to edit this user.', 'awesome-support' ), 403 );
+ }
+
$role = wpas_get_option( 'moderated_activated_user_role' );
$updated = wp_update_user( array( 'ID' => $user_id, 'role' => $role ) );
--- a/awesome-support/includes/gdpr-integration/gdpr-user-profile.php
+++ b/awesome-support/includes/gdpr-integration/gdpr-user-profile.php
@@ -586,12 +586,7 @@
if ( $wp_filesystem->is_writable($dir) ) {
- $filename = $dir . '/.htaccess';
-
- $filecontents = "Options -Indexesn";
- $filecontents .= "<FilesMatch ".*">n";
- $filecontents .= "Deny from alln";
- $filecontents .= "</FilesMatch>n";
+ $filecontents = 'Options -Indexes';
if ( ! file_exists( $filename ) ) {
$result = $wp_filesystem->put_contents($filename, $filecontents, FS_CHMOD_FILE);
--- a/awesome-support/includes/rest-api/includes/API/Passwords.php
+++ b/awesome-support/includes/rest-api/includes/API/Passwords.php
@@ -64,7 +64,7 @@
'permission_callback' => array( $this, 'get_passwords_permissions_check' ),
),
'schema' => array( $this, 'get_public_item_schema' ),
- ) );
+ ) );
register_rest_route( $this->namespace, '/users/(?P<user_id>[d]+)/' . $this->rest_base . '/(?P<slug>[da-fA-F]{12})', array(
'args' => array(
@@ -88,11 +88,12 @@
) );
// Some hosts that run PHP in FastCGI mode won't be given the Authentication header.
+ // SECURITY FIX: CVE-2025-53340 - Restrict access to administrators only
register_rest_route( $this->namespace, '/test-basic-authorization-header/', array(
array(
'methods' => WP_REST_Server::READABLE . ', ' . WP_REST_Server::CREATABLE,
'callback' => array( $this, 'test_basic_authorization_header' ),
- 'permission_callback' => '__return_true',
+ 'permission_callback' => array( $this, 'test_basic_auth_permissions_check' ),
),
'schema' => array( $this, 'test_schema' ),
) );
@@ -111,6 +112,19 @@
}
/**
+ * Checks if a given request has access to test Basic Auth headers.
+ * Only administrators can access this diagnostic endpoint.
+ *
+ * SECURITY FIX: CVE-2025-53340 - Restrict access to prevent unauthenticated access
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return bool True if the request has access, otherwise false.
+ */
+ public function test_basic_auth_permissions_check( $request ) {
+ return is_user_logged_in() && current_user_can( 'manage_options' );
+ }
+
+ /**
* Retrieves the passwords.
*
* @param WP_REST_Request $request Full details about the request.
@@ -293,14 +307,15 @@
*/
public function test_basic_authorization_header() {
+
$response = array();
if ( isset( $_SERVER['PHP_AUTH_USER'] ) ) {
- $response['PHP_AUTH_USER'] = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) );
+ $response['BASIC_AUTH_USER'] = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) );
}
if ( isset( $_SERVER['PHP_AUTH_PW'] ) ) {
- $response['PHP_AUTH_PW'] = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
+ $response['BASIC_AUTH_PW'] = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
}
if ( empty( $response ) ) {