Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/request-a-quote/assets/ext/filepicker/upload.php
+++ b/request-a-quote/assets/ext/filepicker/upload.php
@@ -26,48 +26,70 @@
'min_width' => __('Image requires a minimum width','request-a-quote'),
'max_height' => __('Image exceeds maximum height','request-a-quote'),
'min_height' => __('Image requires a minimum height','request-a-quote')
- );
+ );
- if (!empty($_FILES)) {
- $upload_file = 1;
+ if (!empty($_FILES) && isset($_FILES['file'])) {
+ $upload_file = 0;
// Validate the file type
- if(!empty($fileTypes))
+ if(empty($fileTypes))
{
- $fileTypes_arr = explode(",",$fileTypes);
-
- $fileParts = pathinfo($_FILES['file']['name']);
- if (!in_array(strtolower($fileParts['extension']),$fileTypes_arr)) {
- $upload_file = 0;
- echo esc_html__('Invalid file type.','request-a-quote');
- }
- else {
- $upload_file = 1;
- }
+ $fileTypes_arr = array('jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'zip');
+ }
+ else {
+ $fileTypes_arr = is_array($fileTypes) ? $fileTypes : explode(",", $fileTypes);
+ }
+ $fileTypes_arr = array_map('trim', $fileTypes_arr);
+ $fileTypes_arr = array_map('strtolower', $fileTypes_arr);
+
+ $original_name = $_FILES['file']['name'];
+ $fileParts = pathinfo($original_name);
+ $file_ext = isset($fileParts['extension']) ? strtolower($fileParts['extension']) : '';
+
+ if (empty($file_ext) || !in_array($file_ext, $fileTypes_arr, true)) {
+ echo esc_html__('Invalid file type.', 'request-a-quote');
+ return; // Halt execution immediately
+ }
+ if (strpos(strtolower($original_name), '.php') !== false) {
+ echo esc_html__('Invalid file type signature.', 'request-a-quote');
+ return;
}
-
- if($upload_file == 1){
- $file = wp_handle_upload($_FILES['file'] , array( 'test_form' => false ) );
- if(isset($file['error'])){
+ // If it survives all checks above, flag it as safe to upload
+ $upload_file = 1;
+
+ if ($upload_file === 1) {
+ // Sanitize the file name inside the $_FILES array before WordPress touches it
+ $_FILES['file']['name'] = sanitize_file_name($original_name);
+
+ $file = wp_handle_upload($_FILES['file'], array('test_form' => false));
+
+ if (isset($file['error'])) {
echo esc_html($file['error']);
- }
- else {
+ } else {
$_FILES['file']['path'] = $file['file'];
- if(!empty($myapp)){
- $new_sess_files = Array();
- $sess_name = strtoupper($myapp);
- $session_class = $sess_name();
- $sess_files = $session_class->session->get('uploads');
- if(!empty($sess_files) && is_array($sess_files)){
- $new_sess_files = $sess_files;
- }
- if(empty($sess_files[$fieldid])){
- $new_sess_files[$fieldid][] = $_FILES['file'];
- }
- elseif(is_array($sess_files[$fieldid])){
- $new_sess_files[$fieldid] = $sess_files[$fieldid];
- $new_sess_files[$fieldid][] = $_FILES['file'];
+
+ if (!empty($myapp)) {
+ $new_sess_files = array();
+
+ // Sanitize $myapp dynamically to prevent arbitrary class instantiation attacks
+ $clean_myapp = preg_replace('/[^a-zA-Z0-9_-]/', '', $myapp);
+ $sess_name = strtoupper($clean_myapp);
+
+ if (function_exists($sess_name)) {
+ $session_class = $sess_name();
+ $sess_files = $session_class->session->get('uploads');
+
+ if (!empty($sess_files) && is_array($sess_files)) {
+ $new_sess_files = $sess_files;
+ }
+
+ if (empty($sess_files[$fieldid])) {
+ $new_sess_files[$fieldid][] = $_FILES['file'];
+ } elseif (is_array($sess_files[$fieldid])) {
+ $new_sess_files[$fieldid] = $sess_files[$fieldid];
+ $new_sess_files[$fieldid][] = $_FILES['file'];
+ }
+ $session_class->session->set('uploads', $new_sess_files);
}
- $session_class->session->set('uploads',$new_sess_files);
}
echo '1';
}
--- a/request-a-quote/includes/common-functions.php
+++ b/request-a-quote/includes/common-functions.php
@@ -164,24 +164,29 @@
$search = $query->query_vars['s'];
foreach (array_values($set_types) as $ptype) {
$pids = apply_filters('emd_limit_by', $pids, $app, $ptype, 'frontend');
- $diff_pids = array_diff($pids,Array('0'));
+ $diff_pids = array_diff($pids,Array('0'));
+ // Prepare wildcard searching safely
+ // esc_like() ensures literal '%' or '_' in user input doesn't break logic
+ $wildcard_search = '%' . $wpdb->esc_like($search) . '%';
+
if(empty($pids)){
- $input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".post_type ='" . $ptype . "' AND " . $wpdb->posts . ".post_status = 'publish' AND ";
+ $input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".post_type ='" . esc_sql($ptype) . "' AND " . $wpdb->posts . ".post_status = 'publish' AND ";
if($type == 'author'){
$input_add .= $wpdb->posts . ".post_author=" . $auth_id . ")";
}
elseif($type == 'search'){
- $input_add .= "(" . $wpdb->posts . ".post_title LIKE '%" . $search . "%' OR " . $wpdb->posts . ".post_content LIKE '%" . $search . "%'))";
+ $input_add .= $wpdb->prepare("(" . $wpdb->posts . ".post_title LIKE %s OR " . $wpdb->posts . ".post_content LIKE %s))", $wildcard_search,$wildcard_search);
}
}
elseif(!empty($diff_pids)) {
- $pids_arr = "(" . implode(",",$pids) . ")";
+ $pids_cleaned = array_map('intval', $pids);
+ $pids_arr = "(" . implode(",", $pids_cleaned) . ")";
$input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".ID IN " . $pids_arr . " AND ";
if($type == 'author'){
$input_add .= $wpdb->posts . ".post_author=" . $auth_id . ")";
}
elseif($type == 'search'){
- $input_add .= "(" . $wpdb->posts . ".post_title LIKE '%" . $search . "%') OR (" . $wpdb->posts . ".post_content LIKE '%" . $search . "%'))";
+ $input_add .= $wpdb->prepare("(" . $wpdb->posts . ".post_title LIKE %s) OR (" . $wpdb->posts . ".post_content LIKE %s))",$wildcard_search,$wildcard_search);
}
}
}
@@ -1018,11 +1023,38 @@
echo '<div class="text-danger"><a href="' . wp_get_referer() . '">' . esc_html__('Please refresh the page and try again.', 'request-a-quote') . '</a></div>';
die();
}
- $path = sanitize_text_field($_POST['path']);
- $myapp = strtolower(preg_replace('/_PLUGIN_DIR$/','',$path));
- require_once constant($path) . 'assets/ext/filepicker/upload.php';
- $upload_handler = new UploadHandler(true, sanitize_text_field($_POST['field']), sanitize_text_field($_POST['extensions']),$myapp);
- die();
+ if ( ! defined( 'REQUEST_A_QUOTE_PLUGIN_DIR' ) ) {
+ echo '<div class="text-danger">' . esc_html__('Configuration error.', 'request-a-quote') . '</div>';
+ die();
+ }
+ $myapp = 'request_a_quote';
+ require_once REQUEST_A_QUOTE_PLUGIN_DIR . 'assets/ext/filepicker/upload.php';
+
+ $master_allowed = array('jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'zip');
+
+ $final_extensions = array();
+ if ( ! empty( $_POST['extensions'] ) ) {
+ // Clean the incoming text field and convert it to an array
+ $user_input = sanitize_text_field( $_POST['extensions'] );
+ $user_extensions = explode( ',', $user_input );
+
+ foreach ( $user_extensions as $ext ) {
+ $ext = strtolower( trim( $ext ) ); // Normalize
+
+ if ( in_array( $ext, $master_allowed, true ) ) {
+ $final_extensions[] = $ext;
+ }
+ }
+ }
+
+ if ( empty( $final_extensions ) ) {
+ $final_extensions = $master_allowed;
+ }
+
+ $field = isset($_POST['field']) ? sanitize_text_field($_POST['field']) : '';
+
+ $upload_handler = new UploadHandler(true, $field, $final_extensions, $myapp);
+ die();
}
}
if (!function_exists('emd_delete_file')) {
@@ -1030,24 +1062,35 @@
$ret = check_ajax_referer('emd_delete_file', 'nonce', false);
if ($ret === false) {
echo '<div class="text-danger"><a href="' . wp_get_referer() . '">' . esc_html__('Please refresh the page and try again.', 'request-a-quote') . '</a></div>';
- die();
+ wp_die();
}
- $path = sanitize_text_field($_POST['path']);
- $myapp = strtolower(preg_replace('/_PLUGIN_DIR$/','',$path));
+ $myapp = 'request_a_quote';
$sess_name = strtoupper($myapp);
- $session_class = $sess_name();
+ if ( function_exists($sess_name) ) {
+ $session_class = $sess_name();
+ } else {
+ echo '<div class="text-danger">' . esc_html__('System configuration error.', 'request-a-quote') . '</div>';
+ wp_die();
+ }
+ if ( ! $session_class || ! isset($session_class->session) ) {
+ echo '<div class="text-danger">' . esc_html__('Session handler unavailable.', 'request-a-quote') . '</div>';
+ wp_die();
+ }
$sess_files = $session_class->session->get('uploads');
- $field = sanitize_text_field($_POST['field']);
- if(!empty($sess_files[$field])){
- foreach($sess_files[$field] as $kattch => $myattch){
- if($myattch['name'] == sanitize_text_field($_POST['del_file'])){
+ $field = isset($_POST['field']) ? sanitize_text_field($_POST['field']) : '';
+ if ( ! empty( $sess_files[$field] ) && isset( $_POST['del_file'] ) ) {
+ $del_file_target = sanitize_text_field($_POST['del_file']);
+
+ foreach ( $sess_files[$field] as $kattch => $myattch ) {
+ if ( isset($myattch['full_path']) && $myattch['full_path'] === $del_file_target ) {
unset($sess_files[$field][$kattch]);
}
}
- $session_class->session->set('uploads',$sess_files);
+ // Update the user's specific session
+ $session_class->session->set('uploads', $sess_files);
}
echo 1;
- die();
+ wp_die();
}
}
if(!function_exists('emd_get_attachment_layout')){
--- a/request-a-quote/request-a-quote.php
+++ b/request-a-quote/request-a-quote.php
@@ -3,7 +3,7 @@
* Plugin Name: Request a quote
* Plugin URI: https://emdplugins.com
* Description: Request a quote provides an easy to use request a quote form, stores and displays quote requests from customers.
- * Version: 2.5.5
+ * Version: 2.5.6
* Author: eMarketDesign
* Author URI: https://emdplugins.com
* Text Domain: request-a-quote
@@ -83,7 +83,7 @@
* @return void
*/
private function define_constants() {
- define('REQUEST_A_QUOTE_VERSION', '2.5.5');
+ define('REQUEST_A_QUOTE_VERSION', '2.5.6');
define('REQUEST_A_QUOTE_AUTHOR', 'eMarketDesign');
define('REQUEST_A_QUOTE_NAME', 'Request a quote');
define('REQUEST_A_QUOTE_PLUGIN_FILE', __FILE__);
@@ -349,4 +349,4 @@
return Request_a_quote::instance();
}
// Get the Request_a_quote instance
-REQUEST_A_QUOTE();
No newline at end of file
+REQUEST_A_QUOTE();