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 | admin/class-metasync-otto-debug.php +76 -115 2.6.24 → 2.7.2 View file →
@@ -49,12 +49,43 @@
49 49 }
50 50
51 51
52 52 /**
53 - * Add debug menu (developer only - hidden by default)
53 + * Whether debug tooling is allowed to run at all right now.
54 + *
55 + * `manage_options` alone isn't a real restriction — every site admin has
56 + * it, so this page and its AJAX handlers were reachable by anyone on any
57 + * site. Hard-disabled: the menu item, the page, and every AJAX handler
58 + * are unreachable for all users on all sites, no toggle or capability
59 + * bypasses this. The class and its methods are left in place; only
60 + * access is blocked.
54 61 */
62 + private static function is_debug_tools_enabled() {
63 + return false;
64 + }
65 +
66 + /**
67 + * Whether a URL's crawl pipeline has completed, per the bounded
68 + * Metasync_Otto_Job_Status store. Replaces the legacy unbounded
69 + * metasync_otto_crawldata option lookup: state history is pruned
70 + * 48 hours after completion, so a URL finished longer ago reads as
71 + * "not crawled" here without implying Otto never touched it.
72 + */
73 + private static function is_url_crawled($url) {
74 + if (!class_exists('Metasync_Otto_Job_Status')) {
75 + return false;
76 + }
77 + $entry = Metasync_Otto_Job_Status::get($url);
78 + return is_array($entry)
79 + && isset($entry['state'])
80 + && $entry['state'] === Metasync_Otto_Job_Status::STATE_COMPLETED;
81 + }
82 +
83 + /**
84 + * Add debug menu (internal only - hidden on customer production sites)
85 + */
55 86 public function add_debug_menu() {
56 - if (!current_user_can('manage_options')) {
87 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
57 88 return;
58 89 }
59 90
60 91 $menu_slug = Metasync_Admin::$page_slug;
@@ -72,9 +103,9 @@
72 103 /**
73 104 * Create the debug page
74 105 */
75 106 public function create_debug_page() {
76 - if (!current_user_can('manage_options')) {
107 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
77 108 wp_die('Unauthorized');
78 109 }
79 110
80 111 $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
@@ -88,9 +119,8 @@
88 119 <?php $this->render_url_testing_section(); ?>
89 120 <?php $this->render_configuration_status(); ?>
90 121 <?php $this->render_notification_endpoint_status(); ?>
91 122 <?php $this->render_api_connectivity_status(); ?>
92 - <?php $this->render_crawl_data_status(); ?>
93 123 <?php $this->render_processing_status(); ?>
94 124 <?php $this->render_debug_tools(); ?>
95 125 </div>
96 126 </div>
@@ -228,14 +258,14 @@
228 258 </div>
229 259
230 260 <div class="debug-item info">
231 261 <strong>Current User:</strong>
232 - <span class="debug-value"><?php echo esc_html($current_user->user_login); ?> (ID: <?php echo $current_user->ID; ?>)</span>
262 + <span class="debug-value"><?php echo esc_html($current_user->user_login); ?> (ID: <?php echo (int) $current_user->ID; ?>)</span>
233 263 </div>
234 264
235 265 <div class="debug-item info">
236 266 <strong>User Roles:</strong>
237 - <span class="debug-value"><?php echo implode(', ', $current_user->roles); ?></span>
267 + <span class="debug-value"><?php echo esc_html(implode(', ', $current_user->roles)); ?></span>
238 268 </div>
239 269
240 270 <div class="debug-item success">
241 271 <strong>Debug Access Active</strong>
@@ -331,9 +361,9 @@
331 361 </div>
332 362
333 363 <div class="debug-item info">
334 364 <strong>Plugin Version:</strong>
335 - <span class="debug-value"><?php echo defined('METASYNC_VERSION') ? METASYNC_VERSION : 'Unknown'; ?></span>
365 + <span class="debug-value"><?php echo esc_html(defined('METASYNC_VERSION') ? METASYNC_VERSION : 'Unknown'); ?></span>
336 366 </div>
337 367 </div>
338 368 <?php
339 369 }
@@ -435,64 +465,13 @@
435 465 <?php
436 466 }
437 467
438 468 /**
439 - * Render crawl data status
440 - */
441 - private function render_crawl_data_status() {
442 - $crawl_data = get_option('metasync_otto_crawldata');
443 -
444 - ?>
445 - <div class="debug-section">
446 - <h3><span class="status-indicator <?php echo !empty($crawl_data) ? 'status-success' : 'status-warning'; ?>"></span>Crawl Data Status</h3>
447 -
448 - <div class="debug-item <?php echo !empty($crawl_data) ? 'success' : 'warning'; ?>">
449 - <strong>Crawl Data Available:</strong>
450 - <span class="debug-value"><?php echo !empty($crawl_data) ? 'Yes' : 'No'; ?></span>
451 - </div>
452 -
453 - <?php if (!empty($crawl_data)): ?>
454 - <div class="debug-item info">
455 - <strong>Domain:</strong>
456 - <span class="debug-value"><?php echo esc_html($crawl_data['domain'] ?? 'Not Set'); ?></span>
457 - </div>
458 -
459 - <div class="debug-item info">
460 - <strong>Total URLs Crawled:</strong>
461 - <span class="debug-value"><?php echo count($crawl_data['urls'] ?? []); ?></span>
462 - </div>
463 -
464 - <div class="debug-item info">
465 - <strong>Last Updated:</strong>
466 - <span class="debug-value"><?php echo $this->get_option_last_updated('metasync_otto_crawldata'); ?></span>
467 - </div>
468 -
469 - <div class="debug-item info">
470 - <strong>Sample URLs:</strong>
471 - <div class="debug-value">
472 - <?php
473 - $sample_urls = array_slice($crawl_data['urls'] ?? [], 0, 5);
474 - foreach ($sample_urls as $url) {
475 - echo esc_html($url) . '<br>';
476 - }
477 - if (count($crawl_data['urls'] ?? []) > 5) {
478 - echo '... and ' . (count($crawl_data['urls']) - 5) . ' more';
479 - }
480 - ?>
481 - </div>
482 - </div>
483 - <?php endif; ?>
484 - </div>
485 - <?php
486 - }
487 -
488 - /**
489 469 * Render processing status
490 470 */
491 471 private function render_processing_status() {
492 472 $current_url = $this->get_current_url();
493 - $otto_pixel = new Metasync_otto_pixel(Metasync::get_option('general')['otto_pixel_uuid'] ?? '');
494 - $is_crawled = $otto_pixel->is_url_crawled($current_url);
473 + $is_crawled = self::is_url_crawled($current_url);
495 474 $render_diagnostics = $this->get_render_strategy_diagnostics();
496 475
497 476 ?>
498 477 <div class="debug-section">
@@ -514,19 +493,19 @@
514 493 </div>
515 494
516 495 <div class="debug-item info">
517 496 <strong>Page Type:</strong>
518 - <span class="debug-value"><?php echo $this->get_page_type(); ?></span>
497 + <span class="debug-value"><?php echo esc_html($this->get_page_type()); ?></span>
519 498 </div>
520 499
521 500 <div class="debug-item info">
522 501 <strong>Cache Status:</strong>
523 - <span class="debug-value"><?php echo $this->get_cache_status(); ?></span>
502 + <span class="debug-value"><?php echo esc_html($this->get_cache_status()); ?></span>
524 503 </div>
525 504
526 505 <div class="debug-item info">
527 506 <strong>Processing Method:</strong>
528 - <span class="debug-value"><?php echo $this->get_processing_method(); ?></span>
507 + <span class="debug-value"><?php echo esc_html($this->get_processing_method()); ?></span>
529 508 </div>
530 509
531 510 <?php if (!empty($render_diagnostics) && isset($render_diagnostics['available']) === false): ?>
532 511 <div class="debug-item info">
@@ -638,29 +617,8 @@
638 617 return !is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200;
639 618 }
640 619
641 620 /**
642 - * Get option last updated time
643 - */
644 - private function get_option_last_updated($option_name) {
645 - global $wpdb;
646 -
647 - $result = $wpdb->get_var($wpdb->prepare(
648 - "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
649 - $option_name
650 - ));
651 -
652 - if ($result) {
653 - $data = maybe_unserialize($result);
654 - if (isset($data['last_updated'])) {
655 - return date('Y-m-d H:i:s', $data['last_updated']);
656 - }
657 - }
658 -
659 - return 'Unknown';
660 - }
661 -
662 - /**
663 621 * Get current URL
664 622 */
665 623 private function get_current_url() {
666 624 $scheme = is_ssl() ? 'https' : 'http';
@@ -760,9 +718,9 @@
760 718 */
761 719 public function ajax_test_otto_api() {
762 720 check_ajax_referer('metasync_otto_debug', 'nonce');
763 721
764 - if (!current_user_can('manage_options')) {
722 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
765 723 wp_die('Unauthorized');
766 724 }
767 725
768 726 $general_options = Metasync::get_option('general');
@@ -816,9 +774,9 @@
816 774 */
817 775 public function ajax_test_notification_endpoint() {
818 776 check_ajax_referer('metasync_otto_debug', 'nonce');
819 777
820 - if (!current_user_can('manage_options')) {
778 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
821 779 wp_die('Unauthorized');
822 780 }
823 781
824 782 $endpoint_url = rest_url('metasync/v1/otto_crawl_notify');
@@ -858,20 +816,23 @@
858 816 * AJAX handler for clearing OTTO cache
859 817 */
860 818 public function ajax_clear_otto_cache() {
861 819 check_ajax_referer('metasync_otto_debug', 'nonce');
862 -
863 - if (!current_user_can('manage_options')) {
820 +
821 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
864 822 wp_die('Unauthorized');
865 823 }
866 -
867 - // Clear crawl data
824 +
825 + // Clear the retired crawl-data option if an internal debug tool is
826 + // ever re-enabled. This explicit action is harmless and preserves
827 + // the old clear-cache contract; production never reaches this
828 + // handler because debug tools are hard-disabled.
868 829 delete_option('metasync_otto_crawldata');
869 -
830 +
870 831 // Clear any cached API responses
871 832 $general_options = Metasync::get_option('general');
872 833 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
873 -
834 +
874 835 if (!empty($otto_uuid)) {
875 836 delete_transient(Metasync_Heartbeat_Manager::public_hash_cache_key($otto_uuid));
876 837 }
877 838
@@ -891,9 +852,9 @@
891 852 */
892 853 public function ajax_simulate_crawl_notification() {
893 854 check_ajax_referer('metasync_otto_debug', 'nonce');
894 855
895 - if (!current_user_can('manage_options')) {
856 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
896 857 wp_die('Unauthorized');
897 858 }
898 859
899 860 $general_options = Metasync::get_option('general');
@@ -930,9 +891,9 @@
930 891 public function ajax_test_specific_url() {
931 892 try {
932 893 check_ajax_referer('metasync_otto_debug', 'nonce');
933 894
934 - if (!current_user_can('manage_options')) {
895 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
935 896 wp_die('Unauthorized');
936 897 }
937 898
938 899 $test_url = sanitize_url($_POST['test_url'] ?? '');
@@ -964,9 +925,9 @@
964 925
965 926 wp_send_json_success($results);
966 927
967 928 } catch (Exception $e) {
968 - error_log('MetaSync OTTO Debug: Exception in ajax_test_specific_url: ' . $e->getMessage());
929 + error_log('MetaSync OTTO Debug: Exception in ajax_test_specific_url: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
969 930 wp_send_json_error('Exception: ' . $e->getMessage());
970 931 }
971 932 }
972 933
@@ -975,9 +936,9 @@
975 936 */
976 937 public function ajax_emulate_otto_changes() {
977 938 check_ajax_referer('metasync_otto_debug', 'nonce');
978 939
979 - if (!current_user_can('manage_options')) {
940 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
980 941 wp_die('Unauthorized');
981 942 }
982 943
983 944 $test_url = sanitize_url($_POST['test_url'] ?? '');
@@ -1071,27 +1032,27 @@
1071 1032 /**
1072 1033 * Test crawl status for URL
1073 1034 */
1074 1035 private function test_crawl_status($url) {
1075 - if (class_exists('Metasync_otto_pixel')) {
1076 - $otto_pixel = new Metasync_otto_pixel(false);
1077 - $is_crawled = $otto_pixel->is_url_crawled($url);
1078 -
1079 - // Get crawl data from options
1080 - $crawl_data = get_option('metasync_otto_crawldata');
1081 -
1036 + if (class_exists('Metasync_Otto_Job_Status')) {
1037 + $entry = Metasync_Otto_Job_Status::get($url);
1038 + $is_crawled = is_array($entry)
1039 + && isset($entry['state'])
1040 + && $entry['state'] === Metasync_Otto_Job_Status::STATE_COMPLETED;
1041 +
1082 1042 return array(
1083 1043 'status' => 'success',
1084 1044 'is_crawled' => $is_crawled,
1085 - 'crawl_data' => $crawl_data,
1086 - 'total_crawled_urls' => count($crawl_data['urls'] ?? array()),
1087 - 'domain' => $crawl_data['domain'] ?? 'Not set'
1045 + 'job_state' => is_array($entry) && isset($entry['state']) ? $entry['state'] : 'unknown',
1046 + 'job_stage' => is_array($entry) && isset($entry['stage']) ? $entry['stage'] : '',
1047 + 'job_attempts' => is_array($entry) && isset($entry['attempts']) ? (int) $entry['attempts'] : 0,
1048 + 'job_updated' => is_array($entry) && isset($entry['updated']) ? $entry['updated'] : null
1088 1049 );
1089 1050 }
1090 -
1051 +
1091 1052 return array(
1092 1053 'status' => 'error',
1093 - 'message' => Metasync::get_whitelabel_otto_name() . ' pixel class not available'
1054 + 'message' => Metasync::get_whitelabel_otto_name() . ' job status class not available'
1094 1055 );
1095 1056 }
1096 1057
1097 1058 /**
@@ -1097,9 +1058,9 @@
1097 1058 /**
1098 1059 * Test page type detection
1099 1060 */
1100 1061 private function test_page_type_detection($url) {
1101 - $parsed_url = parse_url($url);
1062 + $parsed_url = wp_parse_url($url);
1102 1063 $path = $parsed_url['path'] ?? '/';
1103 1064
1104 1065 $detection = array(
1105 1066 'url' => $url,
@@ -1198,9 +1159,9 @@
1198 1159 public function ajax_simple_test() {
1199 1160 try {
1200 1161 check_ajax_referer('metasync_otto_debug', 'nonce');
1201 1162
1202 - if (!current_user_can('manage_options')) {
1163 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1203 1164 wp_send_json_error('Unauthorized');
1204 1165 }
1205 1166
1206 1167 wp_send_json_success(array(
@@ -1210,9 +1171,9 @@
1210 1171 'nonce_verified' => true
1211 1172 ));
1212 1173
1213 1174 } catch (Exception $e) {
1214 - error_log('MetaSync OTTO Debug: Exception in ajax_simple_test: ' . $e->getMessage());
1175 + error_log('MetaSync OTTO Debug: Exception in ajax_simple_test: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
1215 1176 wp_send_json_error('Exception: ' . $e->getMessage());
1216 1177 }
1217 1178 }
1218 1179
@@ -1222,9 +1183,9 @@
1222 1183 public function ajax_test_db_permissions() {
1223 1184 try {
1224 1185 check_ajax_referer('metasync_otto_debug', 'nonce');
1225 1186
1226 - if (!current_user_can('manage_options')) {
1187 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1227 1188 wp_send_json_error('Unauthorized');
1228 1189 }
1229 1190
1230 1191 $results = array(
@@ -1244,9 +1205,9 @@
1244 1205
1245 1206 // Test 2: Check if we can access Action Scheduler tables
1246 1207 global $wpdb;
1247 1208 $table_name = $wpdb->prefix . 'actionscheduler_actions';
1248 - $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name;
1209 + $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($table_name))) == $table_name; // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1249 1210
1250 1211 $results['tests']['table_access'] = array(
1251 1212 'table_exists' => $table_exists,
1252 1213 'table_name' => $table_name,
@@ -1254,9 +1215,9 @@
1254 1215 );
1255 1216
1256 1217 // Test 3: Try to insert a test record
1257 1218 if ($table_exists) {
1258 - $insert_result = $wpdb->insert(
1219 + $insert_result = $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1259 1220 $table_name,
1260 1221 array(
1261 1222 'hook' => 'metasync_debug_test',
1262 1223 'status' => 'pending',
@@ -1275,9 +1236,9 @@
1275 1236 );
1276 1237
1277 1238 // Clean up test record
1278 1239 if ($insert_result !== false && $wpdb->insert_id) {
1279 - $wpdb->delete($table_name, array('ID' => $wpdb->insert_id));
1240 + $wpdb->delete($table_name, array('ID' => $wpdb->insert_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1280 1241 }
1281 1242 }
1282 1243
1283 1244 // Test 4: Check WordPress cron functionality
@@ -1289,9 +1250,9 @@
1289 1250
1290 1251 wp_send_json_success($results);
1291 1252
1292 1253 } catch (Exception $e) {
1293 - error_log('MetaSync OTTO Debug: Exception in ajax_test_db_permissions: ' . $e->getMessage());
1254 + error_log('MetaSync OTTO Debug: Exception in ajax_test_db_permissions: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
1294 1255 wp_send_json_error('Exception: ' . $e->getMessage());
1295 1256 }
1296 1257 }
1297 1258 }