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 +12 -7 2.7.0 → 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());
@@ -205,9 +205,9 @@
205 205 $response = wp_remote_post($apiUrl, $data);
206 206
207 207 # PERFORMANCE OPTIMIZATION: Handle timeout and connection errors
208 208 if (is_wp_error($response)) {
209 - 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
210 210 $this->saveHeartBeatError('heartbeat', 'Connection error: ' . $response->get_error_message(), array(1), 0);
211 211 return;
212 212 }
213 213
@@ -226,20 +226,25 @@
226 226 # concurrent settings writes that happened during the HTTP window.
227 227 $_sync_updates = [];
228 228
229 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.
230 233 $response_body = json_decode(wp_remote_retrieve_body($response), true);
231 234 $current_uuid = $metasync_options['general']['otto_pixel_uuid'] ?? '';
232 235 if (is_array($response_body) && !empty($response_body['otto_pixel_uuid'])) {
233 236 $response_uuid = sanitize_text_field($response_body['otto_pixel_uuid']);
234 - 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'])) {
235 240 # Domain clone: backend says local UUID is wrong for this domain
236 241 $_sync_updates['otto_pixel_uuid'] = $response_uuid;
237 - 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
238 243 } elseif (empty($current_uuid)) {
239 244 # Self-heal: SSO callback failed but API key was saved; recover UUID from heartbeat
240 245 $_sync_updates['otto_pixel_uuid'] = $response_uuid;
241 - 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
242 247 }
243 248 }
244 249
245 250 # PR3: Server confirmation → transition to CONNECTED (backend sends registered: true; accept both for compatibility)
@@ -460,9 +465,9 @@
460 465 $response = wp_remote_get($url, $args);
461 466
462 467 # PERFORMANCE OPTIMIZATION: Handle timeout and connection errors
463 468 if (is_wp_error($response)) {
464 - 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
465 470 return;
466 471 }
467 472
468 473 $body = wp_remote_retrieve_body($response);