Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/js-support-ticket/includes/activation.php
+++ b/js-support-ticket/includes/activation.php
@@ -201,8 +201,8 @@
('tplink_faqs_user', '0', 'tplink', 'faq'),
('show_breadcrumbs', '1', 'default', NULL),
('productcode', 'jsticket', 'default', NULL),
- ('versioncode', '3.0.3', 'default', NULL),
- ('productversion', '303', 'default', NULL),
+ ('versioncode', '3.0.4', 'default', NULL),
+ ('productversion', '304', 'default', NULL),
('producttype', 'free', 'default', NULL),
('tve_enabled', '2', 'default', NULL),
('tve_mailreadtype', '3', 'default', NULL),
--- a/js-support-ticket/js-support-ticket.php
+++ b/js-support-ticket/js-support-ticket.php
@@ -3,14 +3,14 @@
/**
* @package JS Help Desk
* @author Ahmad Bilal
- * @version 3.0.3
+ * @version 3.0.4
*/
/*
Plugin Name: JS Help Desk – AI-Powered Support & Ticketing System
Plugin URI: https://www.jshelpdesk.com
Description: JS Help Desk is a trusted open source ticket system. JS Help Desk is a simple, easy to use, web-based customer support system. User can create ticket from front-end. JS Help Desk comes packed with lot features than most of the expensive(and complex) support ticket system on market. JS Help Desk provide you best industry help desk system.
Author: JS Help Desk
- Version: 3.0.3
+ Version: 3.0.4
Text Domain: js-support-ticket
License: GPLv3
Author URI: https://www.jshelpdesk.com
@@ -67,7 +67,7 @@
self::$jsst_data = array();
self::$_search = array();
self::$_captcha = array();
- self::$_currentversion = '303';
+ self::$_currentversion = '304';
self::$_addon_query = array('select'=>'','join'=>'','where'=>'');
self::$_jshdsession = JSSTincluder::getObjectClass('wphdsession');
global $wpdb;
@@ -147,7 +147,7 @@
// restore colors data end
update_option('jsst_currentversion', self::$_currentversion);
include_once JSST_PLUGIN_PATH . 'includes/updates/updates.php';
- JSSTupdates::checkUpdates('303');
+ JSSTupdates::checkUpdates('304');
JSSTincluder::getJSModel('jssupportticket')->updateColorFile();
JSSTincluder::getJSModel('jssupportticket')->jsst_check_license_status();
JSSTincluder::getJSModel('jssupportticket')->JSSTAddonsAutoUpdate();
--- a/js-support-ticket/modules/jssupportticket/controller.php
+++ b/js-support-ticket/modules/jssupportticket/controller.php
@@ -22,7 +22,7 @@
case 'controlpanel':
JSSTincluder::getJSModel('jssupportticket')->getControlPanelData();
include_once JSST_PLUGIN_PATH . 'includes/updates/updates.php';
- JSSTupdates::checkUpdates('303');
+ JSSTupdates::checkUpdates('304');
JSSTincluder::getJSModel('jssupportticket')->updateColorFile();
//JSSTincluder::getJSModel('jssupportticket')->getStaffControlPanelData();
break;
--- a/js-support-ticket/modules/reply/model.php
+++ b/js-support-ticket/modules/reply/model.php
@@ -510,18 +510,39 @@
// Verify nonce
check_ajax_referer('get-filtered-replies', '_wpnonce');
- $jsst_ticket_id = intval(JSSTrequest::getVar('ticket_id'));
+ // Secure the ID (Stops SQL Injection)
+ $jsst_ticket_id = JSSTrequest::getVar('ticket_id', null, 0, 'int');
if (!$jsst_ticket_id) {
wp_send_json_error(['message' => __('Ticket ID is required.', 'js-support-ticket')]);
}
+ // 1. Check if the user is a Agent
+ $is_staff = (in_array('agent', jssupportticket::$_active_addons) && JSSTincluder::getJSModel('agent')->isUserStaff());
+
+ // 2. If they are staff, check if they LACK the specific AI permission
+ if ($is_staff) {
+ $has_ai_permission = JSSTincluder::getJSModel('userpermissions')->checkPermissionGrantedForTask('Use AI Powered Reply Feature');
+ if (!$has_ai_permission) { // Note the "!" (NOT)
+ wp_send_json_error(['message' => __('You do not have permission to use AI features.', 'js-support-ticket')]);
+ }
+ }
+ // 3. If they are NOT staff, check if they are an Administrator
+ else if (!current_user_can('manage_options')) {
+ // If they aren't staff and aren't an admin, they are a normal user or guest
+ wp_send_json_error(['message' => __('Access denied.', 'js-support-ticket')]);
+ }
+
+ // If it reaches here, the user is either:
+ // - Staff WITH AI permissions
+ // - An Administrator
+
$jsst_uids = $this->get_allowed_support_user_ids();
if (empty($jsst_uids)) {
wp_send_json_success(['replies' => [], 'count' => 0]);
}
- $jsst_uids_str = implode(',', array_map('intval', $jsst_uids)); // Ensure integers
+ $jsst_uids_str = implode(',', array_map('absint', $jsst_uids)); // Ensure integers
$jsst_query = "
SELECT r.*
--- a/js-support-ticket/modules/ticket/model.php
+++ b/js-support-ticket/modules/ticket/model.php
@@ -3015,14 +3015,15 @@
die('Security check Failed');
}
- $jsst_id = JSSTrequest::getVar('ticketId');
- $jsst_subject = JSSTrequest::getVar('ticketSubject');
+ // Explicitly cast to integer to kill SQL Injection payloads
+ $jsst_id = absint(JSSTrequest::getVar('ticketId'));
+ $jsst_subject = sanitize_text_field(JSSTrequest::getVar('ticketSubject'));
$jsst_agentquery = "";
if (in_array('agent', jssupportticket::$_active_addons) && JSSTincluder::getJSModel('agent')->isUserStaff()) {
$jsst_allowed = JSSTincluder::getJSModel('userpermissions')->checkPermissionGrantedForTask('Limit AI Replies to Agent-Assigned Tickets');
if ($jsst_allowed) {
- $jsst_staffid = JSSTincluder::getJSModel('agent')->getStaffId(JSSTincluder::getObjectClass('user')->uid());
+ $jsst_staffid = absint(JSSTincluder::getJSModel('agent')->getStaffId(JSSTincluder::getObjectClass('user')->uid()));
$jsst_agentquery = " AND (t.staffid = " . esc_sql($jsst_staffid) . " OR t.departmentid IN (
SELECT dept.departmentid
FROM `" . jssupportticket::$_db->prefix . "js_ticket_acl_user_access_departments` AS dept
@@ -3038,6 +3039,9 @@
FROM `" . jssupportticket::$_db->prefix . "js_ticket_tickets` AS ticket
WHERE ticket.id = " . esc_sql($jsst_id);
$jsst_ticket_data = jssupportticket::$_db->get_row($jsst_query);
+
+ if (!$jsst_ticket_data) return json_encode([]);
+
$jsst_message = wp_strip_all_tags($jsst_ticket_data->message);
// Break the subject and message into words for partial matching