Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/latepoint/latepoint.php
+++ b/latepoint/latepoint.php
@@ -2,7 +2,7 @@
/**
* Plugin Name: LatePoint
* Description: Appointment Scheduling Software for WordPress
- * Version: 5.4.1
+ * Version: 5.4.2
* Author: LatePoint
* Author URI: https://latepoint.com
* Plugin URI: https://latepoint.com
@@ -29,7 +29,7 @@
* LatePoint version.
*
*/
- public $version = '5.4.1';
+ public $version = '5.4.2';
public $db_version = '2.3.0';
@@ -905,7 +905,7 @@
include_once LATEPOINT_ABSPATH . 'lib/mailers/customer_mailer.php';
// ABILITIES (WordPress 6.9+ Abilities API)
- if ( function_exists( 'wp_register_ability' ) ) {
+ if ( apply_filters( 'latepoint_enable_abilities', false ) && function_exists( 'wp_register_ability' ) ) {
include_once LATEPOINT_ABSPATH . 'lib/abilities/class-latepoint-abilities.php';
}
--- a/latepoint/lib/abilities/customers/connect-customer-to-wp-user.php
+++ b/latepoint/lib/abilities/customers/connect-customer-to-wp-user.php
@@ -42,11 +42,23 @@
return new WP_Error( 'not_found', __( 'Customer not found.', 'latepoint' ), [ 'status' => 404 ] );
}
- $wp_user_id = (int) $args['wp_user_id'];
- if ( ! get_userdata( $wp_user_id ) ) {
+ $wp_user_id = (int) $args['wp_user_id'];
+ $target_user = get_userdata( $wp_user_id );
+ if ( ! $target_user ) {
return new WP_Error( 'wp_user_not_found', __( 'WordPress user not found.', 'latepoint' ), [ 'status' => 404 ] );
}
+ // Only allow linking to non-privileged WP accounts using an allowlist of roles.
+ $allowed_roles = [ LATEPOINT_WP_CUSTOMER_ROLE, 'subscriber', 'customer' ];
+ $user_roles = (array) $target_user->roles;
+ if ( empty( $user_roles ) || ! empty( array_diff( $user_roles, $allowed_roles ) ) ) {
+ return new WP_Error(
+ 'privileged_user',
+ __( 'Cannot link a customer to a privileged WordPress account.', 'latepoint' ),
+ [ 'status' => 403 ]
+ );
+ }
+
$customer->wordpress_user_id = $wp_user_id;
if ( ! $customer->save() ) {
return new WP_Error(
--- a/latepoint/lib/controllers/invoices_controller.php
+++ b/latepoint/lib/controllers/invoices_controller.php
@@ -124,7 +124,7 @@
[
'status' => $status,
'message' => $message,
- ]
+ ]
);
}
@@ -163,7 +163,7 @@
[
'status' => LATEPOINT_STATUS_SUCCESS,
'message' => OsInvoicesHelper::generate_invoice_tile_on_order_edit_form( $invoice ),
- ]
+ ]
);
}
@@ -178,7 +178,7 @@
[
'status' => LATEPOINT_STATUS_ERROR,
'message' => $invoice_params->get_error_message(),
- ]
+ ]
);
return;
@@ -211,14 +211,14 @@
[
'status' => LATEPOINT_STATUS_SUCCESS,
'message' => $response_html,
- ]
+ ]
);
} else {
$this->send_json(
[
'status' => LATEPOINT_STATUS_ERROR,
'message' => __( 'Error: ', 'latepoint' ) . $invoice->get_error_messages(),
- ]
+ ]
);
}
}
@@ -257,7 +257,7 @@
[
'status' => $status,
'message' => $response_html,
- ]
+ ]
);
}
}
@@ -293,7 +293,7 @@
'order' => $order,
'customer' => $customer,
'invoice' => $invoice,
- ]
+ ]
);
$subject = OsReplacerHelper::replace_all_vars(
$subject,
@@ -301,7 +301,7 @@
'order' => $order,
'customer' => $customer,
'invoice' => $invoice,
- ]
+ ]
);
$content = OsReplacerHelper::replace_all_vars(
$content,
@@ -309,7 +309,7 @@
'order' => $order,
'customer' => $customer,
'invoice' => $invoice,
- ]
+ ]
);
if ( OsUtilHelper::is_valid_email( $to ) ) {
$mailer = new OsMailer();
@@ -319,7 +319,7 @@
$this->vars['success'] = __( 'Invoice email sent', 'latepoint' );
} else {
$errors[] = __( 'Please enter a valid email address.', 'latepoint' );
- }
+ }
}
$this->vars['errors'] = $errors;
@@ -349,7 +349,6 @@
$order = $invoice->get_order();
// find an existing transaction intent for this invoice
-
$transaction_intent = new OsTransactionIntentModel();
$transaction_intent = $transaction_intent->where(
[
@@ -359,7 +358,7 @@
LATEPOINT_TRANSACTION_INTENT_STATUS_CONVERTED,
],
'invoice_id' => $invoice->id,
- ]
+ ]
)->set_limit( 1 )->get_results_as_models();
if ( empty( $transaction_intent ) ) {
$transaction_intent = new OsTransactionIntentModel();
@@ -492,7 +491,7 @@
[
'status' => LATEPOINT_STATUS_SUCCESS,
'message' => $response_html,
- ]
+ ]
);
} else {
$this->vars['in_lightbox'] = false;
@@ -532,7 +531,7 @@
[
'status' => $status,
'message' => $response_html,
- ]
+ ]
);
}
}
--- a/latepoint/lib/helpers/payments_helper.php
+++ b/latepoint/lib/helpers/payments_helper.php
@@ -329,6 +329,16 @@
*/
$payment_processing_result = apply_filters( 'latepoint_process_payment_for_order_intent', $payment_processing_result, $order_intent );
if ( $payment_processing_result && $payment_processing_result['status'] == LATEPOINT_STATUS_SUCCESS ) {
+ $existing_transaction = ( new OsTransactionModel() )->where(
+ [
+ 'token' => $payment_processing_result['charge_id'],
+ 'status' => LATEPOINT_TRANSACTION_STATUS_SUCCEEDED,
+ ]
+ )->set_limit( 1 )->get_results_as_models();
+ if ( ! empty( $existing_transaction ) ) {
+ OsDebugHelper::log( 'Duplicate payment token: ' . $payment_processing_result['charge_id'], 'payment_security_error' );
+ return false;
+ }
$transaction = new OsTransactionModel();
$transaction->token = $payment_processing_result['charge_id'];
$transaction->payment_method = $order_intent->get_payment_data_value( 'method' );
@@ -365,6 +375,16 @@
*/
$payment_processing_result = apply_filters( 'latepoint_process_payment_for_transaction_intent', $payment_processing_result, $transaction_intent );
if ( $payment_processing_result && $payment_processing_result['status'] == LATEPOINT_STATUS_SUCCESS ) {
+ $existing_transaction = ( new OsTransactionModel() )->where(
+ [
+ 'token' => $payment_processing_result['charge_id'],
+ 'status' => LATEPOINT_TRANSACTION_STATUS_SUCCEEDED,
+ ]
+ )->set_limit( 1 )->get_results_as_models();
+ if ( ! empty( $existing_transaction ) ) {
+ OsDebugHelper::log( 'Duplicate payment token: ' . $payment_processing_result['charge_id'], 'payment_security_error' );
+ return false;
+ }
$transaction = new OsTransactionModel();
$transaction->token = $payment_processing_result['charge_id'];
$transaction->payment_method = $transaction_intent->get_payment_data_value( 'method' );
--- a/latepoint/lib/helpers/stripe_connect_helper.php
+++ b/latepoint/lib/helpers/stripe_connect_helper.php
@@ -434,8 +434,8 @@
$charges_enabled = OsSettingsHelper::is_on( OsSettingsHelper::append_payment_env_key( 'stripe_connect_charges_enabled', $env ) );
$disconnect_link = '<a class="payment-processor-disconnect-link" href="#"
data-os-pass-response="yes"
- data-os-pass-this="yes"
- data-os-before-after="none"
+ data-os-pass-this="yes"
+ data-os-before-after="none"
data-os-after-call="latepointStripeConnectAdmin.reload_connect_status_wrapper"
data-os-params="' . OsUtilHelper::build_os_params( [ 'env' => $env ] ) . '"
data-os-action="' . OsRouterHelper::build_route_name( 'stripe_connect', 'disconnect_connect_account' ) . '"
@@ -456,7 +456,7 @@
$html .= '<div>' . $stripe_connect_account_id . '</div>';
$html .= $disconnect_link;
$html .= '</div>';
- }
+ }
} else {
$html .= '<a data-env="' . $env . '" data-route-name="' . OsRouterHelper::build_route_name( 'stripe_connect', 'start_connect_process' ) . '" href="#" class="payment-start-connecting"><span>' . __( 'Start Connecting', 'latepoint' ) . '</span><i class="latepoint-icon latepoint-icon-arrow-right"></i></a>';
}
@@ -726,7 +726,7 @@
[
'payment_intent_options' => $options,
'customer_data' => $customer_data,
- ]
+ ]
);
if ( empty( $result['data'] ) ) {
// translators: %s is the payment error
@@ -771,7 +771,7 @@
[
'payment_intent_options' => $options,
'customer_data' => $customer_data,
- ]
+ ]
);
if ( empty( $result['data'] ) ) {
// translators: %s is the payment error
@@ -802,4 +802,4 @@
}
return $result;
}
-}
No newline at end of file
+}