PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | customer-sync-requests/class-metasync-sync-requests.php +16 -7 2.6.25 → 2.7.2 View file →
@@ -87,10 +87,10 @@
87 87 $_throttle = Metasync::get_heartbeat_throttle();
88 88 $last_hb_request_time = $_throttle['last_heart_beat'] ?? 0;
89 89
90 90 # PR3: Throttle depends on state — burst (KEY_PENDING within 30 min) allows 30s; else 5 min
91 - $is_heartbeat = ($context === 'heartbeat') || filter_var($_POST['is_heart_beat'] ?? false, FILTER_VALIDATE_BOOLEAN);
92 - $is_burst = !empty($_POST['is_burst']);
91 + $is_heartbeat = ($context === 'heartbeat') || filter_var($_POST['is_heart_beat'] ?? false, FILTER_VALIDATE_BOOLEAN); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only throttle flags, callers are already authorized
92 + $is_burst = !empty($_POST['is_burst']); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only throttle flags, callers are already authorized
93 93 $heartbeat_state = $metasync_options['general']['heartbeat_state'] ?? '';
94 94 $state_changed_at = (int) ($metasync_options['general']['heartbeat_state_changed_at'] ?? 0);
95 95 $burst_window_end = $state_changed_at + (30 * 60); // 30 min cap
96 96 $in_burst_window = ($heartbeat_state === 'KEY_PENDING' && $burst_window_end > time());
@@ -182,8 +182,12 @@
182 182 'permalink_structure'=>((($current_permalink_structure == '/%post_id%/' || $current_permalink_structure == '') && $current_rewrite_rules == '')?false:true),
183 183 'otto_pixel_uuid' => $general_options['otto_pixel_uuid'] ?? '',
184 184 ];
185 185
186 + if (Metasync_Headless_Config::is_active()) {
187 + $payload['frontend_domain'] = Metasync_Headless_Config::get_frontend_domain();
188 + }
189 +
186 190 # append login auth token to payload
187 191 if(!empty($token)){
188 192 $payload['login_auth_token'] = $token;
189 193 }
@@ -201,9 +205,9 @@
201 205 $response = wp_remote_post($apiUrl, $data);
202 206
203 207 # PERFORMANCE OPTIMIZATION: Handle timeout and connection errors
204 208 if (is_wp_error($response)) {
205 - error_log('MetaSync: Heartbeat sync failed: ' . $response->get_error_message());
209 + error_log('MetaSync: Heartbeat sync failed: ' . $response->get_error_message()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
206 210 $this->saveHeartBeatError('heartbeat', 'Connection error: ' . $response->get_error_message(), array(1), 0);
207 211 return;
208 212 }
209 213
@@ -222,20 +226,25 @@
222 226 # concurrent settings writes that happened during the HTTP window.
223 227 $_sync_updates = [];
224 228
225 229 # PR2: Parse heartbeat response for UUID self-healing and clone detection
230 + # The UUID is only accepted when it is a real UUID: the
231 + # heartbeat response is remote input, and usernames/arbitrary text
232 + # landing in otto_pixel_uuid break OTTO SSR authentication.
226 233 $response_body = json_decode(wp_remote_retrieve_body($response), true);
227 234 $current_uuid = $metasync_options['general']['otto_pixel_uuid'] ?? '';
228 235 if (is_array($response_body) && !empty($response_body['otto_pixel_uuid'])) {
229 236 $response_uuid = sanitize_text_field($response_body['otto_pixel_uuid']);
230 - if (!empty($response_body['uuid_mismatch'])) {
237 + if (!Metasync::is_valid_uuid($response_uuid)) {
238 + error_log('MetaSync: ignoring invalid otto_pixel_uuid from heartbeat response (not a UUID)');
239 + } elseif (!empty($response_body['uuid_mismatch'])) {
231 240 # Domain clone: backend says local UUID is wrong for this domain
232 241 $_sync_updates['otto_pixel_uuid'] = $response_uuid;
233 - error_log('MetaSync: UUID corrected from ' . $current_uuid . ' to ' . $response_uuid . ' (domain clone detected)');
242 + error_log('MetaSync: UUID corrected from ' . $current_uuid . ' to ' . $response_uuid . ' (domain clone detected)'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
234 243 } elseif (empty($current_uuid)) {
235 244 # Self-heal: SSO callback failed but API key was saved; recover UUID from heartbeat
236 245 $_sync_updates['otto_pixel_uuid'] = $response_uuid;
237 - error_log('MetaSync: UUID set from heartbeat response (self-heal after SSO callback missed)');
246 + error_log('MetaSync: UUID set from heartbeat response (self-heal after SSO callback missed)'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
238 247 }
239 248 }
240 249
241 250 # PR3: Server confirmation → transition to CONNECTED (backend sends registered: true; accept both for compatibility)
@@ -456,9 +465,9 @@
456 465 $response = wp_remote_get($url, $args);
457 466
458 467 # PERFORMANCE OPTIMIZATION: Handle timeout and connection errors
459 468 if (is_wp_error($response)) {
460 - error_log('MetaSync: White label user sync failed: ' . $response->get_error_message());
469 + error_log('MetaSync: White label user sync failed: ' . $response->get_error_message()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
461 470 return;
462 471 }
463 472
464 473 $body = wp_remote_retrieve_body($response);