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] : '';
?>