admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('mcb_ajax_nonce') )); } public function ajax_load_file() { // Check request method if ($_SERVER['REQUEST_METHOD'] !== 'POST') { wp_die('Invalid request method', 405); } // Rate limiting check if (class_exists('MCB_Privacy_Manager') && MCB_Privacy_Manager::is_privacy_mode()) { // Use nonce-based rate limiting in privacy mode $rate_limit_key = 'mcb_rate_' . wp_get_current_user()->ID . '_' . wp_create_nonce('mcb_rate'); } else { // IP-based rate limiting $user_ip = $this->get_client_ip(); $rate_limit_key = 'mcb_rate_' . md5($user_ip); } $requests = get_transient($rate_limit_key); if ($requests !== false && $requests > 30) { // 30 requests per minute wp_die('Rate limit exceeded. Please try again later.', 429); } set_transient($rate_limit_key, ($requests ? $requests + 1 : 1), 60); // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mcb_ajax_nonce')) { wp_die('Security check failed', 403); } // Note: We allow public viewing of public repositories by default // Site admins can restrict access if needed via filter $require_login = apply_filters('mcb_require_login_for_viewing', false); if ($require_login && !is_user_logged_in()) { wp_die('Please log in to view code repositories', 403); } $repo = isset($_POST['repo']) ? sanitize_text_field($_POST['repo']) : ''; $file_path = isset($_POST['file_path']) ? sanitize_text_field($_POST['file_path']) : ''; // Additional validation using security class if (!MCB_Security::validate_repo_format($repo)) { MCB_Security::log_security_event('invalid_repo_format', array('repo' => $repo)); wp_send_json_error('Invalid repository format'); return; } if (!MCB_Security::validate_file_path($file_path)) { MCB_Security::log_security_event('invalid_file_path', array('path' => $file_path)); wp_send_json_error('Invalid file path'); return; } $github_api = new MCB_GitHub_API(); $content = $github_api->get_file_content($repo, $file_path); if (is_wp_error($content)) { wp_send_json_error($content->get_error_message()); return; } // Validate content before sending $renderer = new MCB_Code_Renderer(); $validation = $renderer->validate_content($content); if (is_wp_error($validation)) { wp_send_json_error($validation->get_error_message()); return; } // Return raw content - JavaScript will handle escaping and rendering // This is more secure as it avoids double-escaping issues wp_send_json_success(array( 'content' => $content, // Send raw content, not rendered HTML 'filename' => basename($file_path) )); } public function ajax_get_repo_files() { // Check request method if ($_SERVER['REQUEST_METHOD'] !== 'POST') { wp_die('Invalid request method', 405); } // Rate limiting check $user_ip = $this->get_client_ip(); $rate_limit_key = 'mcb_rate_' . md5($user_ip); $requests = get_transient($rate_limit_key); if ($requests !== false && $requests > 30) { // 30 requests per minute wp_die('Rate limit exceeded. Please try again later.', 429); } set_transient($rate_limit_key, ($requests ? $requests + 1 : 1), 60); // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mcb_ajax_nonce')) { wp_die('Security check failed', 403); } // Note: We allow public viewing of public repositories by default // Site admins can restrict access if needed via filter $require_login = apply_filters('mcb_require_login_for_viewing', false); if ($require_login && !is_user_logged_in()) { wp_die('Please log in to view code repositories', 403); } $repo = isset($_POST['repo']) ? sanitize_text_field($_POST['repo']) : ''; $path = isset($_POST['path']) ? sanitize_text_field($_POST['path']) : ''; // Validate repository format if (!MCB_Security::validate_repo_format($repo)) { wp_send_json_error('Invalid repository format'); return; } $github_api = new MCB_GitHub_API(); $files = $github_api->get_repository_files($repo, $path); if (is_wp_error($files)) { wp_send_json_error($files->get_error_message()); return; } wp_send_json_success($files); } public function add_admin_menu() { add_options_page( 'GitHub Code Viewer Settings', 'GitHub Code Viewer', 'manage_options', 'maple-code-blocks', array($this, 'admin_page') ); } public function admin_page() { include MCB_PLUGIN_PATH . 'admin/settings-page.php'; } /** * Get client IP address for rate limiting */ private function get_client_ip() { $ip_keys = array('HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'); foreach ($ip_keys as $key) { if (array_key_exists($key, $_SERVER) === true) { $ips = explode(',', $_SERVER[$key]); foreach ($ips as $ip) { $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) { return $ip; } } } } return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; } } // Initialize the plugin Maple_Code_Blocks::get_instance(); // Register deactivation hook to clean up scheduled events register_deactivation_hook(__FILE__, 'mcb_deactivate'); function mcb_deactivate() { // Remove scheduled cleanup event $timestamp = wp_next_scheduled('mcb_privacy_cleanup'); if ($timestamp) { wp_unschedule_event($timestamp, 'mcb_privacy_cleanup'); } // Clear all MCB transients global $wpdb; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_mcb_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_mcb_%'"); } // Register uninstall hook for complete cleanup register_uninstall_hook(__FILE__, 'mcb_uninstall'); function mcb_uninstall() { // Remove all plugin options delete_option('mcb_settings'); delete_option('mcb_github_token_encrypted'); delete_option('mcb_gitlab_token_encrypted'); delete_option('mcb_bitbucket_token_encrypted'); delete_option('mcb_codeberg_token_encrypted'); // Clear all transients global $wpdb; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_mcb_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_mcb_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_site_transient_mcb_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_site_transient_timeout_mcb_%'"); }