PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.9.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.9.0
2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 All 50 releases
← All changes | includes/api/class-social-media-endpoint.php +20 -60 1.32.0 → 2.9.0 View file →
@@ -15,13 +15,16 @@
15 15
16 16 namespace ThinkRank\API;
17 17
18 18 use ThinkRank\SEO\Social_Meta_Manager;
19 +use ThinkRank\API\Traits\Context_Authorization;
19 20 use WP_REST_Controller;
20 21 use WP_REST_Request;
21 22 use WP_REST_Response;
22 23 use WP_Error;
23 24
25 +require_once THINKRANK_PLUGIN_DIR . 'includes/api/traits/trait-context-authorization.php';
26 +
24 27 // Prevent direct access
25 28 if (!defined('ABSPATH')) {
26 29 exit;
27 30 }
@@ -36,8 +39,10 @@
36 39 * @since 1.0.0
37 40 */
38 41 class Social_Media_Endpoint extends WP_REST_Controller {
39 42
43 + use Context_Authorization;
44 +
40 45 /**
41 46 * Social Meta Manager instance
42 47 *
43 48 * @since 1.0.0
@@ -83,9 +88,10 @@
83 88 [
84 89 [
85 90 'methods' => 'GET',
86 91 'callback' => [$this, 'get_settings'],
87 - 'permission_callback' => [$this, 'check_read_permissions']
92 + 'permission_callback' => [$this, 'check_read_permissions'],
93 + 'args' => $this->get_context_route_args()
88 94 ],
89 95 [
90 96 'methods' => 'POST',
91 97 'callback' => [$this, 'update_settings'],
@@ -191,14 +197,20 @@
191 197 *
192 198 * @since 1.0.0
193 199 *
194 200 * @param WP_REST_Request $request Request object
195 - * @return WP_REST_Response Response object
201 + * @return WP_REST_Response|WP_Error Response object, or the context error
196 202 */
197 - public function get_settings(WP_REST_Request $request): WP_REST_Response {
203 + public function get_settings(WP_REST_Request $request) {
198 204 try {
199 - $context_type = $request->get_param('context_type') ?? 'site';
200 - $context_id = $request->get_param('context_id');
205 + // SECURITY: the settings this returns carry the object's social
206 + // title, description and image. update_settings() already authorises
207 + // the object; the read has to as well (#385).
208 + $context = $this->resolve_request_context($request);
209 + if (is_wp_error($context)) {
210 + return $context;
211 + }
212 + [$context_type, $context_id] = $context;
201 213
202 214 // Get settings from Social Meta Manager
203 215 $settings = $this->social_manager->get_settings($context_type, $context_id);
204 216
@@ -324,9 +336,11 @@
324 336 * @return WP_REST_Response|WP_Error Response object
325 337 */
326 338 public function validate_settings(WP_REST_Request $request) {
327 339 try {
328 - $settings = $request->get_param('settings') ?? [];
340 + // `settings` is registered required, so REST rejects the request
341 + // before this runs — the old `?? []` default was unreachable.
342 + $settings = $request->get_param('settings');
329 343 $context_type = $request->get_param('context_type') ?? 'site';
330 344 $validation_context = $request->get_param('validation_context') ?? 'all';
331 345
332 346 // Validate settings using the enhanced Social Meta Manager with tab-specific context
@@ -596,62 +610,8 @@
596 610 * @return bool Permission status
597 611 */
598 612 public function check_manage_permissions(): bool {
599 613 return \ThinkRank\Core\Capability_Manager::current_user_can('thinkrank_social_media');
600 - }
601 -
602 - /**
603 - * Validate context type and ID, and the caller's access to that object.
604 - *
605 - * @since 1.0.0
606 - *
607 - * @param string $context_type Context type
608 - * @param int|null $context_id Context ID
609 - * @return true|WP_Error True when the caller may use this context, WP_Error
610 - * otherwise (400 for a shape error, 403 for authorization).
611 - */
612 - private function validate_context(string $context_type, ?int $context_id) {
613 - $valid_types = ['site', 'post', 'page', 'product'];
614 -
615 - $invalid = new WP_Error(
616 - 'invalid_context',
617 - 'Invalid context type or ID provided',
618 - ['status' => 400]
619 - );
620 -
621 - if (!in_array($context_type, $valid_types, true)) {
622 - return $invalid;
623 - }
624 -
625 - if ($context_type !== 'site' && (!$context_id || $context_id <= 0)) {
626 - return $invalid;
627 - }
628 -
629 - if ($context_id && !get_post($context_id)) {
630 - return $invalid;
631 - }
632 -
633 - // SECURITY: everything above establishes that the context *exists*, not
634 - // that this caller may see it. `edit_post` is a meta capability, so
635 - // map_meta_cap() resolves authorship, published state and
636 - // edit_others_posts for this specific post — the same check the write
637 - // paths in this class already make, and the one schema
638 - // validate_context() makes on its own context routes. Without it a
639 - // delegated Social Media user can walk context_id and read titles,
640 - // descriptions, authors and dates for drafts, pending posts and other
641 - // authors' content (#366).
642 - //
643 - // Site context is deliberately left to the route's capability gate, so
644 - // a delegated Social Media user can still read site-level social meta.
645 - if ($context_type !== 'site' && !current_user_can('edit_post', $context_id)) {
646 - return new WP_Error(
647 - 'rest_forbidden',
648 - 'You are not allowed to access this content.',
649 - ['status' => 403]
650 - );
651 - }
652 -
653 - return true;
654 614 }
655 615
656 616 /**
657 617 * Get arguments for preview endpoint