PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
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 | includes/class-metasync-connect-manager.php +34 -40 2.6.26 → 2.7.1 View file →
@@ -88,9 +88,9 @@
88 88 $general_options = Metasync::get_option('general') ?? [];
89 89 $plugin_auth_token = $general_options['apikey'] ?? '';
90 90
91 91 if (empty($plugin_auth_token)) {
92 - error_log('MetaSync ERROR: Plugin Auth Token missing from options - should have been generated during activation');
92 + error_log('MetaSync ERROR: Plugin Auth Token missing from options - should have been generated during activation'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
93 93 return false;
94 94 }
95 95
96 96 return $plugin_auth_token;
@@ -188,9 +188,9 @@
188 188 wp_send_json_error(array('message' => 'Failed to save new token'));
189 189 }
190 190
191 191 } catch (Exception $e) {
192 - error_log('Plugin Auth Token Refresh Error: ' . $e->getMessage());
192 + error_log('Plugin Auth Token Refresh Error: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
193 193 wp_send_json_error(array('message' => 'Error generating new token'));
194 194 }
195 195 }
196 196
@@ -222,9 +222,9 @@
222 222 wp_send_json_error(array('message' => 'Plugin Auth Token not found'));
223 223 }
224 224
225 225 } catch (Exception $e) {
226 - error_log('Get Plugin Auth Token Error: ' . $e->getMessage());
226 + error_log('Get Plugin Auth Token Error: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
227 227 wp_send_json_error(array('message' => 'Error retrieving Plugin Auth Token'));
228 228 }
229 229 }
230 230
@@ -270,15 +270,21 @@
270 270 $domain = str_replace('://www.', '://', get_site_url());
271 271
272 272 $dashboard_domain = Metasync_Admin::get_effective_dashboard_domain();
273 273
274 - $sa_connect_url = $dashboard_domain . '/sso/wordpress?' . http_build_query([
274 + $connect_args = [
275 275 'nonce_token' => $sa_connect_token,
276 276 'domain' => $domain,
277 277 'callback_url' => get_rest_url(null, 'metasync/v1/searchatlas/connect/callback'),
278 278 'return_url' => admin_url('admin.php?page=' . Metasync_Admin::$page_slug)
279 - ]);
279 + ];
280 280
281 + if (Metasync_Headless_Config::is_active()) {
282 + $connect_args['frontend_domain'] = Metasync_Headless_Config::get_frontend_domain();
283 + }
284 +
285 + $sa_connect_url = $dashboard_domain . '/sso/wordpress?' . http_build_query($connect_args);
286 +
281 287 wp_send_json_success(array(
282 288 'connect_url' => $sa_connect_url,
283 289 'nonce_token' => $sa_connect_token,
284 290 'debug_info' => array(
@@ -323,9 +329,9 @@
323 329 if (!$this_auth_completed && wp_using_ext_object_cache()) {
324 330 global $wpdb;
325 331
326 332 // Check expiry first
327 - $timeout = $wpdb->get_var(
333 + $timeout = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
328 334 $wpdb->prepare(
329 335 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
330 336 '_transient_timeout_' . $success_key
331 337 )
@@ -332,12 +338,12 @@
332 338 );
333 339
334 340 if ($timeout && (int) $timeout < time()) {
335 341 // Expired — clean up
336 - $wpdb->delete($wpdb->options, array('option_name' => '_transient_' . $success_key));
337 - $wpdb->delete($wpdb->options, array('option_name' => '_transient_timeout_' . $success_key));
342 + $wpdb->delete($wpdb->options, array('option_name' => '_transient_' . $success_key)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
343 + $wpdb->delete($wpdb->options, array('option_name' => '_transient_timeout_' . $success_key)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
338 344 } else {
339 - $db_value = $wpdb->get_var(
345 + $db_value = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
340 346 $wpdb->prepare(
341 347 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
342 348 '_transient_' . $success_key
343 349 )
@@ -353,10 +359,10 @@
353 359 delete_transient($success_key);
354 360 // Also clean up DB fallback rows
355 361 if (wp_using_ext_object_cache()) {
356 362 global $wpdb;
357 - $wpdb->delete($wpdb->options, array('option_name' => '_transient_' . $success_key));
358 - $wpdb->delete($wpdb->options, array('option_name' => '_transient_timeout_' . $success_key));
363 + $wpdb->delete($wpdb->options, array('option_name' => '_transient_' . $success_key)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
364 + $wpdb->delete($wpdb->options, array('option_name' => '_transient_timeout_' . $success_key)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
359 365 }
360 366
361 367 // Get current settings to return connection status
362 368 $general_settings = Metasync::get_option('general') ?? [];
@@ -557,9 +563,9 @@
557 563 $general_options = Metasync::get_option('general') ?? [];
558 564 $plugin_auth_token = $general_options['apikey'] ?? '';
559 565
560 566 if (empty($plugin_auth_token)) {
561 - error_log('MetaSync ERROR: Plugin Auth Token missing from options');
567 + error_log('MetaSync ERROR: Plugin Auth Token missing from options'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
562 568 return false;
563 569 }
564 570
565 571 $random_bytes = wp_generate_password(32, false, false);
@@ -597,12 +603,12 @@
597 603 $timeout_active = '_transient_timeout_metasync_sa_connect_active_' . $sa_connect_token;
598 604 $timeout_metadata = '_transient_timeout_' . $transient_key;
599 605 $expires = time() + 900;
600 606
601 - $wpdb->replace($wpdb->options, array('option_name' => $active_option, 'option_value' => $transient_key, 'autoload' => 'no'));
602 - $wpdb->replace($wpdb->options, array('option_name' => $timeout_active, 'option_value' => $expires, 'autoload' => 'no'));
603 - $wpdb->replace($wpdb->options, array('option_name' => $metadata_option, 'option_value' => maybe_serialize($token_metadata), 'autoload' => 'no'));
604 - $wpdb->replace($wpdb->options, array('option_name' => $timeout_metadata, 'option_value' => $expires, 'autoload' => 'no'));
607 + $wpdb->replace($wpdb->options, array('option_name' => $active_option, 'option_value' => $transient_key, 'autoload' => 'no')); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
608 + $wpdb->replace($wpdb->options, array('option_name' => $timeout_active, 'option_value' => $expires, 'autoload' => 'no')); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
609 + $wpdb->replace($wpdb->options, array('option_name' => $metadata_option, 'option_value' => maybe_serialize($token_metadata), 'autoload' => 'no')); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
610 + $wpdb->replace($wpdb->options, array('option_name' => $timeout_metadata, 'option_value' => $expires, 'autoload' => 'no')); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
605 611 }
606 612
607 613 return $sa_connect_token;
608 614 }
@@ -736,9 +742,9 @@
736 742 {
737 743 global $wpdb;
738 744
739 745 try {
740 - $rate_limit_transients = $wpdb->get_results(
746 + $rate_limit_transients = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- transient enumeration/cleanup on the options table — no API to scan or bulk-delete transient rows
741 747 "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '_transient_sa_connect_rate_limit_%'",
742 748 ARRAY_A
743 749 );
744 750
@@ -763,9 +769,9 @@
763 769 public function clear_jwt_token_cache()
764 770 {
765 771 global $wpdb;
766 772
767 - $deleted = $wpdb->query(
773 + $deleted = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
768 774 $wpdb->prepare(
769 775 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
770 776 '_transient_metasync_jwt_token_%'
771 777 )
@@ -770,9 +776,9 @@
770 776 '_transient_metasync_jwt_token_%'
771 777 )
772 778 );
773 779
774 - $wpdb->query(
780 + $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- manual transient rows on the options table (autoload=no) — mirrors core transient storage; TTL reads need the paired timeout row
775 781 $wpdb->prepare(
776 782 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
777 783 '_transient_timeout_metasync_jwt_token_%'
778 784 )
@@ -896,9 +902,9 @@
896 902 try {
897 903 $response = wp_remote_post($url, $args);
898 904
899 905 if (is_wp_error($response)) {
900 - error_log('MetaSync: JWT token API request failed - ' . $response->get_error_message());
906 + error_log('MetaSync: JWT token API request failed - ' . $response->get_error_message()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
901 907 return false;
902 908 }
903 909
904 910 $response_code = wp_remote_retrieve_response_code($response);
@@ -904,9 +910,9 @@
904 910 $response_code = wp_remote_retrieve_response_code($response);
905 911 $response_body = wp_remote_retrieve_body($response);
906 912
907 913 if ($response_code !== 200) {
908 - error_log('MetaSync: JWT token API returned error code ' . $response_code);
914 + error_log('MetaSync: JWT token API returned error code ' . $response_code); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
909 915 return false;
910 916 }
911 917
912 918 $data = json_decode($response_body, true);
@@ -911,9 +917,9 @@
911 917
912 918 $data = json_decode($response_body, true);
913 919
914 920 if (!$data || !isset($data['token'], $data['expires'])) {
915 - error_log('MetaSync: Invalid JWT token API response format');
921 + error_log('MetaSync: Invalid JWT token API response format'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
916 922 return false;
917 923 }
918 924
919 925 $token_data = array(
@@ -927,9 +933,9 @@
927 933
928 934 return $data['token'];
929 935
930 936 } catch (Exception $e) {
931 - error_log('MetaSync: Exception during JWT generation - ' . $e->getMessage());
937 + error_log('MetaSync: Exception during JWT generation - ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
932 938 return false;
933 939 }
934 940 }
935 941
@@ -1112,9 +1118,9 @@
1112 1118 'timestamp' => current_time('mysql', true)
1113 1119 ));
1114 1120
1115 1121 } catch (Exception $e) {
1116 - error_log('Authentication Reset Error: ' . $e->getMessage());
1122 + error_log('Authentication Reset Error: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
1117 1123 wp_send_json_error(array(
1118 1124 'message' => 'An error occurred while resetting authentication. Please try again or contact support.',
1119 1125 'code' => 'reset_failed',
1120 1126 'error' => $e->getMessage()
@@ -1180,20 +1186,8 @@
1180 1186 ));
1181 1187 }
1182 1188
1183 1189 /**
1184 - * Simple AJAX test without nonce (for debugging connectivity)
1185 - */
1186 - public function simple_ajax_test()
1187 - {
1188 - wp_send_json_success(array(
1189 - 'message' => 'Basic AJAX connectivity works',
1190 - 'timestamp' => time(),
1191 - 'no_nonce_required' => true
1192 - ));
1193 - }
1194 -
1195 - /**
1196 1190 * Test whitelabel domain configuration (development/debugging)
1197 1191 */
1198 1192 public function test_whitelabel_domain()
1199 1193 {
@@ -1241,10 +1235,10 @@
1241 1235 if (!is_admin()) {
1242 1236 return;
1243 1237 }
1244 1238
1245 - $active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
1246 - $current_page = isset($_GET['page']) ? $_GET['page'] : '';
1239 + $active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only routing, each action verifies its own nonce
1240 + $current_page = isset($_GET['page']) ? $_GET['page'] : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only routing, each action verifies its own nonce
1247 1241
1248 1242 $whitelabel_settings = Metasync::get_whitelabel_settings();
1249 1243 $user_password = $whitelabel_settings['settings_password'] ?? '';
1250 1244 $hide_settings_enabled = !empty($whitelabel_settings['hide_settings']);
@@ -1279,11 +1273,11 @@
1279 1273 if (isset($_POST['whitelabel_logout'])) {
1280 1274 if (wp_verify_nonce($_POST['whitelabel_logout_nonce'] ?? '', 'whitelabel_logout_nonce')) {
1281 1275 $auth->revoke_access();
1282 1276
1283 - $redirect_tab = $_GET['tab'] ?? 'whitelabel';
1277 + $redirect_tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'whitelabel';
1284 1278 $redirect_url = admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '&tab=' . $redirect_tab);
1285 - wp_redirect($redirect_url);
1279 + wp_safe_redirect($redirect_url);
1286 1280 exit;
1287 1281 }
1288 1282 }
1289 1283