Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : June 25, 2026

CVE-2026-54844: CheckView – Form & Checkout Testing <= 2.1.0 Missing Authorization PoC, Patch Analysis & Rule

Plugin checkview
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 2.1.0
Patched Version 2.2.0
Disclosed June 18, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-54844:

This vulnerability affects the CheckView – Form & Checkout Testing plugin for WordPress versions up to and including 2.1.0. The plugin lacks a proper authorization check in its request validation logic. This allows unauthenticated attackers to bypass access controls and trigger privileged actions. The vulnerability has a CVSS score of 5.3 (medium severity).

The root cause lies in the `checkview_bot_check()` function within `checkview/includes/class-checkview.php`. In the vulnerable version, the function at line 195 checks `$test_type && $ip_verified` to authorize requests. The `$ip_verified` check relies on an IP allowlist and environment type checks. This IP-based verification is easily bypassable by spoofing forwarding headers or using a proxy. The check does not enforce any cryptographic proof of identity. The exploit requires no authentication. The attacker sends a crafted HTTP request to any WordPress endpoint that triggers the `checkview_bot_check()` function. The attacker can set the `checkview_test_id` parameter and manipulate their IP or use forwarding headers to match the allowlist if they know a whitelisted IP. Since the IP allowlist is the sole gate, an attacker on a whitelisted IP or able to spoof one gains access.

The patch in version 2.2.0 introduces a cryptographic signature verification mechanism. It adds a new private method `is_request_signed()` that validates an RS256 JWT token passed in the HTTP header `X-CheckView-Signature`. The patch also introduces a filter `checkview_require_signed_request` that defaults to true. In the patched version, the bot check now requires a valid signature (`$verified = $require_signed ? $sig_verified : ( $sig_verified || $ip_verified )`). By default, the IP allowlist alone is no longer sufficient. An attacker without a valid signed token from the CheckView SaaS cannot pass the check.

If exploited, an unauthenticated attacker could cause the plugin to treat their request as a legitimate CheckView bot test. This could lead to logging of sensitive data, triggering of test actions, or bypass of other security measures that rely on the check. The exact impact depends on the actions gated by this check, but it represents a critical authorization bypass that could be chained with other vulnerabilities.

Differential between vulnerable and patched code

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

Code Diff
--- a/checkview/checkview.php
+++ b/checkview/checkview.php
@@ -11,7 +11,7 @@
  * Plugin Name:       CheckView
  * Plugin URI:        https://checkview.io
  * Description:       CheckView is the #1 fully automated solution to test your WordPress forms and detect form problems fast.  Automatically test your WordPress forms to ensure you never miss a lead again.
- * Version:           2.1.0
+ * Version:           2.2.0
  * Author:            CheckView
  * Author URI:        https://checkview.io/
  * License:           GPL-2.0+
@@ -36,7 +36,7 @@
  *
  * @link https://semver.org
  */
-define( 'CHECKVIEW_VERSION', '2.1.0' );
+define( 'CHECKVIEW_VERSION', '2.2.0' );

 if ( ! defined( 'CHECKVIEW_BASE_DIR' ) ) {
 	define( 'CHECKVIEW_BASE_DIR', plugin_basename( __FILE__ ) );
--- a/checkview/includes/class-checkview.php
+++ b/checkview/includes/class-checkview.php
@@ -80,7 +80,7 @@
 		if ( defined( 'CHECKVIEW_VERSION' ) ) {
 			$this->version = CHECKVIEW_VERSION;
 		} else {
-			$this->version = '2.1.0';
+			$this->version = '2.2.0';
 		}
 		$this->plugin_name = 'checkview';

@@ -175,8 +175,30 @@
 		$cv_bot_ip   = checkview_get_api_ip();
 		$is_local    = defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE === 'local';
 		$ip_verified = $is_local || ( is_array( $cv_bot_ip ) && in_array( $visitor_ip, $cv_bot_ip ) );
+
+		// Unforgeable per-request signal: the CheckView test runner attaches a
+		// short-lived signed token (X-CheckView-Signature) that this verifies
+		// cryptographically. Unlike the IP allowlist, it cannot be spoofed via
+		// forwarding headers and survives proxies/CDNs intact.
+		$sig_verified = self::is_request_signed();
+
+		/**
+		 * Filter: when true (default), a valid CheckView request signature is
+		 * REQUIRED and the (spoofable) IP allowlist is no longer sufficient on
+		 * its own.
+		 *
+		 * When false: a request passes if it carries a valid signature OR
+		 * matches the IP allowlist.
+		 *
+		 * @since 2.1.1
+		 *
+		 * @param bool $require_signed Whether a valid signature is mandatory.
+		 */
+		$require_signed = (bool) apply_filters( 'checkview_require_signed_request', true );
+		$verified       = $require_signed ? $sig_verified : ( $sig_verified || $ip_verified );
+
 		$test_type = self::test_type();
-		$result = $test_type && $ip_verified;
+		$result = $test_type && $verified;

 		// Only log during actual tests
 		if ( isset( $_REQUEST['checkview_test_id'] ) ) {
@@ -216,18 +238,61 @@
 			$headers[] = 'RA=[' . ( $ra ?: 'not set' ) . ']';

 			Checkview_Admin_Logs::add( 'ip-logs', sprintf(
-				'Bot check %s [%s]: detected=[%s], %s, ip_ok=[%s], whitelist=[%d IPs]%s',
+				'Bot check %s [%s]: detected=[%s], %s, ip_ok=[%s], sig_ok=[%s], mode=[%s], whitelist=[%d IPs]%s',
 				$result ? 'PASSED' : 'FAILED',
 				$test_id,
 				$safe_visitor_ip ?: 'empty',
 				implode( ', ', $headers ),
 				$ip_verified ? 'yes' : 'no',
+				$sig_verified ? 'yes' : 'no',
+				$require_signed ? 'require-signed' : 'transitional',
 				is_array( $cv_bot_ip ) ? count( $cv_bot_ip ) : 0,
 				$is_local ? ', LOCAL_ENV' : ''
 			) );
 		}

-		return $test_type && $ip_verified;
+		return $result;
+	}
+
+	/**
+	 * Determines whether the current request carries a valid CheckView SaaS
+	 * signature.
+	 *
+	 * The CheckView test runner attaches a short-lived RS256 JWT as the
+	 * `X-CheckView-Signature` header on every same-domain request. The value
+	 * cannot be forged by a client and passes through proxies/CDNs intact, so it
+	 * authenticates the bot even where forwarding headers are spoofable. Reuses
+	 * the exact validation the REST API already trusts (RS256 signature, site
+	 * binding, and expiry) via checkview_validate_jwt_token().
+	 *
+	 * @since 2.1.1
+	 *
+	 * @return bool True when a valid signature is present, false otherwise.
+	 */
+	private static function is_request_signed(): bool {
+		if ( empty( $_SERVER['HTTP_X_CHECKVIEW_SIGNATURE'] ) ) {
+			return false;
+		}
+
+		if ( ! function_exists( 'checkview_validate_jwt_token' ) ) {
+			return false;
+		}
+
+		$header = trim( wp_unslash( $_SERVER['HTTP_X_CHECKVIEW_SIGNATURE'] ) );
+		if ( '' === $header ) {
+			return false;
+		}
+
+		// checkview_validate_jwt_token() expects a "Bearer <jwt>" string.
+		if ( 0 !== strpos( $header, 'Bearer ' ) ) {
+			$header = 'Bearer ' . $header;
+		}
+
+		$nonce = checkview_validate_jwt_token( $header );
+
+		// A non-empty string (the token's nonce) means the signature validated;
+		// a WP_Error, false, or empty string means it did not.
+		return is_string( $nonce ) && '' !== $nonce;
 	}

 	/**

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