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 | includes/class-metasync-debug-manager.php +121 -43 2.6.18 → 2.7.2 View file →
@@ -72,14 +72,23 @@
72 72 *
73 73 * @param Metasync_Admin $admin The admin instance (needed for header/nav rendering).
74 74 */
75 75 public function metasync_display_error_log($admin) {
76 - $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
76 + $log_file = Metasync_Data_Store::file_path('plugin_errors.log');
77 + if (false === $log_file) {
78 + $log_file = '';
79 + }
77 80 if (!Metasync::current_user_has_plugin_access()) {
78 81 return;
79 82 }
83 +
84 + $can_manage_debug_settings = current_user_can('manage_options');
85 + if (isset($_POST['wp_debug_nonce']) && !$can_manage_debug_settings) {
86 + return;
87 + }
80 88
81 - if (isset($_POST['wp_debug_log_enabled']) &&
89 + if ($can_manage_debug_settings &&
90 + isset($_POST['wp_debug_log_enabled']) &&
82 91 isset($_POST['wp_debug_enabled']) &&
83 92 isset($_POST['wp_debug_display_enabled']) &&
84 93 isset($_POST['wp_debug_nonce']) &&
85 94 wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
@@ -92,20 +101,30 @@
92 101 update_option('wp_debug_log_enabled', $wp_debug_log);
93 102 update_option('wp_debug_display_enabled', $wp_debug_display);
94 103
95 104 $data = new ConfigControllerMetaSync();
96 - $data->store();
97 -
105 + $saved = $data->store();
106 +
107 + if ($saved) {
108 + add_settings_error(
109 + 'metasync_messages',
110 + 'metasync_message',
111 + 'WordPress debug settings updated successfully.',
112 + 'updated'
113 + );
114 + } else {
115 + add_settings_error(
116 + 'metasync_messages',
117 + 'metasync_message',
118 + 'Debug settings were saved, but wp-config.php could not be updated: '
119 + . ($data->getConfigError() !== '' ? $data->getConfigError() : 'the file is not writable.'),
120 + 'error'
121 + );
122 + }
123 + } elseif ($can_manage_debug_settings && isset($_POST['wp_debug_nonce']) && !wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
98 124 add_settings_error(
99 125 'metasync_messages',
100 126 'metasync_message',
101 - 'WordPress debug settings updated successfully.',
102 - 'updated'
103 - );
104 - } elseif (isset($_POST['wp_debug_nonce']) && !wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
105 - add_settings_error(
106 - 'metasync_messages',
107 - 'metasync_message',
108 127 'Security verification failed. Please try again.',
109 128 'error'
110 129 );
111 130 }
@@ -218,9 +237,9 @@
218 237 * Update wp-config.php debug constants.
219 238 */
220 239 public function metasync_update_wp_config() {
221 240 $wp_config_path = ABSPATH . 'wp-config.php';
222 - if (file_exists($wp_config_path) && is_writable($wp_config_path)) {
241 + if (file_exists($wp_config_path) && wp_is_writable($wp_config_path)) {
223 242 $config_file = file_get_contents($wp_config_path);
224 243
225 244 $wp_debug_enabled = get_option('wp_debug_enabled', 'false') === 'true' ? 'true' : 'false';
226 245 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
@@ -242,8 +261,14 @@
242 261 } else {
243 262 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG_DISPLAY', $wp_debug_display_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
244 263 }
245 264
265 + // ABSPATH is intentional and verified: the Debug Mode feature
266 + // toggles WP_DEBUG constants in wp-config.php itself, the one
267 + // file whose location ABSPATH exists to describe. It is not a
268 + // plugin-directory or generated-content write, so it stays here
269 + // (Plugin Check's ABSPATHDetected warning is a false positive —
270 + // wp-config.php cannot live in the uploads directory).
246 271 file_put_contents($wp_config_path, $config_file);
247 272 } else {
248 273 wp_die('The wp-config.php file is not writable. Please check the file permissions.');
249 274 }
@@ -280,9 +305,15 @@
280 305 }
281 306 }
282 307
283 308 if ($changed) {
284 - require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
309 + if (!current_user_can('manage_options')) {
310 + return;
311 + }
312 +
313 + if (!class_exists('Metasync_Activator')) {
314 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
315 + }
285 316 Metasync_Activator::sync_plugin_file_headers();
286 317 }
287 318 }
288 319
@@ -369,9 +400,9 @@
369 400 <!-- Debug Mode Controls -->
370 401 <div style="margin-bottom: 30px;">
371 402 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Controls</h4>
372 403
373 - <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>">
404 + <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced')); ?>">
374 405 <input type="hidden" name="metasync_debug_mode_action_advanced" value="1" />
375 406 <?php wp_nonce_field('metasync_debug_mode_action_advanced', 'metasync_debug_mode_nonce_advanced'); ?>
376 407
377 408 <?php if (!$status['enabled']): ?>
@@ -467,9 +498,9 @@
467 498 }
468 499 });
469 500
470 501 <?php if ($status['enabled'] && !$status['indefinite']): ?>
471 - var initialTimeRemaining = <?php echo $status['time_remaining']; ?>;
502 + var initialTimeRemaining = <?php echo (int) $status['time_remaining']; ?>;
472 503 var hasReloaded = false;
473 504
474 505 function updateDebugTimeRemaining() {
475 506 if (initialTimeRemaining <= 0 || hasReloaded) {
@@ -476,12 +507,12 @@
476 507 return;
477 508 }
478 509
479 510 $.ajax({
480 - url: '<?php echo rest_url('metasync/v1/debug-mode/status'); ?>',
511 + url: '<?php echo esc_url(rest_url('metasync/v1/debug-mode/status')); ?>',
481 512 method: 'GET',
482 513 beforeSend: function(xhr) {
483 - xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
514 + xhr.setRequestHeader('X-WP-Nonce', '<?php echo esc_attr(wp_create_nonce('wp_rest')); ?>');
484 515 },
485 516 success: function(response) {
486 517 console.log('MetaSync Debug Mode Status:', response);
487 518
@@ -529,9 +560,9 @@
529 560 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">View categorized error statistics with counts and last occurrence times.</p>
530 561
531 562 <?php
532 563 if (class_exists('Metasync_Error_Logger')) {
533 - $error_summary = Metasync_Error_Logger::get_error_summary();
564 + $error_summary = Metasync_Error_Logger::get_visible_error_summary();
534 565
535 566 if (!empty($error_summary) && is_array($error_summary)) {
536 567 uasort($error_summary, function($a, $b) {
537 568 return strtotime($b['last_seen']) - strtotime($a['last_seen']);
@@ -551,9 +582,9 @@
551 582 <tbody>
552 583 <?php foreach ($error_summary as $key => $error): ?>
553 584 <tr>
554 585 <td style="padding: 10px 12px; color: var(--dashboard-text-primary);">
555 - <strong><?php echo esc_html($error['category']); ?></strong>
586 + <strong><?php echo esc_html(Metasync_Error_Logger::get_display_label($error['category'])); ?></strong>
556 587 </td>
557 588 <td style="padding: 10px 12px; text-align: center; color: var(--dashboard-text-secondary); font-family: monospace;">
558 589 <code style="background: rgba(255, 255, 255, 0.1); padding: 2px 6px; border-radius: 3px;"><?php echo esc_html($error['code']); ?></code>
559 590 </td>
@@ -565,9 +596,9 @@
565 596 <td style="padding: 10px 12px; color: var(--dashboard-text-secondary); font-size: 13px;">
566 597 <?php
567 598 $last_seen = strtotime($error['last_seen']);
568 599 $time_diff = human_time_diff($last_seen, current_time('timestamp'));
569 - echo esc_html($error['last_seen']) . ' <span style="color: var(--dashboard-text-secondary);">(' . $time_diff . ' ago)</span>';
600 + echo esc_html($error['last_seen']) . ' <span style="color: var(--dashboard-text-secondary);">(' . esc_html($time_diff) . ' ago)</span>';
570 601 ?>
571 602 </td>
572 603 <td style="padding: 10px 12px; color: var(--dashboard-text-primary); max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="<?php echo esc_attr($error['message']); ?>">
573 604 <?php echo esc_html($error['message']); ?>
@@ -577,9 +608,9 @@
577 608 </tbody>
578 609 </table>
579 610 </div>
580 611
581 - <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
612 + <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced')); ?>" style="margin-bottom: 20px;">
582 613 <input type="hidden" name="clear_error_summary" value="yes" />
583 614 <?php wp_nonce_field('metasync_clear_error_summary_nonce', 'clear_error_summary_nonce'); ?>
584 615 <button type="submit" class="button button-secondary" style="background: #dc3232; color: #ffffff; border: none; padding: 8px 16px; border-radius: 4px; font-weight: 500; cursor: pointer;">
585 616 <span class="dashicons dashicons-trash" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> Clear Error Summary
@@ -611,9 +642,9 @@
611 642 <div style="margin-bottom: 30px;">
612 643 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Clear Error Logs</h4>
613 644 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">Clear WordPress error logs to free up space and remove old entries.</p>
614 645
615 - <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
646 + <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced')); ?>" style="margin-bottom: 20px;">
616 647 <input type="hidden" name="clear_log" value="yes" />
617 648 <?php wp_nonce_field('metasync_clear_log_nonce', 'clear_log_nonce'); ?>
618 649 <button type="submit" class="metasync-btn-primary" style="background: var(--dashboard-gradient-primary); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto; min-width: 240px; max-width: fit-content;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
619 650 🧹 Clear Error Logs
@@ -729,9 +760,9 @@
729 760 {
730 761 if (isset($_POST['metasync_debug_mode_action_advanced'])) {
731 762 if (!wp_verify_nonce($_POST['metasync_debug_mode_nonce_advanced'], 'metasync_debug_mode_action_advanced')) {
732 763 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1');
733 - wp_redirect($redirect_url);
764 + wp_safe_redirect($redirect_url);
734 765 exit;
735 766 }
736 767
737 768 if (!current_user_can('manage_options')) {
@@ -739,9 +770,9 @@
739 770 }
740 771
741 772 if (!class_exists('Metasync_Debug_Mode_Manager')) {
742 773 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1&msg=manager_not_available');
743 - wp_redirect($redirect_url);
774 + wp_safe_redirect($redirect_url);
744 775 exit;
745 776 }
746 777
747 778 $debug_manager = Metasync_Debug_Mode_Manager::get_instance();
@@ -778,9 +809,9 @@
778 809 $redirect_url = add_query_arg('debug_error', '1', $redirect_url);
779 810 break;
780 811 }
781 812
782 - wp_redirect($redirect_url);
813 + wp_safe_redirect($redirect_url);
783 814 exit;
784 815 }
785 816 }
786 817
@@ -788,29 +819,36 @@
788 819 * Handle error log operations (clear).
789 820 */
790 821 public function handle_error_log_operations()
791 822 {
823 + if ((isset($_POST['clear_log']) || isset($_POST['clear_error_summary'])) && !current_user_can('manage_options')) {
824 + return;
825 + }
826 +
792 827 if (isset($_POST['clear_log'])) {
793 828 if (wp_verify_nonce($_POST['clear_log_nonce'], 'metasync_clear_log_nonce')) {
794 - $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
829 + $data_dir = Metasync_Data_Store::base_dir();
830 + $log_file = Metasync_Data_Store::file_path('plugin_errors.log');
795 831
796 - if (file_exists($log_file)) {
832 + if (false !== $log_file && file_exists($log_file)) {
797 833 file_put_contents($log_file, '');
798 834
799 - $backup_files = glob(WP_CONTENT_DIR . '/metasync_data/plugin_errors.log.old.*');
800 - if ($backup_files) {
801 - foreach ($backup_files as $backup_file) {
802 - @unlink($backup_file);
835 + if (false !== $data_dir) {
836 + $backup_files = glob($data_dir . '/plugin_errors.log.old.*');
837 + if ($backup_files) {
838 + foreach ($backup_files as $backup_file) {
839 + @wp_delete_file($backup_file);
840 + }
803 841 }
804 842 }
805 843 }
806 844
807 845 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&log_cleared=1');
808 - wp_redirect($redirect_url);
846 + wp_safe_redirect($redirect_url);
809 847 exit;
810 848 } else {
811 849 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_error=1');
812 - wp_redirect($redirect_url);
850 + wp_safe_redirect($redirect_url);
813 851 exit;
814 852 }
815 853 }
816 854
@@ -818,18 +856,18 @@
818 856 if (wp_verify_nonce($_POST['clear_error_summary_nonce'], 'metasync_clear_error_summary_nonce')) {
819 857 if (class_exists('Metasync_Error_Logger')) {
820 858 Metasync_Error_Logger::clear_error_summary();
821 859 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_cleared=1');
822 - wp_redirect($redirect_url);
860 + wp_safe_redirect($redirect_url);
823 861 exit;
824 862 } else {
825 863 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
826 - wp_redirect($redirect_url);
864 + wp_safe_redirect($redirect_url);
827 865 exit;
828 866 }
829 867 } else {
830 868 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
831 - wp_redirect($redirect_url);
869 + wp_safe_redirect($redirect_url);
832 870 exit;
833 871 }
834 872 }
835 873 }
@@ -838,8 +876,12 @@
838 876 * Handle clear all settings operations.
839 877 */
840 878 public function handle_clear_all_settings()
841 879 {
880 + if (isset($_POST['clear_all_settings']) && !current_user_can('manage_options')) {
881 + return;
882 + }
883 +
842 884 if (isset($_POST['clear_all_settings'])) {
843 885 if (wp_verify_nonce($_POST['clear_all_settings_nonce'], 'metasync_clear_all_settings_nonce')) {
844 886
845 887 $metasync_options_to_clear = [
@@ -860,8 +902,18 @@
860 902 // Code Minification settings
861 903 'metasync_code_minification',
862 904 ];
863 905
906 + // Capture the complete white-label identity BEFORE the
907 + // destructive clear so it can be restored afterwards. The
908 + // reset is meant to remove operational data and credentials,
909 + // not the reseller's public branding, logos, domains, custom
910 + // OTTO name, visibility/access controls, or recovery settings.
911 + if (!class_exists('Metasync_Whitelabel_Preservation')) {
912 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-whitelabel-preservation.php';
913 + }
914 + $whitelabel_state = Metasync_Whitelabel_Preservation::extract_whitelabel_state();
915 +
864 916 $cleared_count = 0;
865 917 foreach ($metasync_options_to_clear as $option_name) {
866 918 if (get_option($option_name) !== false) {
867 919 delete_option($option_name);
@@ -903,15 +955,41 @@
903 955 'options_cleared_count' => $cleared_count,
904 956 'new_token_prefix' => substr($new_plugin_auth_token, 0, 8) . '...',
905 957 'triggered_by' => 'settings_reset_action'
906 958 ), 'info');
907 -
959 +
960 + // Restore the preserved white-label identity over the freshly
961 + // cleared options. A failed restore must not report success.
962 + $restored = Metasync_Whitelabel_Preservation::restore_whitelabel_state($whitelabel_state);
963 + if (!$restored) {
964 + $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
965 + wp_redirect($redirect_url);
966 + exit;
967 + }
968 +
969 + // Re-synchronise the branded plugin file headers so the
970 + // WordPress plugin list and the plugin UI stay branded after
971 + // the reset. Only attempted when white-label branding exists,
972 + // and a failed sync is logged as a warning, not reported as a
973 + // reset failure: the reset itself (operational clear + fresh
974 + // token + branding restore) has already succeeded at this
975 + // point, and hosts with read-only plugin files would otherwise
976 + // never see a successful reset.
977 + if (!empty($whitelabel_state['whitelabel']) || !empty($whitelabel_state['general'])) {
978 + if (!class_exists('Metasync_Activator')) {
979 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
980 + }
981 + if (!Metasync_Activator::sync_plugin_file_headers()) {
982 + error_log('MetaSync: failed to re-synchronise branded plugin file headers after the settings reset.');
983 + }
984 + }
985 +
908 986 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&settings_cleared=1');
909 - wp_redirect($redirect_url);
987 + wp_safe_redirect($redirect_url);
910 988 exit;
911 989 } else {
912 990 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
913 - wp_redirect($redirect_url);
991 + wp_safe_redirect($redirect_url);
914 992 exit;
915 993 }
916 994 }
917 995 }
@@ -926,9 +1004,9 @@
926 1004 public function get_error_log_content()
927 1005 {
928 1006 $execution_time = $this->get_execution_setting('max_execution_time');
929 1007 if (function_exists('set_time_limit')) {
930 - @set_time_limit($execution_time);
1008 + @set_time_limit($execution_time); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
931 1009 }
932 1010
933 1011 $log_file = WP_CONTENT_DIR . '/debug.log';
934 1012
@@ -960,9 +1038,9 @@
960 1038 public function get_log_tail($file_path, $lines = null)
961 1039 {
962 1040 $execution_time = $this->get_execution_setting('max_execution_time');
963 1041 if (function_exists('set_time_limit')) {
964 - @set_time_limit($execution_time);
1042 + @set_time_limit($execution_time); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
965 1043 }
966 1044
967 1045 if ($lines === null) {
968 1046 $lines = $this->get_execution_setting('log_batch_size');
@@ -967,9 +1045,9 @@
967 1045 if ($lines === null) {
968 1046 $lines = $this->get_execution_setting('log_batch_size');
969 1047 }
970 1048
971 - $handle = fopen($file_path, 'r');
1049 + $handle = fopen($file_path, 'r'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- streaming file I/O; WP_Filesystem cannot return a raw stream handle
972 1050 if (!$handle) {
973 1051 return false;
974 1052 }
975 1053
@@ -998,9 +1076,9 @@
998 1076 if (!empty($line) && $line_count < $lines) {
999 1077 array_unshift($result_lines, strrev($line));
1000 1078 }
1001 1079
1002 - fclose($handle);
1080 + fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- operates on a native stream handle
1003 1081
1004 1082 return implode("\n", $result_lines);
1005 1083 }
1006 1084 }