Plugin Directory

Changeset 3470719


Ignore:
Timestamp:
02/26/2026 10:24:02 PM (4 weeks ago)
Author:
Marc4
Message:

v0.8

Location:
security-hardener
Files:
5 added
2 edited

Legend:

Unmodified
Added
Removed
  • security-hardener/trunk/readme.txt

    r3466602 r3470719  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 0.7
     7Stable tag: 0.8
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    5353> ⚠️ **Important:** Always test security settings in a staging environment first. Some features may affect third-party integrations or plugins.
    5454
    55 **Privacy:** This plugin does not send data to external services, does not create custom database tables, and only uses WordPress transients for temporary login attempt tracking.
     55**Privacy:** This plugin does not send data to external services and does not create custom database tables. It stores plugin settings and a security event log in the WordPress options table, and uses transients for temporary login attempt tracking. All data is deleted on uninstall.
    5656
    5757== Installation ==
     
    160160
    161161== Changelog ==
     162
     163= 0.8 - 2026-02-26 =
     164* Improved: Moved define_security_constants() from plugins_loaded hook to the constructor, ensuring DISALLOW_FILE_EDIT and DISALLOW_FILE_MODS are defined as early as possible in the WordPress lifecycle
     165* Improved: Expanded @param docblock for render_checkbox_field() to document all $args keys
     166* Added: WordPress Playground blueprint (assets/blueprints/blueprint.json) enabling live plugin preview directly from the WordPress.org plugin directory
     167* Fixed: Plugin header description updated to remove REST API restriction option removed in 0.5
     168* Fixed: Removed stale phpcs:ignore comment in show_admin_notices() — nonce verification is now correctly documented inline
     169* Fixed: Wrapped login block error message with wp_kses_post() for consistent output escaping
     170* Fixed: Added esc_url() and esc_html__() to add_settings_link() sprintf output
     171* Fixed: Removed redundant get_client_ip() call in log_security_event() — IP resolved once per event
     172* Fixed: Added autoload=false to wpsh_security_logs option — logs are only needed on the settings page, not loaded on every request
    162173
    163174= 0.7 - 2026-02-21 =
  • security-hardener/trunk/security-hardener.php

    r3466602 r3470719  
    33Plugin Name: Security Hardener
    44Plugin URI: https://wordpress.org/plugins/security-hardener/
    5 Description: Basic hardening: secure headers, disable XML-RPC/pingbacks, hide version, block user enumeration, login errors, IP-based rate limiting, and optional restriction of the REST API.
    6 Version: 0.7
     5Description: Basic hardening: secure headers, disable XML-RPC/pingbacks, hide version, block user enumeration, generic login errors, and IP-based rate limiting.
     6Version: 0.8
    77Requires at least: 6.0
    88Tested up to: 6.9
     
    2020
    2121// Plugin constants
    22 define( 'WPSH_VERSION', '0.7' );
     22define( 'WPSH_VERSION', '0.8' );
    2323define( 'WPSH_FILE', __FILE__ );
    2424define( 'WPSH_DIR', plugin_dir_path( __FILE__ ) );
     
    7171            $this->options = $this->get_options();
    7272
     73            // Define security constants as early as possible
     74            $this->define_security_constants();
     75
    7376            // Activation/Deactivation hooks
    7477            register_activation_hook( WPSH_FILE, array( $this, 'activate' ) );
     
    8386         */
    8487        public function init() {
    85             // Define security constants early
    86             $this->define_security_constants();
    87 
    8888            // Security headers
    8989            add_action( 'send_headers', array( $this, 'send_security_headers' ) );
     
    457457                return new WP_Error(
    458458                    'too_many_attempts',
    459                     '<strong>' . esc_html__( 'Error:', 'security-hardener' ) . '</strong> ' . sprintf(
    460                         /* translators: %d: number of minutes */
    461                         esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'security-hardener' ),
    462                         $minutes
     459                    wp_kses_post(
     460                        '<strong>' . esc_html__( 'Error:', 'security-hardener' ) . '</strong> ' . sprintf(
     461                            /* translators: %d: number of minutes */
     462                            esc_html__( 'Too many failed login attempts. Please try again in %d minutes.', 'security-hardener' ),
     463                            $minutes
     464                        )
    463465                    )
    464466                );
     
    638640            $logs = get_option( 'wpsh_security_logs', array() );
    639641
     642            // Resolve IP once to avoid calling get_client_ip() twice
     643            $ip = $this->get_client_ip();
     644
    640645            // Add new log entry
    641646            $logs[] = array(
     
    643648                'type'       => $event_type,
    644649                'message'    => $message,
    645                 'ip'         => $this->get_client_ip(),
     650                'ip'         => $ip,
    646651                'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '',
    647652            );
     
    652657            }
    653658
    654             update_option( 'wpsh_security_logs', $logs );
     659            // autoload=false: logs are only needed on the settings page, not on every request
     660            update_option( 'wpsh_security_logs', $logs, false );
    655661        }
    656662
     
    831837         * Render checkbox field
    832838         *
    833          * @param array $args Field arguments.
     839         * @param array $args {
     840         *     Field arguments.
     841         *
     842         *     @type string $field_id    Option key in the stored options array.
     843         *     @type string $description Optional description shown beside the checkbox.
     844         * }
    834845         */
    835846        public function render_checkbox_field( $args ) {
     
    10421053        public function show_admin_notices() {
    10431054            // Clear logs action - verify nonce to prevent CSRF
    1044             // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    1045             if ( isset( $_GET['action'] ) && 'clear_logs' === $_GET['action'] && current_user_can( 'manage_options' ) ) {
     1055            if ( isset( $_GET['action'] ) && 'clear_logs' === $_GET['action'] && current_user_can( 'manage_options' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce is verified on the next line
    10461056                if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wpsh_clear_logs' ) ) {
    10471057                    wp_die( esc_html__( 'Security check failed.', 'security-hardener' ) );
     
    11531163            $settings_link = sprintf(
    11541164                '<a href="%s">%s</a>',
    1155                 admin_url( 'options-general.php?page=security-hardener' ),
    1156                 __( 'Settings', 'security-hardener' )
     1165                esc_url( admin_url( 'options-general.php?page=security-hardener' ) ),
     1166                esc_html__( 'Settings', 'security-hardener' )
    11571167            );
    11581168            array_unshift( $links, $settings_link );
Note: See TracChangeset for help on using the changeset viewer.