Complianz – GDPR/CCPA Cookie Consent WordPress plugin banner

CVE-2026-4019: Unauthenticated Private Post Content Disclosure In Complianz Plugin

CVE-2026-4019 is a CVSS 5.3 Medium missing authorization vulnerability in the Complianz – GDPR/CCPA Cookie Consent WordPress plugin. Unauthenticated attackers can read private, draft, or unpublished post content through a publicly accessible REST API endpoint.

Vulnerability Summary

FieldValue
Plugin NameComplianz – GDPR/CCPA Cookie Consent
Plugin Slugcomplianz-gdpr
CVE IDCVE-2026-4019
CVSS Score5.3 (Medium)
Vulnerability TypeMissing Authorization to Unauthenticated Private Post Content Disclosure via Consent Area REST Endpoint
Affected Versions<= 7.4.5
Patched Version7.4.6
PublishedApril 28, 2026
ResearcherWesley van de Kamp - Conda Security
Wordfence AdvisoryLink

Description

Unauthenticated attackers can read private, draft, or unpublished WordPress post content through a publicly accessible REST API endpoint. The Complianz plugin registers a route at /wp-json/complianz/v1/consent-area/{post_id}/{block_id} that returns the hidden content of a complianz/consent-area block. The endpoint does not check whether the requesting user has permission to view the post. Because of this, anyone on the internet can retrieve sensitive content that site owners intended to hide behind consent requirements.

In practice, the consent-area block is designed to show content only after a visitor accepts a specific cookie category. Site owners often place marketing scripts, embedded media, or other restricted content inside it. However, the REST endpoint bypasses this protection entirely for posts that are not publicly visible.


Technical Analysis

Vulnerable Code Path

The cmplz_documents_rest_route() function in rest-api/rest-api.php registers the vulnerable endpoint on line 51:

register_rest_route( 'complianz/v1', 'consent-area/(?P<post_id>'.$id_pattern.')/(?P<block_id>'.$string_pattern.')', array(
    'methods'             => 'GET',
    'callback'            => 'cmplz_rest_consented_content',
    'permission_callback' => '__return_true',
) );

The permission_callback is set to __return_true. This tells WordPress to allow the request without any authentication or authorization check.

The callback function cmplz_rest_consented_content() starts on line 61 of the same file:

function cmplz_rest_consented_content( WP_REST_Request $request ) {
    $post_id = (int) ($request->get_param('post_id'));
    $block_id = sanitize_title($request->get_param('block_id'));
    $post = get_post($post_id);

    if ( !$post ) {
        return '';
    }

    $html = $post->post_content;
    $output = '';
    if ( has_block('complianz/consent-area', $html)) {
        $blocks = parse_blocks($post->post_content);
        foreach($blocks as $block){
            if ($block['blockName']==='complianz/consent-area' && $block['attrs']['blockId']===$block_id){
                $output = $block['attrs']['consentedContent'];
                break;
            }
        }
    }
    // ... shortcode handling omitted for brevity

    $output = do_shortcode($output);
    return $output;
}

The function retrieves the post with get_post($post_id). It then parses the post content for a complianz/consent-area block. When it finds a matching block, it returns the consentedContent attribute directly. This attribute stores the HTML content that the site owner wants to hide until the visitor gives consent.

Root Cause

The function does not verify the post’s post_status or the current user’s permission to read the post before returning the block content.

Why Existing Controls Failed

The REST route relies entirely on __return_true as its permission callback. WordPress never runs any capability check. Because of this, the endpoint skips WordPress’s built-in post visibility protections. The get_post() function itself does not filter by status or user permissions — it fetches the post from the database.

Attack Impact

In the worst case, an attacker can read the full consentedContent of consent-area blocks from any post on the site. This includes private posts, drafts, and pending reviews. It may also expose embedded media, marketing tracking scripts, contact forms, or other content that the site owner assumed was protected.


Proof of Concept

Disclaimer: This PoC is provided for educational and defensive security research purposes only.

Prerequisites

Step-by-Step Reproduction

Step 1: Identify a target post and block ID

Create a private post and add a Complianz consent-area block. Note the post ID from the WordPress editor URL. Inspect the block attributes or the front-end HTML to find the data-block_id value. For example, the block might render as:

<div class="cmplz-consent-area cmplz-placeholder" data-post_id="42" data-block_id="my-block" ...>

In this example:

Step 2: Send an unauthenticated request to the REST endpoint

Run the following curl command from any machine with network access to the WordPress site:

curl -s "https://example.com/wp-json/complianz/v1/consent-area/42/my-block"

Replace example.com with the target domain, 42 with the target post ID, and my-block with the target block ID.

Expected Result

The server returns the raw HTML stored in the block’s consentedContent attribute. The server returns this content even though the post is private and the request is unauthenticated.

Verification

Confirm the exploit succeeded by checking that:


Patch Analysis

What Changed

Fix Explanation

To close the gap, the patch checks the post’s visibility before returning any content. It adds these lines immediately after the existing $post = get_post($post_id); check:

if ( 'publish' !== $post->post_status && ! current_user_can( 'read_post', $post->ID ) ) {
    return new WP_Error( 'rest_forbidden', '', array( 'status' => 403 ) );
}

This check works in two stages. First, it allows the request if the post status is publish. Second, if the post is not published, it checks whether the current user has the read_post capability for that specific post. Unauthenticated visitors do not have this capability for private or draft posts. As a result, the endpoint now returns a 403 Forbidden error for any post that the user is not allowed to read.

The permission callback on the route itself remains __return_true. This is acceptable. The authorization logic has moved into the callback function, where it can evaluate the specific post being requested.

Code Diff (Key Changes)

 function cmplz_rest_consented_content( WP_REST_Request $request ) {
-    $post_id = (int) ($request->get_param('post_id'));
-    $block_id = sanitize_title($request->get_param('block_id'));
-    $post = get_post($post_id);
+    $post_id  = (int) ( $request->get_param( 'post_id' ) );
+    $block_id = sanitize_title( $request->get_param( 'block_id' ) );
+    $post     = get_post( $post_id );
 
-    if ( !$post ) {
+    if ( ! $post ) {
         return '';
     }
 
-    $html = $post->post_content;
+    if ( 'publish' !== $post->post_status && ! current_user_can( 'read_post', $post->ID ) ) {
+        return new WP_Error( 'rest_forbidden', '', array( 'status' => 403 ) );
+    }
+
+    $html   = $post->post_content;
     $output = '';
-    if ( has_block('complianz/consent-area', $html)) {
-        $blocks = parse_blocks($post->post_content);
-        foreach($blocks as $block){
-            if ($block['blockName']==='complianz/consent-area' && $block['attrs']['blockId']===$block_id){
+    if ( has_block( 'complianz/consent-area', $html ) ) {
+        $blocks = parse_blocks( $post->post_content );
+        foreach ( $blocks as $block ) {
+            if ( 'complianz/consent-area' === $block['blockName'] && $block['attrs']['blockId'] === $block_id ) {
                 $output = $block['attrs']['consentedContent'];
                 break;
             }
         }
-    } else if ( strpos($html, '[cmplz-consent-area')!==false ) {
+    } elseif ( strpos( $html, '[cmplz-consent-area' ) !== false ) {

Timeline

DateEvent
April 28, 2026Vulnerability publicly disclosed
April 28, 2026Patched version 7.4.6 released

Remediation

Update the complianz-gdpr plugin to version 7.4.6 or later.


References

  1. Wordfence Advisory
  2. CVE-2026-4019 Record
  3. Vulnerable code on GitHub (rest-api.php line 61)
  4. Plugins Trac - vulnerable version 7.4.4.2 rest-api.php line 54
  5. Plugins Trac - vulnerable version 7.4.4.2 rest-api.php line 61
  6. Plugins Trac - changeset 3508713 (patch)
  7. Plugins Trac - diff between 7.4.5 and 7.4.6

Frequently Asked Questions

What is CVE-2026-4019?

CVE-2026-4019 is a CVSS 5.3 Medium missing authorization vulnerability in the Complianz plugin that allows unauthenticated attackers to read private, draft, or unpublished WordPress post content through a publicly accessible REST API endpoint.

Which versions of Complianz – GDPR/CCPA Cookie Consent are affected by CVE-2026-4019?

All versions of the Complianz plugin up to and including 7.4.5 are affected. Version 7.4.6 contains the fix and is safe to use.

What can an attacker do with CVE-2026-4019?

An attacker can send a simple HTTP request to a REST endpoint and retrieve the hidden content of any consent-area block, even on private or draft posts. This may expose embedded media, marketing scripts, contact forms, or other content the site owner intended to protect.

Does an attacker need to be logged in to exploit CVE-2026-4019?

No. The vulnerable endpoint has no authentication requirement. Any visitor on the internet can send the request and read the protected content without any account or credentials.

How do I fix CVE-2026-4019 in Complianz – GDPR/CCPA Cookie Consent?

Update the Complianz plugin to version 7.4.6 or later. You can do this from the WordPress admin dashboard under Plugins, or by downloading the latest version from the WordPress plugin directory.

Has Complianz – GDPR/CCPA Cookie Consent been patched for CVE-2026-4019?

Yes. The developers released version 7.4.6 on April 28, 2026, which adds a post-status and capability check to the vulnerable REST endpoint before returning any content.

If you found this post helpful, consider buying me a coffee. It keeps me writing!

Buy Me A Coffee