options = get_option('ash_options', array()); if (!is_array($this->options)) { $this->options = array(); } } /** * Mark plugin admin screens so CSS can hide the WordPress footer and pad the canvas. * * @param string $classes Existing admin body classes. * @return string */ public function filter_admin_body_class($classes) { $screen_id = ''; if (function_exists('get_current_screen')) { $screen = get_current_screen(); if (is_object($screen) && isset($screen->id)) { $screen_id = (string) $screen->id; } } $hooks = array($this->page_hook, $this->settings_hook); if (!in_array($screen_id, $hooks, true)) { return $classes; } return $classes . ' ash-admin-screen'; } public function enqueue_admin_assets($hook) { $plugin_hooks = array($this->page_hook, $this->settings_hook); if (!in_array($hook, $plugin_hooks, true)) { return; } $css_path = ASH_PLUGIN_DIR . 'assets/css/admin.css'; $js_path = ASH_PLUGIN_DIR . 'assets/js/admin.js'; $css_version = file_exists($css_path) ? (string) filemtime($css_path) : ASH_VERSION; $js_version = file_exists($js_path) ? (string) filemtime($js_path) : ASH_VERSION; wp_enqueue_style( 'ash-admin-styles', ASH_PLUGIN_URL . 'assets/css/admin.css', array('dashicons'), $css_version ); wp_enqueue_script( 'ash-admin-scripts', ASH_PLUGIN_URL . 'assets/js/admin.js', array(), $js_version, true ); wp_localize_script('ash-admin-scripts', 'ashAdmin', array( 'headerFields' => array( 'x_xss_protection', 'x_content_type_options', 'strict_transport_security', 'permissions_policy', 'x_frame_options', 'referrer_policy', ), 'featureFields' => array( 'remove_x_powered_by', 'hide_wp_version', 'remove_login_errors', 'disable_xmlrpc', 'remove_x_pingback', 'restrict_rest_api', ), 'strings' => array( 'confirmSave' => __('Are you sure you want to save these settings?', 'abdal-security-headers'), 'confirmReset' => __('Reset unsaved changes and restore the last saved values?', 'abdal-security-headers'), 'saveChanges' => __('Save Changes', 'abdal-security-headers'), 'reset' => __('Reset', 'abdal-security-headers'), 'yes' => __('Yes', 'abdal-security-headers'), 'no' => __('No', 'abdal-security-headers'), 'success' => __('Settings saved successfully', 'abdal-security-headers'), 'ok' => __('OK', 'abdal-security-headers'), 'cancel' => __('Cancel', 'abdal-security-headers'), 'editorOk' => _x('OK', 'csp editor confirm', 'abdal-security-headers'), /* translators: %s: CSP directive name such as script-src */ 'editDirective' => __('Edit %s', 'abdal-security-headers'), 'error' => __('Error', 'abdal-security-headers'), 'errorMessage' => __('An error occurred while saving the settings. Please try again.', 'abdal-security-headers'), 'copied' => __('Copied', 'abdal-security-headers'), 'copyCsp' => __('Copy CSP header', 'abdal-security-headers'), 'copyFailed' => __('Unable to copy the CSP header.', 'abdal-security-headers'), 'cspEmpty' => __('Content-Security-Policy header is enabled but no directives are set.', 'abdal-security-headers'), 'cspDisabled' => __('Content Security Policy is disabled. Enable it to send this header.', 'abdal-security-headers'), 'statusGood' => _x('Good', 'security status', 'abdal-security-headers'), 'statusFair' => _x('Fair', 'security status', 'abdal-security-headers'), 'statusNeedsAttention' => _x('Needs attention', 'security status', 'abdal-security-headers'), 'statusGoodHint' => __('Most security controls are enabled.', 'abdal-security-headers'), 'statusFairHint' => __('Some recommended security controls are still disabled.', 'abdal-security-headers'), 'statusNeedsHint' => __('Enable more security headers and hardening options.', 'abdal-security-headers'), 'cspOn' => _x('Enabled', 'CSP status', 'abdal-security-headers'), 'cspOff' => _x('Disabled', 'CSP status', 'abdal-security-headers'), 'cspOnHint' => __('Content Security Policy is being sent with responses.', 'abdal-security-headers'), 'cspOffHint' => __('Content Security Policy is currently turned off.', 'abdal-security-headers'), /* translators: 1: number of enabled headers, 2: total headers */ 'headersHint' => __('%1$d of %2$d security headers are enabled', 'abdal-security-headers'), /* translators: 1: number of enabled features, 2: total features */ 'featuresHint' => __('%1$d of %2$d additional features are enabled', 'abdal-security-headers'), ), )); if ($hook !== $this->page_hook) { return; } $assistant_js_path = ASH_PLUGIN_DIR . 'assets/js/csp-assistant.js'; $assistant_js_version = file_exists($assistant_js_path) ? (string) filemtime($assistant_js_path) : ASH_VERSION; wp_enqueue_script( 'ash-csp-assistant', ASH_PLUGIN_URL . 'assets/js/csp-assistant.js', array('ash-admin-scripts'), $assistant_js_version, true ); wp_localize_script('ash-csp-assistant', 'ashCspAssistant', array( 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('ash_csp_assistant'), 'siteHost' => strtolower((string) wp_parse_url(home_url(), PHP_URL_HOST)), 'optionMap' => class_exists('ASH_CSP_Assistant') ? ASH_CSP_Assistant::OPTION_MAP : array(), 'strings' => class_exists('ASH_CSP_Assistant') ? ASH_CSP_Assistant::ui_strings() : array(), 'initial' => class_exists('ASH_CSP_Assistant') ? ASH_CSP_Assistant::instance()->payload() : array(), )); } public function add_plugin_page() { $this->page_hook = add_menu_page( esc_html__('Abdal Security Headers', 'abdal-security-headers'), esc_html__('Security Headers', 'abdal-security-headers'), 'manage_options', 'abdal-security-headers', array($this, 'create_admin_page'), 'dashicons-shield', 58 ); add_submenu_page( 'abdal-security-headers', esc_html__('Dashboard', 'abdal-security-headers'), esc_html__('Dashboard', 'abdal-security-headers'), 'manage_options', 'abdal-security-headers' ); $this->settings_hook = add_submenu_page( 'abdal-security-headers', esc_html__('Settings', 'abdal-security-headers'), esc_html__('Settings', 'abdal-security-headers'), 'manage_options', 'abdal-security-headers-settings', array($this, 'create_plugin_settings_page') ); } public function create_admin_page() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You are not allowed to manage these settings.', 'abdal-security-headers')); } require_once ASH_PLUGIN_DIR . 'includes/class-ash-admin-ui.php'; $ui = new ASH_Admin_UI($this->options); $ui->render(); } /** * Render the plugin Settings screen (plugin-owned options, not HTTP headers). * * @return void */ public function create_plugin_settings_page() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You are not allowed to manage these settings.', 'abdal-security-headers')); } $plugin_settings = get_option('ash_plugin_settings', array()); if (!is_array($plugin_settings)) { $plugin_settings = array(); } require_once ASH_PLUGIN_DIR . 'includes/class-ash-admin-ui.php'; $ui = new ASH_Admin_UI($this->options); $ui->render_plugin_settings($plugin_settings); } public function page_init() { register_setting( 'ash_options_group', 'ash_options', array($this, 'sanitize') ); register_setting( 'ash_plugin_settings_group', 'ash_plugin_settings', array( 'type' => 'array', 'sanitize_callback' => array($this, 'sanitize_plugin_settings'), 'default' => array( 'remove_data_on_uninstall' => '0', ), 'show_in_rest' => false, 'capability' => 'manage_options', ) ); // Basic Security Headers Section add_settings_section( 'ash_basic_headers', esc_html__('Basic Security Headers', 'abdal-security-headers'), array($this, 'print_section_info'), 'abdal-security-headers' ); // Original Headers $this->add_checkbox_field('x_xss_protection', esc_html__('X-XSS-Protection', 'abdal-security-headers'), 'ash_basic_headers'); $this->add_checkbox_field('x_content_type_options', esc_html__('X-Content-Type-Options', 'abdal-security-headers'), 'ash_basic_headers'); $this->add_checkbox_field('strict_transport_security', esc_html__('Strict-Transport-Security', 'abdal-security-headers'), 'ash_basic_headers'); $this->add_checkbox_field('permissions_policy', esc_html__('Permissions-Policy', 'abdal-security-headers'), 'ash_basic_headers'); $this->add_checkbox_field('x_frame_options', esc_html__('X-Frame-Options', 'abdal-security-headers'), 'ash_basic_headers'); $this->add_checkbox_field('referrer_policy', esc_html__('Referrer-Policy', 'abdal-security-headers'), 'ash_basic_headers'); // Additional Security Section add_settings_section( 'ash_additional_security', esc_html__('Additional Security Features', 'abdal-security-headers'), array($this, 'print_additional_section_info'), 'abdal-security-headers' ); // New Security Options $this->add_checkbox_field('remove_x_powered_by', esc_html__('Remove X-Powered-By Header', 'abdal-security-headers'), 'ash_additional_security'); $this->add_checkbox_field('hide_wp_version', esc_html__('Hide WordPress Version', 'abdal-security-headers'), 'ash_additional_security'); $this->add_checkbox_field('remove_login_errors', esc_html__('Remove Login Error Messages', 'abdal-security-headers'), 'ash_additional_security'); $this->add_checkbox_field('disable_xmlrpc', esc_html__('Disable XML-RPC', 'abdal-security-headers'), 'ash_additional_security'); $this->add_checkbox_field('remove_x_pingback', esc_html__('Remove X-Pingback Header', 'abdal-security-headers'), 'ash_additional_security'); $this->add_checkbox_field('restrict_rest_api', esc_html__('Restrict REST API Access', 'abdal-security-headers'), 'ash_additional_security'); // CSP Section add_settings_section( 'ash_csp_section', esc_html__('Content Security Policy', 'abdal-security-headers'), array($this, 'print_csp_section_info'), 'abdal-security-headers' ); // CSP Fields $csp_fields = array( 'default-src' => esc_html__('Default Source', 'abdal-security-headers'), 'script-src' => esc_html__('Script Source', 'abdal-security-headers'), 'style-src' => esc_html__('Style Source', 'abdal-security-headers'), 'img-src' => esc_html__('Image Source', 'abdal-security-headers'), 'connect-src' => esc_html__('Connect Source', 'abdal-security-headers'), 'font-src' => esc_html__('Font Source', 'abdal-security-headers'), 'object-src' => esc_html__('Object Source', 'abdal-security-headers'), 'media-src' => esc_html__('Media Source', 'abdal-security-headers'), 'frame-src' => esc_html__('Frame Source', 'abdal-security-headers'), 'worker-src' => esc_html__('Worker Source', 'abdal-security-headers'), 'form-action' => esc_html__('Form Action', 'abdal-security-headers'), 'base-uri' => esc_html__('Base URI', 'abdal-security-headers'), 'sandbox' => esc_html__('Sandbox', 'abdal-security-headers'), 'report-uri' => esc_html__('Report URI', 'abdal-security-headers'), 'report-to' => esc_html__('Report To', 'abdal-security-headers') ); foreach ($csp_fields as $key => $label) { $this->add_text_field('csp_' . str_replace('-', '_', $key), $label, 'ash_csp_section'); } } private function add_checkbox_field($id, $title, $section) { add_settings_field( $id, $title, array($this, 'checkbox_callback'), 'abdal-security-headers', $section, array( 'id' => $id, 'label' => $title, 'class' => 'ash-settings-row' ) ); } private function add_text_field($id, $title, $section) { add_settings_field( $id, $title, array($this, 'text_callback'), 'abdal-security-headers', $section, array( 'id' => $id, 'label' => $title, 'class' => 'ash-settings-row' ) ); } public function checkbox_callback($args) { $id = isset($args['id']) ? $args['id'] : ''; $label = isset($args['label']) ? $args['label'] : ''; $checked = isset($this->options[$id]) ? $this->options[$id] : '0'; ?> options[$id]) ? $this->options[$id] : ''; ?>