--- a/wp-crm-system/includes/gdpr-export-contact.php
+++ b/wp-crm-system/includes/gdpr-export-contact.php
@@ -26,7 +26,32 @@
}
public function get_cpt_post_ids(){
- $ids = array( $_GET['contact_id'] );
+ // Security: Validate and sanitize contact_id
+ if ( ! isset( $_GET['contact_id'] ) || empty( $_GET['contact_id'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Contact ID is required.', 'wp-crm-system' ) );
+ }
+
+ $contact_id = absint( $_GET['contact_id'] );
+
+ // Security: Verify contact exists and is correct post type
+ if ( 0 === $contact_id || 'wpcrm-contact' !== get_post_type( $contact_id ) ) {
+ wp_die( esc_html__( 'Invalid contact ID.', 'wp-crm-system' ) );
+ }
+
+ // Security: Validate GDPR secret token to prevent unauthorized access
+ if ( ! isset( $_GET['secret'] ) || empty( $_GET['secret'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Secret token is required.', 'wp-crm-system' ) );
+ }
+
+ $secret = sanitize_text_field( $_GET['secret'] );
+ $contact_secret = get_post_meta( $contact_id, '_wpcrm_system_gdpr_secret', true );
+
+ // Security: Verify secret token matches the contact's stored secret
+ if ( empty( $contact_secret ) || $secret !== $contact_secret ) {
+ wp_die( esc_html__( 'Invalid request. Secret token does not match.', 'wp-crm-system' ) );
+ }
+
+ $ids = array( $contact_id );
return $ids;
}
--- a/wp-crm-system/includes/gdpr-shortcode.php
+++ b/wp-crm-system/includes/gdpr-shortcode.php
@@ -382,6 +382,31 @@
function wp_crm_system_gdpr_export_contacts(){
if ( isset( $_POST[ 'wpcrm_system_gdpr_export_contact_nonce' ] ) ) {
if( wp_verify_nonce( $_POST[ 'wpcrm_system_gdpr_export_contact_nonce' ], 'wpcrm-system-gdpr-export-contact-nonce' ) ) {
+ // Security: Validate contact_id and secret token
+ if ( ! isset( $_GET['contact_id'] ) || empty( $_GET['contact_id'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Contact ID is required.', 'wp-crm-system' ) );
+ }
+
+ $contact_id = absint( $_GET['contact_id'] );
+
+ // Security: Verify contact exists and is correct post type
+ if ( 0 === $contact_id || 'wpcrm-contact' !== get_post_type( $contact_id ) ) {
+ wp_die( esc_html__( 'Invalid contact ID.', 'wp-crm-system' ) );
+ }
+
+ // Security: Validate GDPR secret token to prevent unauthorized access
+ if ( ! isset( $_GET['secret'] ) || empty( $_GET['secret'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Secret token is required.', 'wp-crm-system' ) );
+ }
+
+ $secret = sanitize_text_field( $_GET['secret'] );
+ $contact_secret = get_post_meta( $contact_id, '_wpcrm_system_gdpr_secret', true );
+
+ // Security: Verify secret token matches the contact's stored secret
+ if ( empty( $contact_secret ) || $secret !== $contact_secret ) {
+ wp_die( esc_html__( 'Invalid request. Secret token does not match.', 'wp-crm-system' ) );
+ }
+
require_once WP_CRM_SYSTEM_PLUGIN_DIR_PATH . '/includes/class-export.php';
require_once WP_CRM_SYSTEM_PLUGIN_DIR_PATH . '/includes/gdpr-export-contact.php';
@@ -397,49 +422,77 @@
$message = '';
if ( isset( $_POST[ 'wpcrm_system_gdpr_delete_contact_nonce' ] ) ) {
if( wp_verify_nonce( $_POST[ 'wpcrm_system_gdpr_delete_contact_nonce' ], 'wpcrm-system-gdpr-delete-contact-nonce' ) ) {
- if ( $_GET['contact_id'] != $_POST['wpcrm_system_gdpr_contact_id'] || 0 == intval( $_POST['wpcrm_system_gdpr_contact_id'] ) ){
- $message = __( 'Invalid request. Please try again.', 'wp-crm-system' );
- } else {
- $contact_delete = array(
- 'ID' => sanitize_text_field( trim( $_POST['wpcrm_system_gdpr_contact_id'] ) ),
- 'post_status' => 'gdpr_deletion'
- );
-
- $post_id = wp_update_post( $contact_delete );
-
- if ( is_wp_error( $post_id ) ){
- $errors = $post_id->get_error_messages();
- foreach ( $errors as $error ){
- $message .= $error . '<br />';
- }
- } else {
- $message = __( 'Your data was successfully marked for deletion.', 'wp-crm-system' );
+ // Security: Validate contact_id from GET and POST
+ if ( ! isset( $_GET['contact_id'] ) || ! isset( $_POST['wpcrm_system_gdpr_contact_id'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Contact ID is required.', 'wp-crm-system' ) );
+ }
+
+ $get_contact_id = absint( $_GET['contact_id'] );
+ $post_contact_id = absint( $_POST['wpcrm_system_gdpr_contact_id'] );
+
+ // Security: Verify contact IDs match and are valid
+ if ( $get_contact_id !== $post_contact_id || 0 === $post_contact_id ) {
+ wp_die( esc_html__( 'Invalid request. Contact IDs do not match.', 'wp-crm-system' ) );
+ }
+
+ // Security: Verify contact exists and is correct post type
+ if ( 'wpcrm-contact' !== get_post_type( $post_contact_id ) ) {
+ wp_die( esc_html__( 'Invalid contact ID.', 'wp-crm-system' ) );
+ }
+
+ // Security: Validate GDPR secret token to prevent unauthorized access
+ if ( ! isset( $_GET['secret'] ) || empty( $_GET['secret'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Secret token is required.', 'wp-crm-system' ) );
+ }
+
+ $secret = sanitize_text_field( $_GET['secret'] );
+ $contact_secret = get_post_meta( $post_contact_id, '_wpcrm_system_gdpr_secret', true );
+
+ // Security: Verify secret token matches the contact's stored secret
+ if ( empty( $contact_secret ) || $secret !== $contact_secret ) {
+ wp_die( esc_html__( 'Invalid request. Secret token does not match.', 'wp-crm-system' ) );
+ }
+
+ // All security checks passed, proceed with deletion
+ $contact_delete = array(
+ 'ID' => $post_contact_id,
+ 'post_status' => 'gdpr_deletion'
+ );
+
+ $post_id = wp_update_post( $contact_delete );
+
+ if ( is_wp_error( $post_id ) ){
+ $errors = $post_id->get_error_messages();
+ foreach ( $errors as $error ){
+ $message .= $error . '<br />';
}
- /* Send email to site admin notifying of delete request */
- $to = apply_filters( 'wpcrm_system_gdpr_delete_request_email', get_option( 'admin_email' ) );
- $subject = apply_filters( 'wpcrm_system_gdpr_delete_request_subject', __( 'GDPR Delete Request', 'wp-crm-system' ) );
+ } else {
+ $message = __( 'Your data was successfully marked for deletion.', 'wp-crm-system' );
+ }
+ /* Send email to site admin notifying of delete request */
+ $to = apply_filters( 'wpcrm_system_gdpr_delete_request_email', get_option( 'admin_email' ) );
+ $subject = apply_filters( 'wpcrm_system_gdpr_delete_request_subject', __( 'GDPR Delete Request', 'wp-crm-system' ) );
- $contact_name = get_the_title( $contact_delete['ID'] );
- $edit_link = admin_url( 'post.php?post=' . $contact_delete['ID'] . '&action=edit' );
+ $contact_name = get_the_title( $contact_delete['ID'] );
+ $edit_link = admin_url( 'post.php?post=' . $contact_delete['ID'] . '&action=edit' );
- $message = sprintf(
- wp_kses(
- __( 'A contact, %s, has requested that their information be deleted from WP-CRM System. Their record has been marked for deletion, but will not be deleted automatically. This enables you to determine whether or not you are required to retain their data, or delete it from any other system your company uses, such as a mailing list. You can review their record in WP-CRM System by copying and pasting the following link into your browser: %s', 'wp-crm-system' ),
- array( 'a' => array( 'href' => array() ) )
- ),
- $contact_name, esc_url( $edit_link )
- );
+ $message = sprintf(
+ wp_kses(
+ __( 'A contact, %s, has requested that their information be deleted from WP-CRM System. Their record has been marked for deletion, but will not be deleted automatically. This enables you to determine whether or not you are required to retain their data, or delete it from any other system your company uses, such as a mailing list. You can review their record in WP-CRM System by copying and pasting the following link into your browser: %s', 'wp-crm-system' ),
+ array( 'a' => array( 'href' => array() ) )
+ ),
+ $contact_name, esc_url( $edit_link )
+ );
- $message = apply_filters( 'wpcrm_system_gdpr_delete_request_message', $message, $contact_name, $edit_link );
+ $message = apply_filters( 'wpcrm_system_gdpr_delete_request_message', $message, $contact_name, $edit_link );
- $headers = apply_filters( 'wpcrm_system_gdpr_delete_request_headers', '' );
+ $headers = apply_filters( 'wpcrm_system_gdpr_delete_request_headers', '' );
- $attachments = apply_filters( 'wpcrm_system_gdpr_delete_request_attachments', '' );
+ $attachments = apply_filters( 'wpcrm_system_gdpr_delete_request_attachments', '' );
- wp_mail( $to, $subject, $message, $headers, $attachments );
- /* possibly add support for Slack/Zapier add-on to send notification elsewhere */
- }
+ wp_mail( $to, $subject, $message, $headers, $attachments );
+ /* possibly add support for Slack/Zapier add-on to send notification elsewhere */
}
}
return $message;
--- a/wp-crm-system/includes/import-export/import-contacts.php
+++ b/wp-crm-system/includes/import-export/import-contacts.php
@@ -24,6 +24,15 @@
$count_skipped = 0;
$count_added = 0;
$count_updated = 0;
+ // Security: Validate file extension first (cannot be easily spoofed)
+ $allowed_extensions = array( 'csv' );
+ $file_extension = strtolower( pathinfo( $file_name, PATHINFO_EXTENSION ) );
+
+ if ( ! in_array( $file_extension, $allowed_extensions, true ) ) {
+ $errors[] = __( 'File not allowed, please use a CSV file.', 'wp-crm-system' );
+ }
+
+ // Security: Also verify MIME type (defense in depth)
$csv_types = array(
'text/csv',
'text/plain',
@@ -37,8 +46,8 @@
'application/txt',
);
- if( !in_array( $file_type, $csv_types ) ){
- $errors[] = __( 'File not allowed, please use a CSV file.', 'wp-crm-system' );
+ if ( ! in_array( $file_type, $csv_types, true ) ) {
+ $errors[] = __( 'File type not allowed.', 'wp-crm-system' );
}
if( $file_size > wp_crm_system_return_bytes( ini_get( 'upload_max_filesize' ) ) ){
$errors[] = __( 'File size must be less than', 'wp-crm-system' ) . ini_get( 'upload_max_filesize' ) . ' current file size is ' . $file_size;
@@ -209,7 +218,12 @@
*/
global $wpdb;
$prefix_tbl = $wpdb->prefix . 'postmeta';
- $results = $wpdb->get_row( "SELECT * FROM {$prefix_tbl} WHERE `meta_key` = '_wpcrm_contact-email' AND `meta_value`='$email'", OBJECT );
+ // Security: Use prepared statement to prevent SQL injection
+ $results = $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM {$prefix_tbl} WHERE `meta_key` = %s AND `meta_value` = %s",
+ '_wpcrm_contact-email',
+ $email
+ ), OBJECT );
/**
* If and only if we have result
@@ -352,7 +366,11 @@
global $wpdb;
$prefix_tbl = $wpdb->prefix . 'postmeta';
- $results = $wpdb->get_results( "SELECT * FROM {$prefix_tbl} WHERE `meta_value`='$email'", OBJECT );
+ // Security: Use prepared statement to prevent SQL injection
+ $results = $wpdb->get_results( $wpdb->prepare(
+ "SELECT * FROM {$prefix_tbl} WHERE `meta_value` = %s",
+ $email
+ ), OBJECT );
if ( ! $results ) {
$create_contact = true;
--- a/wp-crm-system/includes/import-export/import-plugin-settings.php
+++ b/wp-crm-system/includes/import-export/import-plugin-settings.php
@@ -19,10 +19,29 @@
}
$import_file = $_FILES['import_file']['tmp_name'];
if( empty( $import_file ) ) {
- wp_die( esc_html__( 'Please upload a file to import' ) );
+ wp_die( esc_html__( 'Please upload a file to import', 'wp-crm-system' ) );
}
+
+ // Security: Validate file size before processing (3MB limit)
+ $file_size = filesize( $import_file );
+ $max_file_size = 3145728; // 3MB in bytes
+ if ( $file_size > $max_file_size ) {
+ wp_die( esc_html__( 'File size exceeds maximum allowed size of 3MB.', 'wp-crm-system' ) );
+ }
+
+ // Security: Validate JSON before decoding
+ $json_content = file_get_contents( $import_file );
+ if ( false === $json_content ) {
+ wp_die( esc_html__( 'Error reading import file.', 'wp-crm-system' ) );
+ }
+
+ $json_data = json_decode( $json_content, true );
+ if ( json_last_error() !== JSON_ERROR_NONE ) {
+ wp_die( esc_html__( 'Invalid JSON file. Please check the file format.', 'wp-crm-system' ) );
+ }
+
// Retrieve the settings from the file and convert the json object to an array.
- $settings = (array) json_decode( file_get_contents( $import_file ) );
+ $settings = (array) $json_data;
foreach( $settings as $option_name => $option_value ){
update_option( $option_name, wpcrm_safe_unserialize( $option_value ) );
}
--- a/wp-crm-system/includes/legacy/gdpr-shortcode.php
+++ b/wp-crm-system/includes/legacy/gdpr-shortcode.php
@@ -6,17 +6,21 @@
add_shortcode( 'wpcrm_system_gdpr', 'wpcrm_system_gdpr_check' );
function wpcrm_system_gdpr_check( $atts ){
- if( !$_GET || !$_GET['contact_id'] || !$_GET['secret'] )
+ // Security: Validate and sanitize inputs
+ if( ! isset( $_GET['contact_id'] ) || ! isset( $_GET['secret'] ) || empty( $_GET['contact_id'] ) || empty( $_GET['secret'] ) )
return __( 'Error: Incorrect URL given. Please request a valid URL.', 'wp-crm-system' ); // Possibly add an error message of some sort here.
- $id = $_GET['contact_id'];
- if ( 'wpcrm-contact' != get_post_type( $id ) )
+ $id = absint( $_GET['contact_id'] );
+
+ // Security: Verify contact ID is valid and is correct post type
+ if ( 0 === $id || 'wpcrm-contact' != get_post_type( $id ) )
return __( 'Error: Incorrect contact URL given. Please request a valid contact URL.'); // Possibly add an error message indicating that the contact ID is not a valid WP-CRM System contact ID.
- $secret = $_GET['secret'];
- $contact_secret = get_post_meta( $id, '_wpcrm_system_gdpr_secret', true );
+ $secret = sanitize_text_field( $_GET['secret'] );
+ $contact_secret = get_post_meta( $id, '_wpcrm_system_gdpr_secret', true );
- if( $secret != $contact_secret )
+ // Security: Verify secret token matches using strict comparison
+ if( empty( $contact_secret ) || $secret !== $contact_secret )
return __( 'Error: URL is incorrect. Please request a valid URL.'); // Possibly add an error message indicating that the secret is incorrect. Might not be a good idea as it leaves open the opportunity to guess the secret.
/*
@@ -390,49 +394,77 @@
$message = '';
if ( isset( $_POST[ 'wpcrm_system_gdpr_delete_contact_nonce' ] ) ) {
if( wp_verify_nonce( $_POST[ 'wpcrm_system_gdpr_delete_contact_nonce' ], 'wpcrm-system-gdpr-delete-contact-nonce' ) ) {
- if ( $_GET['contact_id'] != $_POST['wpcrm_system_gdpr_contact_id'] || 0 == intval( $_POST['wpcrm_system_gdpr_contact_id'] ) ){
- $message = __( 'Invalid request. Please try again.', 'wp-crm-system' );
- } else {
- $contact_delete = array(
- 'ID' => sanitize_text_field( trim( $_POST['wpcrm_system_gdpr_contact_id'] ) ),
- 'post_status' => 'gdpr_deletion'
- );
-
- $post_id = wp_update_post( $contact_delete );
-
- if ( is_wp_error( $post_id ) ){
- $errors = $post_id->get_error_messages();
- foreach ( $errors as $error ){
- $message .= $error . '<br />';
- }
- } else {
- $message = __( 'Your data was successfully marked for deletion.', 'wp-crm-system' );
+ // Security: Validate contact_id from GET and POST
+ if ( ! isset( $_GET['contact_id'] ) || ! isset( $_POST['wpcrm_system_gdpr_contact_id'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Contact ID is required.', 'wp-crm-system' ) );
+ }
+
+ $get_contact_id = absint( $_GET['contact_id'] );
+ $post_contact_id = absint( $_POST['wpcrm_system_gdpr_contact_id'] );
+
+ // Security: Verify contact IDs match and are valid
+ if ( $get_contact_id !== $post_contact_id || 0 === $post_contact_id ) {
+ wp_die( esc_html__( 'Invalid request. Contact IDs do not match.', 'wp-crm-system' ) );
+ }
+
+ // Security: Verify contact exists and is correct post type
+ if ( 'wpcrm-contact' !== get_post_type( $post_contact_id ) ) {
+ wp_die( esc_html__( 'Invalid contact ID.', 'wp-crm-system' ) );
+ }
+
+ // Security: Validate GDPR secret token to prevent unauthorized access
+ if ( ! isset( $_GET['secret'] ) || empty( $_GET['secret'] ) ) {
+ wp_die( esc_html__( 'Invalid request. Secret token is required.', 'wp-crm-system' ) );
+ }
+
+ $secret = sanitize_text_field( $_GET['secret'] );
+ $contact_secret = get_post_meta( $post_contact_id, '_wpcrm_system_gdpr_secret', true );
+
+ // Security: Verify secret token matches the contact's stored secret
+ if ( empty( $contact_secret ) || $secret !== $contact_secret ) {
+ wp_die( esc_html__( 'Invalid request. Secret token does not match.', 'wp-crm-system' ) );
+ }
+
+ // All security checks passed, proceed with deletion
+ $contact_delete = array(
+ 'ID' => $post_contact_id,
+ 'post_status' => 'gdpr_deletion'
+ );
+
+ $post_id = wp_update_post( $contact_delete );
+
+ if ( is_wp_error( $post_id ) ){
+ $errors = $post_id->get_error_messages();
+ foreach ( $errors as $error ){
+ $message .= $error . '<br />';
}
- /* Send email to site admin notifying of delete request */
- $to = apply_filters( 'wpcrm_system_gdpr_delete_request_email', get_option( 'admin_email' ) );
- $subject = apply_filters( 'wpcrm_system_gdpr_delete_request_subject', __( 'GDPR Delete Request', 'wp-crm-system' ) );
+ } else {
+ $message = __( 'Your data was successfully marked for deletion.', 'wp-crm-system' );
+ }
+ /* Send email to site admin notifying of delete request */
+ $to = apply_filters( 'wpcrm_system_gdpr_delete_request_email', get_option( 'admin_email' ) );
+ $subject = apply_filters( 'wpcrm_system_gdpr_delete_request_subject', __( 'GDPR Delete Request', 'wp-crm-system' ) );
- $contact_name = get_the_title( $contact_delete['ID'] );
- $edit_link = admin_url( 'post.php?post=' . $contact_delete['ID'] . '&action=edit' );
+ $contact_name = get_the_title( $contact_delete['ID'] );
+ $edit_link = admin_url( 'post.php?post=' . $contact_delete['ID'] . '&action=edit' );
- $message = sprintf(
- wp_kses(
- __( 'A contact, %s, has requested that their information be deleted from WP-CRM System. Their record has been marked for deletion, but will not be deleted automatically. This enables you to determine whether or not you are required to retain their data, or delete it from any other system your company uses, such as a mailing list. You can review their record in WP-CRM System by copying and pasting the following link into your browser: %s', 'wp-crm-system' ),
- array( 'a' => array( 'href' => array() ) )
- ),
- $contact_name, esc_url( $edit_link )
- );
+ $message = sprintf(
+ wp_kses(
+ __( 'A contact, %s, has requested that their information be deleted from WP-CRM System. Their record has been marked for deletion, but will not be deleted automatically. This enables you to determine whether or not you are required to retain their data, or delete it from any other system your company uses, such as a mailing list. You can review their record in WP-CRM System by copying and pasting the following link into your browser: %s', 'wp-crm-system' ),
+ array( 'a' => array( 'href' => array() ) )
+ ),
+ $contact_name, esc_url( $edit_link )
+ );
- $message = apply_filters( 'wpcrm_system_gdpr_delete_request_message', $message, $contact_name, $edit_link );
+ $message = apply_filters( 'wpcrm_system_gdpr_delete_request_message', $message, $contact_name, $edit_link );
- $headers = apply_filters( 'wpcrm_system_gdpr_delete_request_headers', '' );
+ $headers = apply_filters( 'wpcrm_system_gdpr_delete_request_headers', '' );
- $attachments = apply_filters( 'wpcrm_system_gdpr_delete_request_attachments', '' );
+ $attachments = apply_filters( 'wpcrm_system_gdpr_delete_request_attachments', '' );
- wp_mail( $to, $subject, $message, $headers, $attachments );
- /* possibly add support for Slack/Zapier add-on to send notification elsewhere */
- }
+ wp_mail( $to, $subject, $message, $headers, $attachments );
+ /* possibly add support for Slack/Zapier add-on to send notification elsewhere */
}
}
return $message;
--- a/wp-crm-system/includes/wcs-dashboard-task-list.php
+++ b/wp-crm-system/includes/wcs-dashboard-task-list.php
@@ -169,23 +169,62 @@
'task_list_vars',
array(
'task_list_nonce' => wp_create_nonce( 'task-list-nonce' ),
+ 'task_change_status_nonce' => wp_create_nonce( 'task-change-status-nonce' ),
)
);
}
add_action( 'admin_enqueue_scripts', 'wpcrm_system_dashboard_task_js' );
function wpcrm_system_ajax_task_change_status() {
- $post_id = isset( $_POST['post_id'] ) ? sanitize_text_field( $_POST['post_id'] ) : '';
+ // Verify nonce for security
+ if ( ! isset( $_POST['task_change_status_nonce'] ) ) {
+ wp_send_json_error( array( 'message' => __( 'Security check failed: Nonce not provided', 'wp-crm-system' ) ) );
+ }
+
+ if ( ! wp_verify_nonce( $_POST['task_change_status_nonce'], 'task-change-status-nonce' ) ) {
+ wp_send_json_error( array( 'message' => __( 'Security check failed: Invalid nonce', 'wp-crm-system' ) ) );
+ }
+
+ // Check user capabilities
+ if ( ! current_user_can( 'edit_posts' ) ) {
+ wp_send_json_error( array( 'message' => __( 'Insufficient permissions', 'wp-crm-system' ) ) );
+ }
+
+ // Validate and sanitize post_id
+ $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
+ if ( empty( $post_id ) ) {
+ wp_send_json_error( array( 'message' => __( 'Invalid post ID', 'wp-crm-system' ) ) );
+ }
+
+ // Verify the post exists and is a task
+ $post = get_post( $post_id );
+ if ( ! $post || 'wpcrm-task' !== $post->post_type ) {
+ wp_send_json_error( array( 'message' => __( 'Invalid task post', 'wp-crm-system' ) ) );
+ }
+
+ // Check if user can edit this specific post
+ if ( ! current_user_can( 'edit_post', $post_id ) ) {
+ wp_send_json_error( array( 'message' => __( 'You do not have permission to edit this task', 'wp-crm-system' ) ) );
+ }
+
+ // Validate and sanitize task status
$task_status = isset( $_POST['task_status'] ) ? sanitize_text_field( $_POST['task_status'] ) : '';
- $meta = 'status';
+
+ // Define allowed status values
+ $allowed_statuses = array( 'not-started', 'in-progress', 'complete', 'on-hold' );
+ if ( ! in_array( $task_status, $allowed_statuses, true ) ) {
+ wp_send_json_error( array( 'message' => __( 'Invalid task status', 'wp-crm-system' ) ) );
+ }
+
+ $meta = 'status';
+ // Update the post meta
$update_status = update_post_meta( $post_id, '_wpcrm_task-' . $meta, $task_status );
if ( $update_status ) {
- echo 'success';
+ wp_send_json_success( array( 'message' => __( 'Task status updated successfully', 'wp-crm-system' ) ) );
} else {
- echo 'fail';
+ wp_send_json_error( array( 'message' => __( 'Failed to update task status', 'wp-crm-system' ) ) );
}
- exit;
}
add_action( 'wp_ajax_task_change_status', 'wpcrm_system_ajax_task_change_status' );
--- a/wp-crm-system/includes/wcs-functions.php
+++ b/wp-crm-system/includes/wcs-functions.php
@@ -339,7 +339,7 @@
$next_month = $month + 1;
$next_year = $year;
}
- $view_as_param = isset( $_GET['view-as-id'] ) ? '&view-as-id=' . sanitize_text_field( $_GET['view-as-id'] ) : '';
+ $view_as_param = isset( $_GET['view-as-id'] ) ? '&view-as-id=' . urlencode( sanitize_text_field( $_GET['view-as-id'] ) ) : '';
/* draw table */
$calendar = '<table cellpadding="0" cellspacing="0" class="calendar">';
@@ -940,8 +940,15 @@
*/
add_action( 'wp_ajax_wpcrm_get_email_recipients', 'wpcrm_get_email_recipients' );
function wpcrm_get_email_recipients() {
-
- $recipient = sanitize_text_field( $_REQUEST['recipient'] );
+ // Security: Verify nonce to prevent CSRF attacks
+ check_ajax_referer( 'wpcrm-nonce', 'nonce' );
+
+ // Security: Check user capabilities
+ if ( ! current_user_can( wpcrm_system_get_required_user_role() ) ) {
+ wp_die();
+ }
+
+ $recipient = isset( $_REQUEST['recipient'] ) ? sanitize_text_field( $_REQUEST['recipient'] ) : '';
$contacts = new WP_Query(
array(
@@ -1959,7 +1966,8 @@
add_action( 'admin_init', 'wpcrm_save_duplicate_contact_option' );
function wpcrm_save_duplicate_contact_option() {
if ( isset( $_REQUEST['wpcrm_system_duplicate_contact'] ) ) {
- if ( ! current_user_can( 'manage_options' ) && ! wp_verify_nonce( $_REQUEST['wpcrm-options'], 'update-options' ) ) {
+ // Security: Exit if user lacks permissions OR nonce is invalid (use OR, not AND)
+ if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( $_REQUEST['wpcrm-options'], 'update-options' ) ) {
exit;
}
@@ -1982,7 +1990,8 @@
add_action( 'admin_init', 'wpcrm_save_contact_display_duplicates_option' );
function wpcrm_save_contact_display_duplicates_option() {
if ( isset( $_REQUEST['wpcrm_system_contact_display_duplicates'] ) ) {
- if ( ! current_user_can( 'manage_options' ) && ! wp_verify_nonce( $_REQUEST['wpcrm-options'], 'update-options' ) ) {
+ // Security: Exit if user lacks permissions OR nonce is invalid (use OR, not AND)
+ if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( $_REQUEST['wpcrm-options'], 'update-options' ) ) {
exit;
}
@@ -2106,7 +2115,7 @@
if ( ! empty( $type ) ) {
$query = new WP_Query(
array(
- 's' => sanitize_text_field( $_GET['q'] ),
+ 's' => isset( $_GET['q'] ) ? sanitize_text_field( $_GET['q'] ) : '',
'post_status' => 'publish',
'posts_per_page' => 10,
'post_type' => $type,
@@ -2184,26 +2193,92 @@
add_filter( 'wp_kses_allowed_html', 'wpcrm_sanitize_svg_icon' );
/**
- * Safe unserialize
+ * Safe unserialize - Prevents PHP Object Injection
*
* @since 3.2.9
+ * @param mixed $data Data to unserialize
+ * @return mixed Unserialized data, or original data if unsafe
*/
function wpcrm_safe_unserialize( $data ) {
+ // If not serialized, return as-is
if ( ! is_serialized( $data ) ) {
return $data;
}
+ // Security: Check for object signatures in serialized string before unserializing
+ // This prevents object instantiation during unserialization
+ if ( preg_match( '/[oO]:s*d+:/', $data ) ) {
+ // Contains object serialization, reject it
+ return $data; // Return original data
+ }
+
+ // Security: Check for resource serialization
+ if ( preg_match( '/[rR]:s*d+:/', $data ) ) {
+ // Contains resource serialization, reject it
+ return $data;
+ }
+
+ // Security: Check for nested serialized data that might contain objects
+ // This regex looks for serialized arrays/strings that might contain objects
+ $nested_serialized = preg_match_all( '/s:d+:"[^"]*";/', $data, $matches );
+ if ( $nested_serialized ) {
+ foreach ( $matches[0] as $match ) {
+ // Check if nested serialized data contains objects
+ if ( preg_match( '/[oO]:s*d+:/', $match ) ) {
+ return $data;
+ }
+ }
+ }
+
+ // Now safe to unserialize
$unserialized = maybe_unserialize( $data );
- // Reject objects (potential PHP Object Injection)
+ // Additional security: Double-check for objects after unserialization
+ // This catches any edge cases where objects might have been created
if ( is_object( $unserialized ) ) {
return $data; // Return original data
}
- // Reject resources
+ // Additional security: Reject resources
if ( is_resource( $unserialized ) ) {
return $data;
}
+ // Security: Recursively check arrays for objects
+ if ( is_array( $unserialized ) ) {
+ $checked = wpcrm_safe_unserialize_recursive( $unserialized );
+ if ( false === $checked ) {
+ // Array contains objects, reject it
+ return $data;
+ }
+ return $checked;
+ }
+
return $unserialized;
+}
+
+/**
+ * Recursively check array for objects (helper function for wpcrm_safe_unserialize)
+ *
+ * @since 3.2.9
+ * @param array $array Array to check
+ * @return array|false Cleaned array or false if objects found
+ */
+function wpcrm_safe_unserialize_recursive( $array ) {
+ foreach ( $array as $key => $value ) {
+ if ( is_object( $value ) ) {
+ return false; // Found an object, reject
+ }
+ if ( is_resource( $value ) ) {
+ return false; // Found a resource, reject
+ }
+ if ( is_array( $value ) ) {
+ $result = wpcrm_safe_unserialize_recursive( $value );
+ if ( false === $result ) {
+ return false; // Nested array contains objects
+ }
+ $array[ $key ] = $result;
+ }
+ }
+ return $array;
}
No newline at end of file
--- a/wp-crm-system/includes/wcs-recurring-entries-delete.php
+++ b/wp-crm-system/includes/wcs-recurring-entries-delete.php
@@ -3,11 +3,33 @@
if ( !defined( 'ABSPATH' ) ) {
die( "Sorry, you are not allowed to access this page directly." );
}
+
+// Security: Check user permissions before allowing access to delete page
+if ( ! current_user_can( wpcrm_system_get_required_user_role() ) ) {
+ wp_die( esc_html__( 'You do not have permission to access this page.', 'wp-crm-system' ) );
+}
?>
<div class="wrap">
<h2><?php esc_html_e( 'Delete Entry', 'wp-crm-system' ); ?> - <a href="admin.php?page=wpcrm-settings&tab=recurring&subtab=recurring-entries" class="button-secondary"><?php esc_html_e( 'Cancel - Go Back', 'wp-crm-system' ); ?></a></h2>
<form method="post" action="" class="wp_crm_system_recurring_entries_form">
- <?php $entry = $wpdb->get_row("SELECT * FROM " . $wpcrm_system_recurring_db_name . " WHERE id='" . $_GET['entry_id'] . "';"); ?>
+ <?php
+ // Security: Use prepared statement to prevent SQL injection
+ $entry_id = isset( $_GET['entry_id'] ) ? absint( $_GET['entry_id'] ) : 0;
+ $entry = null;
+ if ( $entry_id > 0 ) {
+ $entry = $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM " . $wpcrm_system_recurring_db_name . " WHERE id = %d",
+ $entry_id
+ ) );
+ }
+
+ // If entry not found or invalid ID, show error and exit
+ if ( ! $entry ) {
+ echo '<div class="error"><p>' . esc_html__( 'Invalid entry ID or entry not found.', 'wp-crm-system' ) . '</p></div>';
+ echo '</div>'; // Close wrap div
+ return;
+ }
+ ?>
<p><?php esc_html_e( 'Recurring entry deletion is permanent and cannot be undone. No further recurring entries will be created for this entry.', 'wp-crm-system' ); ?></p>
<p><?php esc_html_e( 'Are you sure you wish to delete yes this entry?', 'wp-crm-system' ); ?></p>
<p>
--- a/wp-crm-system/wp-crm-system.php
+++ b/wp-crm-system/wp-crm-system.php
@@ -3,7 +3,7 @@
Plugin Name: WP-CRM System – Manage Clients and Projects
Plugin URI: https://www.wp-crm.com
Description: A complete CRM for WordPress
-Version: 3.4.5
+Version: 3.4.6
Author: Premium WordPress Support
Author URI: https://www.wp-crm.com
Text Domain: wp-crm-system
@@ -34,7 +34,7 @@
define( 'WP_CRM_SYSTEM', __FILE__ );
}
if ( ! defined( 'WP_CRM_SYSTEM_VERSION' ) ) {
- define( 'WP_CRM_SYSTEM_VERSION', '3.4.5' );
+ define( 'WP_CRM_SYSTEM_VERSION', '3.4.6' );
}
if( ! defined( 'WP_CRM_SYSTEM_URL' ) ) {
define( 'WP_CRM_SYSTEM_URL', plugins_url( '', __FILE__ ) );