Plugin Directory

Changeset 3300103


Ignore:
Timestamp:
05/25/2025 06:56:50 AM (10 months ago)
Author:
wpiron
Message:

Add functionality to change default admin username

Location:
iron-security
Files:
2 added
10 edited
45 copied

Legend:

Unmodified
Added
Removed
  • iron-security/tags/2.3.3/README.txt

    r3296251 r3300103  
    55Requires at least: 4.7
    66Tested up to: 6.8
    7 Stable tag: 2.3.2
     7Stable tag: 2.3.3
    88Requires PHP: 7.0
    99License: GPLv2 or later
     
    3535- Change default Admin ID
    3636- Block user enumeration
     37- Change Default Admin Username
    3738
    3839**Files & Directory Protection**
     
    6566
    6667= What makes Iron Security different from other WordPress security plugins? =
    67 Iron Security is designed to be lightweight, fast, and focused on practical features that matter most for securing your WordPress site.
     68Iron Security is lightweight, fast, and focused on the most effective hardening techniques. Instead of bloated features, it delivers practical tools that directly protect your WordPress site from real threats.
    6869
    6970= Is Iron Security suitable for beginners? =
    70 Yes! Iron Security comes with an intuitive dashboard and clear explanations for each option. Whether you're a WordPress beginner or an experienced developer, you'll find it easy to use and configure.
    71 
    72 = How does the custom login URL help protect my site? =
    73 Changing the default `/wp-admin` or `/wp-login.php` URL makes it harder for bots and attackers to find your login page, reducing brute force attempts. You can set your own unique login slug in a few clicks from the plugin settings.
    74 
    75 = What happens when a user exceeds the allowed login attempts? =
    76 If a user exceeds the allowed number of login attempts, their IP will be temporarily blocked based on your configured lockout settings. You can customize the number of allowed attempts, lockout duration, and view attempt logs.
    77 
    78 = How does the Admin ID protection work? =
    79 By default, WordPress assigns user ID 1 to the first admin account — a known vulnerability targeted by bots. Iron Security lets you assign a different ID to your admin account, making it harder to guess and exploit.
    80 
    81 = Does Iron Security block XML-RPC and REST API? Why? =
    82 Yes, you can optionally disable XML-RPC and REST API — two common attack vectors. XML-RPC is often used in DDoS and brute force attacks, while REST API may expose user data. Disabling them improves security, especially if you don’t use them.
    83 
    84 = What are HTTP security headers and why should I enable them? =
    85 HTTP security headers like X-Frame-Options, Content-Security-Policy, and Strict-Transport-Security provide an extra layer of browser-based protection. They help prevent XSS, clickjacking, and other code injection attacks. Iron Security lets you enable them easily from the dashboard.
     71Absolutely. Iron Security features an intuitive dashboard with clear explanations for each setting. Whether you’re new to WordPress or a seasoned developer, you’ll find it easy to set up and use.
     72
     73= How does changing the login URL protect my site? =
     74By replacing the default /wp-admin or /wp-login.php URLs, you reduce the risk of automated brute force attacks. Bots and attackers can’t easily locate your login page, adding an extra layer of protection.
     75
     76= What happens if someone exceeds the allowed login attempts? =
     77Their IP will be temporarily blocked based on your configured settings. You can set how many failed attempts are allowed, how long the lockout lasts, and monitor the attempt logs directly from the plugin dashboard.
     78
     79= How does Admin ID protection work? =
     80WordPress assigns the first admin user an ID of 1 — a known target for attacks. Iron Security helps you avoid this by allowing you to assign a different user ID to your admin account, reducing exposure to targeted exploits.
     81
     82= Can Iron Security block XML-RPC and REST API access? =
     83Yes. These features can be optionally disabled. XML-RPC and REST API are common targets for attacks like brute force or user data enumeration. If you don’t use them, disabling them can significantly reduce your attack surface.
     84
     85= What are HTTP security headers, and why should I enable them? =
     86HTTP security headers such as X-Frame-Options, Content-Security-Policy, and Strict-Transport-Security enhance browser-level security. They help protect your site against XSS, clickjacking, and other injection-based attacks. Iron Security lets you enable these headers with just a few clicks.
    8687
    8788= Will Iron Security slow down my website? =
    88 Not at all. The plugin is built to be lightweight and uses efficient code practices. It doesn’t run background scans or heavy processes, so your site’s performance remains unaffected.
    89 
    90 = Can I use Iron Security on WooCommerce stores? =
    91 Absolutely. Iron Security is fully compatible with WooCommerce and protects your login area, admin panel, and core files without affecting your store’s functionality.
    92 
    93 = Where can I get support or report a bug? =
    94 You can submit issues or ask for help via the [support forum on WordPress.org](https://wordpress.org/support/plugin/iron-security/) or by contacting us directly at [https://wpiron.com](https://wpiron.com).
     89No. Iron Security is performance-focused and doesn't include heavy scans or background processes. It’s designed to secure your site without impacting loading speed or user experience.
     90
     91= Is Iron Security compatible with WooCommerce? =
     92Yes, Iron Security works seamlessly with WooCommerce. It protects your admin area, login forms, and critical files without interfering with your store's functionality or customer experience.
     93
     94= How can I get support or report a bug? =
     95You can reach out via the WordPress.org support forum: https://wordpress.org/support/plugin/iron-security/ or contact our team directly at https://wpiron.com.
    9596
    9697= How often is Iron Security updated? =
    97 We actively maintain and improve Iron Security. You can expect regular updates for new features, security patches, and WordPress compatibility improvements.
     98We actively maintain Iron Security to ensure compatibility with the latest WordPress releases. Expect regular updates with new features, security improvements, and bug fixes.
     99
     100= What is the benefit of disabling file editing in WordPress? =
     101Disabling the file editor in the admin panel prevents attackers from injecting malicious code directly into theme or plugin files if they gain admin access. This is a simple but effective hardening step.
     102
     103= What does hiding the WordPress version do? =
     104Iron Security removes the WordPress version from your website’s source code to reduce the risk of automated attacks targeting specific versions with known vulnerabilities.
     105
     106= How does Iron Security block AI crawlers? =
     107The plugin adds specific rules to your robots.txt file to block AI and data scraping bots, helping protect your content from unauthorized use and training.
     108
     109= What does "Limit number of administrators" mean? =
     110Limiting admin users helps reduce the number of high-privilege accounts on your site. The plugin notifies you if more than one admin account exists, encouraging better access control and role management.
     111
     112= How does session timeout protect my site? =
     113If a logged-in user is inactive for a certain period, they are automatically logged out. This prevents unauthorized access from unattended sessions on shared or public devices.
     114
     115= Can I block user enumeration? =
     116Yes. Iron Security prevents bots from discovering usernames through the `?author=` query parameter, a common tactic used in brute force and targeted attacks.
     117
     118= What does “Change default admin username” do? =
     119If your admin account uses "admin" as the username, it becomes an easy target. Iron Security helps you safely change this default to something unique and secure.
     120
     121= What does "Prevent direct file access" mean? =
     122The plugin restricts access to sensitive files like PHP templates and system files that should not be accessed directly, protecting your site from unauthorized requests.
     123
     124= Can I block PHP file uploads? =
     125Yes. Iron Security allows you to block PHP file uploads in forms and media to prevent malicious scripts from being uploaded to your site.
     126
     127= What are plugin and core auto-updates, and why enable them? =
     128Keeping your WordPress core and plugins updated is critical to staying secure. Iron Security allows you to enable automatic updates, so your site is always protected with the latest patches.
    98129
    99130
     
    111142== Changelog ==
    112143
     144= 2.3.3 =
     145* Add functionality to change default admin username
     146
    113147= 2.3.2 =
    114148* Gutenberg some blocks were disabled - fixed it.
  • iron-security/tags/2.3.3/admin/class-iron-security-admin.php

    r3291538 r3300103  
    4747                plugin_dir_url( __FILE__ ) . 'css/admin.css',
    4848                array(),
    49                 time(),
     49                $this->version,
    5050                'all' );
    5151            wp_enqueue_style( $this->plugin_name . '-dashboard',
     
    5757                plugin_dir_url( __FILE__ ) . 'css/transitions.css',
    5858                array(),
    59                 time(),
     59                $this->version,
    6060                'all' );
    6161        }
     
    8181            plugin_dir_url( __FILE__ ) . 'js/iron-security-admin.js',
    8282            array( 'jquery', 'wp-element', 'wp-components' ),
    83 //          $this->version,
    84             time(),
     83            $this->version,
     84//          time(),
    8585            false
    8686        );
     
    9292                plugin_dir_url( __FILE__ ) . 'js/session-timeout.js',
    9393                array( 'jquery' ),
    94 //              $this->version,
    95                 time(),
     94                $this->version,
     95//              time(),
    9696                true
    9797            );
     
    114114                array( 'wp-element', 'wp-components', 'wp-i18n' ),
    115115                $this->version,
     116//                time(),
    116117                true
    117118            );
     
    122123                array( 'wp-element', 'wp-components', 'wp-i18n' ),
    123124                $this->version,
     125//                time(),
    124126                true
    125127            );
     
    171173                // Your content for login/logout tab
    172174                echo '<h3>Login/Logout Settings</h3>';
    173                 // add your logic here
    174             }
    175             // Add other tabs content conditions
     175
     176            }
     177
    176178            ?>
    177179        </div>
     
    223225        $sanitized_input = array();
    224226        foreach ( $input as $key => $value ) {
    225             // Special handling for checkbox/toggle fields
     227
    226228            if ( in_array( $key, array(
    227229                'wpironis_disable_xmlrpc',
     
    346348            $secret = get_user_meta( $user->ID, 'google_authenticator_secret', true );
    347349            if ( ! $secret ) {
    348                 return $user; // No 2FA setup for the user
     350                return $user;
    349351            }
    350352
     
    478480        }
    479481
    480         // When enabled is true, we want to disable XML-RPC
     482
    481483        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    482484        $options = get_option( 'wpironis_options', array() );
    483485
    484         // Set to 1 to disable XML-RPC when toggle is enabled
     486
    485487        $options['wpironis_disable_xmlrpc'] = $enabled ? 1 : 0;
    486488
     
    494496            ) );
    495497        } else {
    496             // Check if the value was actually the same (no update needed)
     498
    497499            $current_options = get_option( 'wpironis_options' );
    498500            if ( isset( $current_options['wpironis_disable_xmlrpc'] ) &&
     
    520522        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    521523
    522         // Get current options or initialize array
     524
    523525        $options = get_option( 'wpironis_options', array() );
    524526
    525         // Update the WordPress version hiding setting
     527
    526528        $options['wpironis_hide_wp_version'] = $enabled ? 1 : 0;
    527529
    528         // Save the updated options
     530
    529531        $updated = update_option( 'wpironis_options', $options );
    530532
     
    536538            ) );
    537539        } else {
    538             // Check if the value was actually the same (no update needed)
     540
    539541            $current_options = get_option( 'wpironis_options' );
    540542            if ( isset( $current_options['wpironis_hide_wp_version'] ) &&
     
    562564        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    563565
    564         // Get current options or initialize array
     566
    565567        $options = get_option( 'wpironis_options', array() );
    566568
    567         // Update the security headers setting
     569
    568570        $options['wpironis_security_headers'] = $enabled ? 1 : 0;
    569571
    570         // Save the updated options
     572
    571573        $updated = update_option( 'wpironis_options', $options );
    572574
     
    578580            ) );
    579581        } else {
    580             // Check if the value was actually the same (no update needed)
     582
    581583            $current_options = get_option( 'wpironis_options' );
    582584            if ( isset( $current_options['wpironis_security_headers'] ) &&
     
    604606        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    605607
    606         // Get current options or initialize array
     608
    607609        $options = get_option( 'wpironis_options', array() );
    608610
    609         // Update the PHP uploads blocking setting
     611
    610612        $options['wpironis_block_php_uploads'] = $enabled ? 1 : 0;
    611613
    612         // Save the updated options
     614
    613615        $updated = update_option( 'wpironis_options', $options );
    614616
    615         // Handle .htaccess rules
     617
    616618        if ( $enabled ) {
    617619            $this->wpironis_create_upload_protection();
     
    627629            ) );
    628630        } else {
    629             // Check if the value was actually the same (no update needed)
     631
    630632            $current_options = get_option( 'wpironis_options' );
    631633            if ( isset( $current_options['wpironis_block_php_uploads'] ) &&
     
    646648        $htaccess_path = $upload_dir['basedir'] . '/.htaccess';
    647649
    648         // Define the rules
     650
    649651        $rules = "
    650652# Iron Security
     
    675677";
    676678
    677         // Create or update .htaccess file
     679
    678680        if ( ! file_exists( $htaccess_path ) || is_writable( $htaccess_path ) ) {
    679681            file_put_contents( $htaccess_path, $rules, LOCK_EX );
    680682        }
    681683
    682         // Also create an index.php file to prevent directory listing
     684
    683685        $index_path = $upload_dir['basedir'] . '/index.php';
    684686        if ( ! file_exists( $index_path ) ) {
     
    697699
    698700    public function wpironis_prevent_php_upload( $file ) {
    699         // Get the current options
     701
    700702        $options = get_option( 'wpironis_options', array() );
    701703
    702         // Check if PHP upload blocking is enabled
     704
    703705        if ( ! isset( $options['wpironis_block_php_uploads'] ) || $options['wpironis_block_php_uploads'] !== 1 ) {
    704706            return $file;
    705707        }
    706708
    707         // Ensure the file name is set and is a string
     709
    708710        if ( ! isset( $file['name'] ) || empty( $file['name'] ) || ! is_string( $file['name'] ) ) {
    709711            $file['error'] = __( 'Invalid file name detected. Upload blocked.', 'iron-security' );
     
    712714        }
    713715
    714         // List of blocked extensions
     716
    715717        $blocked_extensions = array(
    716718            'php',
     
    734736        $extension = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) ?: '' );
    735737
    736         // Check if the file extension is in the blocked list
     738
    737739        if ( in_array( $extension, $blocked_extensions, true ) ) {
    738740            $file['error'] = sprintf(
     
    745747        }
    746748
    747         // Check for double extensions (e.g., file.jpg.php)
     749
    748750        $all_extensions = explode( '.', $file_path );
    749751        if ( count( $all_extensions ) > 2 ) {
     
    789791            ) );
    790792        } else {
    791             // Check if the value was actually the same (no update needed)
     793
    792794            $current_options = get_option( 'wpironis_options' );
    793795            if ( isset( $current_options['wpironis_prevent_direct_access'] ) &&
     
    842844";
    843845
    844         // Create or update root .htaccess file
     846
    845847        if ( ! file_exists( $root_htaccess_path ) || is_writable( $root_htaccess_path ) ) {
    846             // If .htaccess exists, read its content
     848
    847849            $existing_content = file_exists( $root_htaccess_path ) ? file_get_contents( $root_htaccess_path ) : '';
    848850
    849             // Check if our rules are already present
     851
    850852            if ( strpos( $existing_content, 'Iron Security - Prevent Direct File Access' ) === false ) {
    851                 // Add our rules after WordPress rules if they exist
     853
    852854                if ( strpos( $existing_content, '# END WordPress' ) !== false ) {
    853855                    $existing_content = str_replace( '# END WordPress',
     
    862864        }
    863865
    864         // Also protect wp-content directory
     866
    865867        $content_htaccess_path = WP_CONTENT_DIR . '/.htaccess';
    866868        $content_rules         = "
     
    918920        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    919921
    920         // Get current options or initialize array
     922
    921923        $options = get_option( 'wpironis_options', array() );
    922924
    923         // Update the file editor setting
     925
    924926        $options['wpironis_disable_file_editor'] = $enabled ? 1 : 0;
    925927
    926         // Save the updated options
     928
    927929        $updated = update_option( 'wpironis_options', $options );
    928930
    929         // Update wp-config.php to disable file editor
     931
    930932        if ( $enabled ) {
    931933            $this->wpironis_disable_file_editor_in_config();
     
    941943            ) );
    942944        } else {
    943             // Check if the value was actually the same (no update needed)
     945
    944946            $current_options = get_option( 'wpironis_options' );
    945947            if ( isset( $current_options['wpironis_disable_file_editor'] ) &&
     
    965967        $config_content = file_get_contents( $wp_config_path );
    966968
    967         // Check if the constant is already defined
     969
    968970        if ( strpos( $config_content, 'DISALLOW_FILE_EDIT' ) === false ) {
    969             // Find the line where we should add our constant
     971
    970972            $insert_point = strpos( $config_content, "/* That's all, stop editing!" );
    971973
     
    981983            }
    982984        } else {
    983             // Update existing constant if it's set to false
     985
    984986            $config_content = preg_replace(
    985987                "/define\(\s*'DISALLOW_FILE_EDIT'\s*,\s*false\s*\);/",
     
    10001002        $config_content = file_get_contents( $wp_config_path );
    10011003
    1002         // Remove the constant definition if it exists
     1004
    10031005        $config_content = preg_replace(
    10041006            "/define\(\s*'DISALLOW_FILE_EDIT'\s*,\s*true\s*\);(\r\n|\n|\r)?/",
     
    10211023        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    10221024
    1023         // Get current options or initialize array
     1025
    10241026        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    10251027
    1026         // Update the custom URL setting
     1028
    10271029        $options['enable_slug_change'] = $enabled ? '1' : '0';
    10281030
    1029         // Save the updated options
     1031
    10301032        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    10311033
     
    10371039            ) );
    10381040        } else {
    1039             // Check if the value was actually the same (no update needed)
     1041
    10401042            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    10411043            if ( isset( $current_options['enable_slug_change'] ) &&
     
    10671069        }
    10681070
    1069         $url = sanitize_title( $_POST['url'] ); // Sanitize and convert spaces to hyphens
     1071        $url = sanitize_title( $_POST['url'] );
    10701072
    10711073        if ( empty( $url ) ) {
     
    10751077        }
    10761078
    1077         // Get current options
     1079
    10781080        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    10791081
    1080         // Update the custom URL
     1082
    10811083        $options['wpironis_custom_login_slug'] = $url;
    10821084
    1083         // Save the updated options
     1085
    10841086        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    10851087
     
    10911093            ) );
    10921094        } else {
    1093             // Check if the value was actually the same (no update needed)
     1095
    10941096            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    10951097            if ( isset( $current_options['wpironis_custom_login_slug'] ) &&
     
    11171119        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    11181120
    1119         // Get current options or initialize array
     1121
    11201122        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    11211123
    1122         // Update the session timeout setting
     1124
    11231125        $options['enable_session_timeout'] = $enabled ? '1' : '0';
    11241126
    1125         // Save the updated options
     1127
    11261128        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    11271129
     
    11331135            ) );
    11341136        } else {
    1135             // Check if the value was actually the same (no update needed)
     1137
    11361138            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    11371139            if ( isset( $current_options['enable_session_timeout'] ) &&
     
    11721174        }
    11731175
    1174         // Get current options
     1176
    11751177        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    11761178
    1177         // Update the timeout value
     1179
    11781180        $options['session_timeout_value'] = $timeout;
    11791181
    1180         // Save the updated options
     1182
    11811183        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    11821184
     
    11881190            ) );
    11891191        } else {
    1190             // Check if the value was actually the same (no update needed)
     1192
    11911193            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    11921194            if ( isset( $current_options['session_timeout_value'] ) &&
     
    12141216        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    12151217
    1216         // Get current options
     1218
    12171219        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    12181220
    1219         // Update the limit login attempts setting
     1221
    12201222        $options['enable_limit_login_attempts'] = $enabled ? '1' : '0';
    12211223
    1222         // Save the updated options
     1224
    12231225        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    12241226
     
    12301232            ) );
    12311233        } else {
    1232             // Check if the value was actually the same (no update needed)
     1234
    12331235            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    12341236            if ( isset( $current_options['enable_limit_login_attempts'] ) &&
     
    12771279        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    12781280
    1279         // Update the settings
     1281
    12801282        $options['wpironis_limit_login_attempts'] = (string) $attempts;
    12811283        $options['wpironis_lockout_duration']     = (string) $duration;
    12821284
    1283         // Save the updated options
     1285
    12841286        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    12851287
     
    12921294            ) );
    12931295        } else {
    1294             // Check if the values were actually the same (no update needed)
     1296
    12951297            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    12961298            if ( isset( $current_options['wpironis_limit_login_attempts'] ) &&
     
    13211323        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    13221324
    1323         // Get current options
     1325
    13241326        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    13251327
    1326         // Update the user enumeration protection setting
     1328
    13271329        $options['enable_user_enumeration'] = $enabled ? '1' : '0';
    13281330
    1329         // Save the updated options
     1331
    13301332        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    13311333
     
    13371339            ) );
    13381340        } else {
    1339             // Check if the value was actually the same (no update needed)
     1341
    13401342            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    13411343            if ( isset( $current_options['enable_user_enumeration'] ) &&
     
    13751377        }
    13761378
    1377         // Get current options
     1379
    13781380        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    13791381
    1380         // Update the custom error message
     1382
    13811383        $options['user_enumeration_message'] = $message;
    13821384
    1383         // Save the updated options
     1385
    13841386        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    13851387
     
    13911393            ) );
    13921394        } else {
    1393             // Check if the value was actually the same (no update needed)
     1395
    13941396            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    13951397            if ( isset( $current_options['user_enumeration_message'] ) &&
     
    14071409
    14081410    public function wpironis_modify_login_errors( $error ) {
    1409         // Get the options
     1411
    14101412        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    14111413
    1412         // Check if user enumeration protection is enabled
     1414
    14131415        if ( ! empty( $options['enable_user_enumeration'] ) && $options['enable_user_enumeration'] === '1' ) {
    1414             // Get the custom message or use default
     1416
    14151417            $custom_message = ! empty( $options['user_enumeration_message'] )
    14161418                ? $options['user_enumeration_message']
    14171419                : 'Wrong Login credentials';
    14181420
    1419             // Check if this is a password-related error for an existing username
     1421
    14201422            if ( strpos( $error, 'password you entered for' ) !== false ) {
    14211423                return __( $custom_message, 'iron-security' );
     
    14271429
    14281430    public function wpironis_block_user_enumeration() {
    1429         // Get the options
     1431
    14301432        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    14311433
    1432         // Check if user enumeration protection is enabled
     1434
    14331435        if ( ! empty( $options['enable_user_enumeration'] ) && $options['enable_user_enumeration'] === '1' ) {
    1434             // Block access to author pages
     1436
    14351437            if ( is_author() ) {
    14361438                wp_redirect( home_url(), 301 );
     
    14381440            }
    14391441
    1440             // Block user enumeration through REST API
     1442
    14411443            add_filter( 'rest_endpoints', function ( $endpoints ) {
    14421444                if ( isset( $endpoints['/wp/v2/users'] ) ) {
     
    14501452            } );
    14511453
    1452             // Block ?author=N queries
     1454
    14531455            if ( isset( $_GET['author'] ) && is_numeric( $_GET['author'] ) ) {
    14541456                wp_redirect( home_url(), 301 );
     
    14691471        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    14701472
    1471         // Get current options or initialize array
     1473
    14721474        $options = get_option( 'wpironis_options', array() );
    14731475
    1474         // Update the REST API setting
     1476
    14751477        $options['wpironis_disable_rest_api'] = $enabled ? 1 : 0;
    14761478
    1477         // Save the updated options
     1479
    14781480        $updated = update_option( 'wpironis_options', $options );
    14791481
     
    14851487            ) );
    14861488        } else {
    1487             // Check if the value was actually the same (no update needed)
     1489
    14881490            $current_options = get_option( 'wpironis_options' );
    14891491            if ( isset( $current_options['wpironis_disable_rest_api'] ) &&
     
    15111513        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    15121514
    1513         // Get current options or initialize array
     1515
    15141516        $options = get_option( 'wpironis_options', array() );
    15151517
    1516         // Update the plugin auto-update setting
     1518
    15171519        $options['wpironis_enable_plugin_autoupdate'] = $enabled ? 1 : 0;
    15181520
    1519         // Save the updated options
     1521
    15201522        $updated = update_option( 'wpironis_options', $options );
    15211523
    1522         // Configure plugin auto-updates
     1524
    15231525        $this->wpironis_configure_plugin_autoupdates( $enabled );
    15241526
     
    15301532            ) );
    15311533        } else {
    1532             // Check if the value was actually the same (no update needed)
     1534
    15331535            $current_options = get_option( 'wpironis_options' );
    15341536            if ( isset( $current_options['wpironis_enable_plugin_autoupdate'] ) &&
     
    15561558        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    15571559
    1558         // Get current options or initialize array
     1560
    15591561        $options = get_option( 'wpironis_options', array() );
    15601562
    1561         // Update the core auto-update setting
     1563
    15621564        $options['wpironis_enable_core_autoupdate'] = $enabled ? 1 : 0;
    15631565
    1564         // Save the updated options
     1566
    15651567        $updated = update_option( 'wpironis_options', $options );
    15661568
    1567         // Configure core auto-updates
     1569
    15681570        $this->wpironis_configure_core_autoupdates( $enabled );
    15691571
     
    15751577            ) );
    15761578        } else {
    1577             // Check if the value was actually the same (no update needed)
     1579
    15781580            $current_options = get_option( 'wpironis_options' );
    15791581            if ( isset( $current_options['wpironis_enable_core_autoupdate'] ) &&
     
    15921594    private function wpironis_configure_plugin_autoupdates( $enabled ) {
    15931595        if ( $enabled ) {
    1594             // Enable auto-updates for all plugins using filter
     1596
    15951597            add_filter( 'auto_update_plugin', '__return_true' );
    15961598
    1597             // Also set the auto_update_plugins option
     1599
    15981600            $all_plugins = array_keys( get_plugins() );
    15991601            update_site_option( 'auto_update_plugins', $all_plugins );
    16001602        } else {
    1601             // Disable auto-updates for all plugins
     1603
    16021604            add_filter( 'auto_update_plugin', '__return_false' );
    16031605
    1604             // Clear the auto_update_plugins option
     1606
    16051607            update_site_option( 'auto_update_plugins', array() );
    16061608        }
     
    16091611    private function wpironis_configure_core_autoupdates( $enabled ) {
    16101612        if ( $enabled ) {
    1611             // Enable core auto-updates for all update types
     1613
    16121614            add_filter( 'allow_major_auto_core_updates', '__return_true' );
    16131615            add_filter( 'allow_minor_auto_core_updates', '__return_true' );
     
    16151617            add_filter( 'auto_update_translation', '__return_true' );
    16161618
    1617             // Update the core update settings
     1619
    16181620            update_site_option( 'auto_update_core_major', 'enabled' );
    16191621            update_site_option( 'auto_update_core_minor', 'enabled' );
    16201622            update_site_option( 'auto_update_core_dev', 'enabled' );
    16211623        } else {
    1622             // Disable core auto-updates
     1624
    16231625            add_filter( 'allow_major_auto_core_updates', '__return_false' );
    16241626            add_filter( 'allow_minor_auto_core_updates', '__return_false' );
     
    16261628            add_filter( 'auto_update_translation', '__return_false' );
    16271629
    1628             // Update the core update settings
     1630
    16291631            update_site_option( 'auto_update_core_major', 'disabled' );
    16301632            update_site_option( 'auto_update_core_minor', 'disabled' );
     
    16601662        }
    16611663
    1662         // Update last activity time
     1664
    16631665        update_user_meta( get_current_user_id(), 'iron_security_last_activity', time() );
    16641666    }
     
    17041706        }
    17051707
    1706         // Get filter parameters
     1708
    17071709        $log_type   = isset( $_POST['log_type'] ) ? sanitize_text_field( $_POST['log_type'] ) : '';
    17081710        $status     = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : '';
     
    17141716        $per_page   = isset( $_POST['per_page'] ) ? intval( $_POST['per_page'] ) : 50;
    17151717
    1716         // Build filter arguments
     1718
    17171719        $args = array(
    17181720            'log_type'   => $log_type,
     
    17261728        );
    17271729
    1728         // Get logs with the specified filters
     1730
    17291731        $logs_data = Iron_Security_Logger::get_logs( $args );
    17301732
    1731         // Get log summary
     1733
    17321734        $summary = $this->get_logs_summary();
    17331735
     
    17491751        $table_name = $wpdb->prefix . 'iron_security_logs';
    17501752
    1751         // Get total count
     1753
    17521754        $total = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name" );
    17531755
    1754         // Get count by status
     1756
    17551757        $success_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'success'" );
    17561758        $failure_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'failure'" );
     
    17771779        }
    17781780
    1779         // Get filter parameters
     1781
    17801782        $log_type   = isset( $_POST['log_type'] ) ? sanitize_text_field( $_POST['log_type'] ) : '';
    17811783        $status     = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : '';
     
    17851787        $date_to    = isset( $_POST['date_to'] ) ? sanitize_text_field( $_POST['date_to'] ) : '';
    17861788
    1787         // Build filter arguments (with large per_page for export)
     1789
    17881790        $args = array(
    17891791            'log_type'   => $log_type,
     
    17931795            'date_from'  => $date_from,
    17941796            'date_to'    => $date_to,
    1795             'per_page'   => 10000, // Large number to get most/all logs
     1797            'per_page'   => 10000,
    17961798            'page'       => 1
    17971799        );
    17981800
    1799         // Get all logs with the specified filters
     1801
    18001802        $logs_data = Iron_Security_Logger::get_logs( $args );
    18011803        $logs      = $logs_data['logs'];
    18021804
    1803         // Set headers for CSV download
     1805
    18041806        header( 'Content-Type: text/csv' );
    18051807        header( 'Content-Disposition: attachment; filename="iron-security-logs-' . date( 'Y-m-d' ) . '.csv"' );
     
    18071809        header( 'Expires: 0' );
    18081810
    1809         // Create a file pointer connected to the output stream
     1811
    18101812        $output = fopen( 'php://output', 'w' );
    18111813
    1812         // Output the column headings
     1814
    18131815        fputcsv( $output,
    18141816            array( 'ID', 'Date', 'Type', 'Username', 'User ID', 'IP Address', 'Message', 'Status', 'Extra Data' ) );
    18151817
    1816         // Output each row of the data
     1818
    18171819        foreach ( $logs as $log ) {
    18181820            $log_type_labels = Iron_Security_Logger::get_log_types();
    18191821            $log_type_label  = isset( $log_type_labels[ $log['log_type'] ] ) ? $log_type_labels[ $log['log_type'] ] : $log['log_type'];
    18201822
    1821             // Format extra data for CSV
     1823
    18221824            $extra_data = '';
    18231825            if ( ! empty( $log['extra_data'] ) ) {
     
    18421844        }
    18431845
    1844         // Close the file pointer
     1846
    18451847        fclose( $output );
    18461848        exit;
     
    18951897        }
    18961898
    1897         // Map the JavaScript camelCase to the database option name
     1899
    18981900        $option_map = [
    18991901            'contentTypeOptions'      => 'wpironis_security_header_content_type_options',
     
    19141916        $option_name = $option_map[ $header_name ];
    19151917
    1916         // Get current options
     1918
    19171919        $options = get_option( 'wpironis_options', array() );
    19181920
    1919         // Update the specific security header setting
     1921
    19201922        $options[ $option_name ] = $enabled ? 1 : 0;
    19211923
    1922         // Save the updated options
     1924
    19231925        $updated = update_option( 'wpironis_options', $options );
    19241926
    1925         // Generate a friendly name for the header for use in messages
     1927
    19261928        $header_friendly_names = [
    19271929            'contentTypeOptions'      => 'X-Content-Type-Options',
     
    19451947            ) );
    19461948        } else {
    1947             // Check if the value was actually the same (no update needed)
     1949
    19481950            $current_options = get_option( 'wpironis_options' );
    19491951            if ( isset( $current_options[ $option_name ] ) && $current_options[ $option_name ] === ( $enabled ? 1 : 0 ) ) {
     
    19751977        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    19761978
    1977         // Get current settings
     1979
    19781980        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    19791981
    1980         // Update the limit admins setting
     1982
    19811983        $options['enable_limit_admins'] = $enabled ? '1' : '0';
    19821984
    1983         // Save settings
     1985
    19841986        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    19851987
     
    19901992            );
    19911993
    1992             // If the feature is enabled, include admin count and users list
     1994
    19931995            if ( $enabled ) {
    19941996                $admin_users = get_users( array(
     
    20142016            wp_send_json_success( $response_data );
    20152017        } else {
    2016             // Check if the value was actually the same (no update needed)
     2018
    20172019            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    20182020            if ( isset( $current_options['enable_limit_admins'] ) &&
     
    20232025                );
    20242026
    2025                 // If the feature is enabled, include admin count and users list
     2027
    20262028                if ( $enabled ) {
    20272029                    $admin_users = get_users( array(
     
    20642066        }
    20652067
    2066         // Validate and sanitize inputs
     2068
    20672069        $max_admins    = isset( $_POST['max_admins'] ) ? absint( $_POST['max_admins'] ) : 1;
    20682070        $fallback_role = isset( $_POST['fallback_role'] ) ? sanitize_text_field( $_POST['fallback_role'] ) : 'editor';
    20692071
    2070         // Ensure max_admins is at least 1
     2072
    20712073        if ( $max_admins < 1 ) {
    20722074            $max_admins = 1;
    20732075        }
    20742076
    2075         // Validate fallback role is a valid WordPress role
     2077
    20762078        $valid_roles = array( 'editor', 'author', 'contributor', 'subscriber' );
    20772079        if ( ! in_array( $fallback_role, $valid_roles ) ) {
     
    20792081        }
    20802082
    2081         // Get current settings
     2083
    20822084        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    20832085
    2084         // Update the limit admins settings
     2086
    20852087        $options['wpironis_max_admins']          = (string) $max_admins;
    20862088        $options['wpironis_admin_fallback_role'] = $fallback_role;
    20872089
    2088         // Save settings
     2090
    20892091        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    20902092
    2091         // Get admin users for the response
     2093
    20922094        $admin_users = get_users( array(
    20932095            'role' => 'administrator',
    20942096        ) );
    20952097
    2096         // Count admins
     2098
    20972099        $admin_count = count( $admin_users );
    20982100
    2099         // Prepare the user data to return
     2101
    21002102        $users = array();
    21012103        foreach ( $admin_users as $user ) {
     
    21092111
    21102112        if ( $updated ) {
    2111             // Enforce the limit now by checking current admin count
     2113
    21122114            $this->wpironis_enforce_admin_limit_now();
    21132115
    2114             // Get updated admin count after enforcement
     2116
    21152117            $updated_admin_users = get_users( array(
    21162118                'role' => 'administrator',
     
    21182120            $updated_admin_count = count( $updated_admin_users );
    21192121
    2120             // Prepare updated user list
     2122
    21212123            $updated_users = array();
    21222124            foreach ( $updated_admin_users as $user ) {
     
    21382140            ) );
    21392141        } else {
    2140             // Check if the value was actually the same (no update needed)
     2142
    21412143            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    21422144            if ( isset( $current_options['wpironis_max_admins'] ) &&
     
    21652167     */
    21662168    public function wpironis_enforce_admin_limit( $user_id, $role, $old_roles ) {
    2167         // Check if the new role is administrator
     2169
    21682170        if ( $role !== 'administrator' ) {
    21692171            return;
    21702172        }
    21712173
    2172         // Get the settings
     2174
    21732175        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    21742176
    2175         // Check if the feature is enabled
     2177
    21762178        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    21772179            return;
    21782180        }
    21792181
    2180         // Get maximum allowed admins
     2182
    21812183        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    21822184
    2183         // Get the fallback role
     2185
    21842186        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    21852187
    2186         // Count current admins
     2188
    21872189        $admin_count = $this->wpironis_count_admins();
    21882190
    2189         // If the max has been reached, change this user's role to the fallback
     2191
    21902192        if ( $admin_count > $max_admins ) {
    2191             // Skip the current hook to avoid infinite loop
     2193
    21922194            remove_action( 'set_user_role', array( $this, 'wpironis_enforce_admin_limit' ), 10 );
    21932195
    2194             // Downgrade the user to the fallback role
     2196
    21952197            $user = new WP_User( $user_id );
    21962198            $user->set_role( $fallback_role );
    21972199
    2198             // Log the event safely
     2200
    21992201            $this->safe_log_security_event(
    22002202                'admin_limit_enforced',
     
    22072209            );
    22082210
    2209             // Add admin notice
     2211
    22102212            add_action( 'admin_notices', function () use ( $user, $fallback_role, $max_admins ) {
    22112213                echo '<div class="notice notice-warning is-dismissible">';
     
    22202222            } );
    22212223
    2222             // Re-add the hook
     2224
    22232225            add_action( 'set_user_role', array( $this, 'wpironis_enforce_admin_limit' ), 10, 3 );
    22242226        }
     
    22312233     */
    22322234    public function wpironis_check_admin_limit_on_register( $user_id ) {
    2233         // Get the user object
     2235
    22342236        $user = new WP_User( $user_id );
    22352237
    2236         // Check if the user is being registered as an admin
     2238
    22372239        if ( ! in_array( 'administrator', $user->roles ) ) {
    22382240            return;
    22392241        }
    22402242
    2241         // Get the settings
     2243
    22422244        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    22432245
    2244         // Check if the feature is enabled
     2246
    22452247        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    22462248            return;
    22472249        }
    22482250
    2249         // Get maximum allowed admins
     2251
    22502252        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    22512253
    2252         // Get the fallback role
     2254
    22532255        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    22542256
    2255         // Count current admins
     2257
    22562258        $admin_count = $this->wpironis_count_admins();
    22572259
    2258         // If the max has been reached, change this user's role to the fallback
     2260
    22592261        if ( $admin_count > $max_admins ) {
    2260             // Downgrade the user to the fallback role
     2262
    22612263            $user->set_role( $fallback_role );
    22622264
    2263             // Log the event safely
     2265
    22642266            $this->safe_log_security_event(
    22652267                'admin_limit_enforced',
     
    22812283     */
    22822284    private function safe_log_security_event( $event_type, $message ) {
    2283         // Try to use the Logger class if it exists and has the method
     2285
    22842286        if ( class_exists( 'Iron_Security_Logger' ) && method_exists( 'Iron_Security_Logger', 'log_security_event' ) ) {
    22852287            Iron_Security_Logger::log_security_event( $event_type, $message );
    22862288        } else {
    2287             // Fallback logging to the WordPress error log
     2289
    22882290            error_log( sprintf( '[Iron Security] %s: %s', $event_type, $message ) );
    22892291
    2290             // Attempt to log to a custom database table if it exists
     2292
    22912293            global $wpdb;
    22922294            $table_name = $wpdb->prefix . 'iron_security_logs';
    22932295
    2294             // Check if the table exists before attempting to insert
     2296
    22952297            if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) === $table_name ) {
    22962298                $wpdb->insert(
     
    23302332     */
    23312333    private function wpironis_enforce_admin_limit_now() {
    2332         // Get the settings
     2334
    23332335        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    23342336
    2335         // Check if the feature is enabled
     2337
    23362338        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    23372339            return;
    23382340        }
    23392341
    2340         // Get maximum allowed admins
     2342
    23412343        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    23422344
    2343         // Get the fallback role
     2345
    23442346        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    23452347
    2346         // Get all admins
     2348
    23472349        $admin_users = get_users( array(
    23482350            'role'    => 'administrator',
     
    23512353        ) );
    23522354
    2353         // Skip if we have fewer admins than the limit
     2355
    23542356        if ( count( $admin_users ) <= $max_admins ) {
    23552357            return;
    23562358        }
    23572359
    2358         // Keep track of how many admins we've processed
     2360
    23592361        $admin_count = 0;
    23602362
    2361         // Process each admin user
     2363
    23622364        foreach ( $admin_users as $admin_user ) {
    23632365            $admin_count ++;
    23642366
    2365             // Skip the first max_admins administrators (ordered by ID)
     2367
    23662368            if ( $admin_count <= $max_admins ) {
    23672369                continue;
    23682370            }
    23692371
    2370             // Downgrade the user to the fallback role
     2372
    23712373            $admin_user->set_role( $fallback_role );
    23722374
    2373             // Log the event safely
     2375
    23742376            $this->safe_log_security_event(
    23752377                'admin_limit_enforced',
     
    24152417        }
    24162418
    2417         // Check if this is a user create/update request
     2419
    24182420        $route = $request->get_route();
    24192421        if ( strpos( $route, '/wp/v2/users' ) === false ) {
     
    24212423        }
    24222424
    2423         // Check if operation involves setting a role to administrator
     2425
    24242426        $params = $request->get_params();
    24252427        if ( ! isset( $params['roles'] ) || ! in_array( 'administrator', (array) $params['roles'] ) ) {
     
    24272429        }
    24282430
    2429         // Get the settings
     2431
    24302432        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    24312433
    2432         // Check if the feature is enabled
     2434
    24332435        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    24342436            return $response;
    24352437        }
    24362438
    2437         // Get maximum allowed admins
     2439
    24382440        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    24392441
    2440         // Get the fallback role
     2442
    24412443        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    24422444
    2443         // Count current admins
     2445
    24442446        $admin_count = $this->wpironis_count_admins();
    24452447
    2446         // Get the user ID from the request
     2448
    24472449        $user_id = isset( $params['id'] ) ? $params['id'] : null;
    24482450
    2449         // For user creation, the ID will be in the response
     2451
    24502452        if ( ! $user_id && isset( $response->data['id'] ) ) {
    24512453            $user_id = $response->data['id'];
    24522454        }
    24532455
    2454         // Get the user to check if they were already an admin
     2456
    24552457        $user      = get_user_by( 'id', $user_id );
    24562458        $was_admin = $user && in_array( 'administrator', $user->roles );
    24572459
    2458         // If the user was not already an admin and the limit has been reached
     2460
    24592461        if ( ! $was_admin && $admin_count >= $max_admins ) {
    2460             // Update the user to the fallback role
     2462
    24612463            $user = new WP_User( $user_id );
    24622464            $user->set_role( $fallback_role );
    24632465
    2464             // Log the event safely
     2466
    24652467            $this->safe_log_security_event(
    24662468                'admin_limit_enforced_api',
     
    24732475            );
    24742476
    2475             // Update the response to reflect the actual role
     2477
    24762478            if ( isset( $response->data['roles'] ) ) {
    24772479                $response->data['roles'] = array( $fallback_role );
    24782480            }
    24792481
    2480             // Add a message to the response
     2482
    24812483            if ( ! isset( $response->data['iron_security_message'] ) ) {
    24822484                $response->data['iron_security_message'] = sprintf(
     
    24962498     */
    24972499    public function wpironsec_get_admin_info() {
    2498         // Verify nonce
     2500
    24992501        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'iron_security_nonce' ) ) {
    25002502            wp_send_json_error( 'Invalid security token sent.' );
     
    25022504        }
    25032505
    2504         // Check if user is authorized
     2506
    25052507        if ( ! current_user_can( 'manage_options' ) ) {
    25062508            wp_send_json_error( 'You do not have permission to perform this action.' );
     
    25082510        }
    25092511
    2510         // Get administrator users
     2512
    25112513        $admin_users = get_users( array(
    25122514            'role' => 'administrator',
    25132515        ) );
    25142516
    2515         // Prepare the user data to return
     2517
    25162518        $users = array();
    25172519        foreach ( $admin_users as $user ) {
     
    25242526        }
    25252527
    2526         // Count
     2528
    25272529        $count = count( $admin_users );
    25282530
    2529         // Send the response
     2531
    25302532        wp_send_json_success( array(
    25312533            'count'   => $count,
     
    25672569        }
    25682570
    2569         // Get the differences to determine what was updated
     2571
    25702572        $changes = array();
    25712573
    2572         // Check email change
     2574
    25732575        if ( $old_user_data->user_email !== $user->user_email ) {
    25742576            $changes['email'] = array(
     
    25782580        }
    25792581
    2580         // Check display name change
     2582
    25812583        if ( $old_user_data->display_name !== $user->display_name ) {
    25822584            $changes['display_name'] = array(
     
    25862588        }
    25872589
    2588         // Check URL change
     2590
    25892591        if ( $old_user_data->user_url !== $user->user_url ) {
    25902592            $changes['url'] = array(
     
    25952597
    25962598        if ( empty( $changes ) ) {
    2597             // Log general profile update if no specific changes detected
     2599
    25982600            $message = sprintf( 'User "%s" profile was updated', $user->user_login );
    25992601        } else {
    2600             // Log specific changes
     2602
    26012603            $fields  = array_keys( $changes );
    26022604            $message = sprintf( 'User "%s" profile was updated. Changed fields: %s',
     
    28222824        global $wp_version;
    28232825
    2824         // Only log this occasionally to avoid excessive logs
     2826
    28252827        $last_check = get_option( 'wpironis_last_version_check_log', 0 );
    2826         if ( time() - $last_check < 86400 ) { // Once per day max
     2828        if ( time() - $last_check < 86400 ) {
    28272829            return;
    28282830        }
     
    28552857        global $pagenow;
    28562858
    2857         // Only log certain admin actions to avoid excessive logging
     2859
    28582860        if ( ! is_admin() || ! current_user_can( 'edit_posts' ) ) {
    28592861            return;
     
    28622864        $action = isset( $_REQUEST['action'] ) ? sanitize_text_field( $_REQUEST['action'] ) : '';
    28632865
    2864         // Skip logging for common, routine actions
     2866
    28652867        $skip_actions = array( 'heartbeat', 'wp-refresh-post-lock', 'ajax-tag-search' );
    28662868        if ( in_array( $action, $skip_actions ) ) {
     
    28682870        }
    28692871
    2870         // Log specific high-value actions only
     2872
    28712873        $important_actions = array(
    28722874            'update'            => 'Update',
     
    28932895            $details = array(
    28942896                'page'       => $pagenow,
    2895                 'query_args' => $_GET // Including all query parameters for context
     2897                'query_args' => $_GET
    28962898            );
    28972899
     
    28992901        }
    29002902
    2901         // Log access to sensitive admin pages
     2903
    29022904        $sensitive_pages = array(
    29032905            'options.php'                            => 'Settings Update',
     
    29222924
    29232925        foreach ( $sensitive_pages as $page => $description ) {
    2924             // Check if we're on this page or if page with query matches
     2926
    29252927            if ( $pagenow === $page || ( strpos( $page, '?' ) !== false && strpos( $_SERVER['REQUEST_URI'],
    29262928                        $page ) !== false ) ) {
     
    29922994     */
    29932995    public function detect_frame_embedding() {
    2994         // Only run this occasionally to avoid performance issues
     2996
    29952997        if ( ! is_admin() || mt_rand( 1, 10 ) !== 1 ) {
    29962998            return;
     
    30023004            $home_url = home_url();
    30033005
    3004             // Check if the referer is from a different domain
     3006
    30053007            if ( strpos( $referer, $site_url ) !== 0 && strpos( $referer, $home_url ) !== 0 ) {
    3006                 // This could potentially be a clickjacking attempt
     3008
    30073009                $referer_host = parse_url( $referer, PHP_URL_HOST );
    30083010                $site_host    = parse_url( $site_url, PHP_URL_HOST );
     
    30203022     */
    30213023    public function detect_suspicious_requests() {
    3022         // Only run this occasionally to avoid performance issues
     3024
    30233025        if ( mt_rand( 1, 10 ) !== 1 ) {
    30243026            return;
    30253027        }
    30263028
    3027         // Detect common attack patterns in URLs
     3029
    30283030        $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';
    30293031        $user_agent  = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
    30303032
    3031         // List of suspicious URL patterns with attack type and whether it's a regex
     3033
    30323034        $suspicious_patterns = array(
    30333035            'eval\('             => array( 'type' => 'PHP Injection', 'is_regex' => true ),
     
    30773079        }
    30783080
    3079         // Check for known malicious or scanning user agents
     3081
    30803082        $malicious_agents = array(
    30813083            'nikto'           => 'Security Scanner',
     
    31023104                    sprintf( 'Detected %s user agent: %s', $agent_type, $agent )
    31033105                );
    3104                 break; // Only log once per request
     3106                break;
    31053107            }
    31063108        }
     
    31113113     */
    31123114    public function monitor_request_methods() {
    3113         // Only monitor in admin area or for login/register pages
     3115
    31143116        if ( ! is_admin() && ! in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ) ) ) {
    31153117            return;
     
    31183120        $method = isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : '';
    31193121
    3120         // Unusual request methods for WordPress admin
     3122
    31213123        $unusual_methods = array( 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH' );
    31223124
     
    31563158     */
    31573159    public function setup_rest_logging() {
    3158         // Add a filter to log API requests
     3160
    31593161        add_filter( 'rest_pre_dispatch', array( $this, 'log_rest_api_request' ), 10, 3 );
    31603162    }
     
    31723174        $route = $request->get_route();
    31733175
    3174         // Skip some common, noisy routes
     3176
    31753177        $skip_routes = array(
    31763178            '/wp/v2/users/me',
     
    31823184        }
    31833185
    3184         // Sensitive routes that should always be logged
     3186
    31853187        $sensitive_routes = array(
    31863188            '/wp/v2/users',
     
    32013203        }
    32023204
    3203         // Always log writes (POST, PUT, DELETE) or sensitive routes
     3205
    32043206        $method = $request->get_method();
    32053207        if ( $is_sensitive || in_array( $method, array( 'POST', 'PUT', 'DELETE', 'PATCH' ) ) ) {
     
    32433245     */
    32443246    public function log_http_api_errors( $response, $context, $class, $parsed_args, $url ) {
    3245         // Only log errors
     3247
    32463248        if ( ! is_wp_error( $response ) ) {
    32473249            return;
     
    32513253        $error_message = $response->get_error_message();
    32523254
    3253         // Don't log common timeout errors which may be normal
     3255
    32543256        if ( strpos( $error_code, 'timeout' ) !== false ) {
    32553257            return;
     
    32633265     */
    32643266    public function detect_security_scanners() {
    3265         // Only run this check occasionally to avoid performance impact
     3267
    32663268        if ( mt_rand( 1, 20 ) !== 1 ) {
    32673269            return;
    32683270        }
    32693271
    3270         // Check for rapid-fire requests from the same IP
     3272
    32713273        $ip = $this->get_ip_address();
    32723274
    3273         // Get the last time we recorded this IP
     3275
    32743276        $last_checked = get_transient( 'wpironis_ip_check_' . md5( $ip ) );
    32753277        $now          = time();
    32763278
    32773279        if ( $last_checked ) {
    3278             // If this IP made a request very recently (within 1 second)
     3280
    32793281            if ( $now - $last_checked < 1 ) {
    3280                 // Increment the counter for this IP
     3282
    32813283                $count = get_transient( 'wpironis_ip_count_' . md5( $ip ) );
    32823284                $count = $count ? $count + 1 : 1;
    3283                 set_transient( 'wpironis_ip_count_' . md5( $ip ), $count, 300 ); // Keep count for 5 minutes
    3284 
    3285                 // If we've seen too many rapid requests, log it as a scanner
     3285                set_transient( 'wpironis_ip_count_' . md5( $ip ), $count, 300 );
     3286
     3287
    32863288                if ( $count > 10 ) {
    32873289                    Iron_Security_Logger::log_suspicious_behavior(
     
    32903292                    );
    32913293
    3292                     // Reset counter to avoid logging repeatedly
     3294
    32933295                    set_transient( 'wpironis_ip_count_' . md5( $ip ), 0, 300 );
    32943296                }
     
    32963298        }
    32973299
    3298         // Update the last checked time for this IP
    3299         set_transient( 'wpironis_ip_check_' . md5( $ip ), $now, 300 ); // Keep for 5 minutes
     3300
     3301        set_transient( 'wpironis_ip_check_' . md5( $ip ), $now, 300 );
    33003302    }
    33013303
     
    33053307     */
    33063308    public function wpironis_handle_admin_id_protection_toggle() {
    3307         // Verify nonce
     3309
    33083310        if ( ! check_ajax_referer( 'iron_security_nonce', 'nonce', false ) ) {
    33093311            wp_send_json_error( 'Invalid security token. Please refresh the page and try again.' );
     
    33203322        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    33213323
    3322         // Get current settings
     3324
    33233325        $loginlogout_settings = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    33243326
    3325         // Update the settings
     3327
    33263328        $loginlogout_settings['enable_admin_id_protection'] = $enabled ? '1' : '0';
    33273329
    3328         $admin_id = '1'; // Default admin ID
    3329 
    3330         // If enabling the feature
     3330        $admin_id = '1';
     3331
     3332
    33313333        if ( $enabled ) {
    3332             // Check if we already have a stored admin ID in settings
     3334
    33333335            if ( ! empty( $loginlogout_settings['current_admin_id'] ) ) {
    33343336                $admin_id = $loginlogout_settings['current_admin_id'];
    33353337                $message  = 'Admin ID protection enabled. Using current administrator ID: ' . $admin_id;
    33363338
    3337                 // Log this security event
     3339
    33383340                $this->safe_log_security_event(
    33393341                    'settings_change',
     
    33423344                    'success'
    33433345                );
    3344             } // Check if admin with ID 1 still exists
     3346            }
    33453347            elseif ( ! get_userdata( 1 ) ) {
    3346                 // Admin ID 1 doesn't exist, try to find the first admin user
     3348
    33473349                $admins = get_users( array( 'role' => 'administrator', 'number' => 1 ) );
    33483350                if ( ! empty( $admins ) ) {
     
    33513353                    $message                                  = 'Admin ID protection enabled. Using existing administrator ID: ' . $admin_id;
    33523354
    3353                     // Log this security event
     3355
    33543356                    $this->safe_log_security_event(
    33553357                        'settings_change',
     
    33593361                    );
    33603362                } else {
    3361                     // No admin users found - this is unlikely but handle it
     3363
    33623364                    $loginlogout_settings['enable_admin_id_protection'] = '0';
    33633365                    wp_send_json_error( 'No administrator accounts found.' );
     
    33653367                    return;
    33663368                }
    3367             } // If admin ID 1 exists and needs to be changed
     3369            }
    33683370            else {
    33693371                try {
     
    33803382                        $message = 'Admin ID protection enabled. Administrator ID changed from 1 to ' . $admin_id;
    33813383                    } else {
    3382                         // If we couldn't change the ID, disable the feature
     3384
    33833385                        $loginlogout_settings['enable_admin_id_protection'] = '0';
    33843386                        wp_send_json_error( 'Failed to change admin ID. Please try again.' );
     
    33943396            }
    33953397        } else {
    3396             // When disabling, just update the setting but don't change the ID back
    3397             // since that would be complex and potentially disruptive
     3398
     3399
    33983400            $admin_id = ! empty( $loginlogout_settings['current_admin_id'] ) ? $loginlogout_settings['current_admin_id'] : '1';
    33993401            $message  = 'Admin ID protection disabled. Note that the admin ID will remain changed.';
    34003402
    3401             // Log this security event
     3403
    34023404            $this->safe_log_security_event(
    34033405                'settings_change',
     
    34083410        }
    34093411
    3410         // Save updated settings
     3412
    34113413        update_option( 'wpironis_plugin_settings_loginlogout', $loginlogout_settings );
    34123414
    3413         // Return success with the admin ID
     3415
    34143416        wp_send_json_success( array(
    34153417            'message'  => $message,
     
    34263428        global $wpdb;
    34273429
    3428         // First check if user ID 1 exists
     3430
    34293431        $user = get_userdata( 1 );
    34303432        if ( ! $user ) {
     
    34343436        }
    34353437
    3436         // Check if user is an administrator
     3438
    34373439        if ( ! in_array( 'administrator', $user->roles ) ) {
    34383440            error_log( 'Iron Security: Cannot change admin ID - User ID 1 is not an administrator' );
     
    34413443        }
    34423444
    3443         // Start a transaction
     3445
    34443446        $wpdb->query( 'START TRANSACTION' );
    34453447
    34463448        try {
    3447             // Get the highest user ID in the database
     3449
    34483450            $highest_id = $wpdb->get_var( "SELECT MAX(ID) FROM {$wpdb->users}" );
    34493451
    3450             // New ID will be highest + a random offset between 100-999
     3452
    34513453            $new_id = (int) $highest_id + 1;
    34523454
    3453             // Update user ID in users table
     3455
    34543456            $result = $wpdb->query( $wpdb->prepare(
    34553457                "UPDATE {$wpdb->users} SET ID = %d WHERE ID = 1",
     
    34613463            }
    34623464
    3463             // Update user ID in usermeta table
     3465
    34643466            $result = $wpdb->query( $wpdb->prepare(
    34653467                "UPDATE {$wpdb->usermeta} SET user_id = %d WHERE user_id = 1",
     
    34713473            }
    34723474
    3473             // Update user ID in posts table (for post author)
     3475
    34743476            $result = $wpdb->query( $wpdb->prepare(
    34753477                "UPDATE {$wpdb->posts} SET post_author = %d WHERE post_author = 1",
     
    34813483            }
    34823484
    3483             // Update user ID in comments table
     3485
    34843486            $result = $wpdb->query( $wpdb->prepare(
    34853487                "UPDATE {$wpdb->comments} SET user_id = %d WHERE user_id = 1",
     
    34913493            }
    34923494
    3493             // Additional tables to check for user_id references
    3494             // WooCommerce orders if present
     3495
     3496
    34953497            if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_orders'" ) ) {
    34963498                $wpdb->query( $wpdb->prepare(
     
    35003502            }
    35013503
    3502             // If using BuddyPress
     3504
    35033505            if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}bp_activity'" ) ) {
    35043506                $wpdb->query( $wpdb->prepare(
     
    35083510            }
    35093511
    3510             // If we're here, everything succeeded
     3512
    35113513            $wpdb->query( 'COMMIT' );
    35123514
    3513             // Clear caches
     3515
    35143516            wp_cache_delete( 1, 'users' );
    35153517            wp_cache_delete( 1, 'user_meta' );
     
    35173519            wp_cache_delete( $new_id, 'user_meta' );
    35183520
    3519             // Return the new ID
     3521
    35203522            return (string) $new_id;
    35213523        } catch ( Exception $e ) {
    3522             // Something went wrong, rollback
     3524
    35233525            $wpdb->query( 'ROLLBACK' );
    35243526
    3525             // Log the error
     3527
    35263528            error_log( 'Iron Security: Failed to change admin ID: ' . $e->getMessage() );
    35273529
    3528             // Rethrow the exception
     3530
    35293531            throw $e;
    35303532        }
     
    35783580        }
    35793581
    3580         // Correctly interpret "true" or "false" string from JS
     3582
    35813583        $enabled = isset( $_POST['enabled'] ) && $_POST['enabled'] === 'true';
    35823584
    3583         // Save proper value to DB
     3585
    35843586        $general_settings                           = get_option( 'wpironis_options', array() );
    35853587        $general_settings['wpironis_block_ai_bots'] = $enabled ? 1 : 0;
    35863588        update_option( 'wpironis_options', $general_settings );
    35873589
    3588         // Modify .htaccess
     3590
    35893591        $this->general_security->modifyHtaccessForAiBots( $enabled );
    35903592
    3591         // Prepare response message
     3593
    35923594        $message = $enabled
    35933595            ? __( 'AI bot blocking has been enabled. Your site is now protected from AI bot crawlers.',
     
    36163618        }
    36173619
    3618         // Fallback to glob if manifest doesn't exist or entry not found
     3620
    36193621        $dist_path = plugin_dir_path( __FILE__ ) . 'js/dist/';
    36203622        $files     = glob( $dist_path . $base_name . '.*.js' );
     
    36263628        }
    36273629
    3628         // Final fallback to non-hashed filename
     3630
    36293631        return $base_name . '.bundle.js';
    36303632    }
    36313633
     3634    /**
     3635     * Handle toggle for changing admin username
     3636     */
     3637    public function wpironis_handle_change_admin_username_toggle() {
     3638
     3639        if ( ! check_ajax_referer( 'iron_security_nonce', 'nonce', false ) ) {
     3640            wp_send_json_error( 'Invalid security token. Please refresh the page and try again.' );
     3641            return;
     3642        }
     3643
     3644        if ( ! current_user_can( 'manage_options' ) ) {
     3645            wp_send_json_error( 'You do not have permission to change this setting.' );
     3646            return;
     3647        }
     3648
     3649        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
     3650
     3651
     3652        $loginlogout_settings = get_option( 'wpironis_plugin_settings_loginlogout', array() );
     3653
     3654
     3655        $loginlogout_settings['enable_change_admin_username'] = $enabled ? '1' : '0';
     3656
     3657
     3658        if ( $enabled && empty( $loginlogout_settings['wpironis_new_admin_username'] ) ) {
     3659            $loginlogout_settings['wpironis_new_admin_username'] = $this->wpironis_generate_random_username();
     3660        }
     3661
     3662
     3663        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $loginlogout_settings );
     3664
     3665
     3666        $admin_usernames = $this->wpironis_get_admin_usernames();
     3667
     3668        if ( $updated ) {
     3669            $message = 'Admin username protection setting updated successfully';
     3670            $status = 'success';
     3671
     3672            if ( $enabled && !empty( $admin_usernames ) ) {
     3673                $message = 'Admin username protection enabled. Insecure usernames detected: ' . implode(', ', $admin_usernames);
     3674                $status = 'warning';
     3675            }
     3676
     3677            wp_send_json_success( array(
     3678                'message' => $message,
     3679                'status' => $status,
     3680                'admin_usernames' => $admin_usernames
     3681            ) );
     3682        } else {
     3683            wp_send_json_error( 'Failed to update admin username protection setting' );
     3684        }
     3685    }
     3686
     3687    /**
     3688     * Handle saving new admin username
     3689     */
     3690    public function wpironis_handle_change_admin_username_save() {
     3691
     3692        if ( ! check_ajax_referer( 'iron_security_nonce', 'nonce', false ) ) {
     3693            wp_send_json_error( 'Invalid security token. Please refresh the page and try again.' );
     3694            return;
     3695        }
     3696
     3697        if ( ! current_user_can( 'manage_options' ) ) {
     3698            wp_send_json_error( 'You do not have permission to change admin username.' );
     3699            return;
     3700        }
     3701
     3702
     3703        $new_username = isset( $_POST['new_username'] ) ? sanitize_user( $_POST['new_username'] ) : '';
     3704
     3705        if ( empty( $new_username ) ) {
     3706            wp_send_json_error( 'Username cannot be empty.' );
     3707            return;
     3708        }
     3709
     3710
     3711        if ( !validate_username( $new_username ) ) {
     3712            wp_send_json_error( 'Invalid username. Please use only letters.' );
     3713            return;
     3714        }
     3715
     3716
     3717        if ( username_exists( $new_username ) ) {
     3718            wp_send_json_error( 'Username already exists. Please choose another username.' );
     3719            return;
     3720        }
     3721
     3722
     3723        $loginlogout_settings = get_option( 'wpironis_plugin_settings_loginlogout', array() );
     3724
     3725
     3726        $loginlogout_settings['wpironis_new_admin_username'] = $new_username;
     3727        update_option( 'wpironis_plugin_settings_loginlogout', $loginlogout_settings );
     3728
     3729
     3730        $admin_usernames = $this->wpironis_get_admin_usernames();
     3731
     3732
     3733        $changed_users = array();
     3734
     3735        if ( !empty( $admin_usernames ) ) {
     3736            foreach ( $admin_usernames as $username ) {
     3737                $user = get_user_by( 'login', $username );
     3738                if ( $user ) {
     3739
     3740                    $unique_username = $this->wpironis_get_unique_username( $new_username );
     3741
     3742
     3743                    global $wpdb;
     3744                    $wpdb->update(
     3745                        $wpdb->users,
     3746                        array( 'user_login' => $unique_username ),
     3747                        array( 'ID' => $user->ID )
     3748                    );
     3749
     3750
     3751                    $this->safe_log_security_event( 'username_change', sprintf(
     3752                        'Changed username from %s to %s for user ID %d',
     3753                        $username,
     3754                        $unique_username,
     3755                        $user->ID
     3756                    ));
     3757
     3758
     3759                    clean_user_cache( $user->ID );
     3760
     3761                    $changed_users[] = array(
     3762                        'old_username' => $username,
     3763                        'new_username' => $unique_username,
     3764                        'display_name' => $user->display_name
     3765                    );
     3766                }
     3767            }
     3768        }
     3769
     3770        if ( !empty( $changed_users ) ) {
     3771            wp_send_json_success( array(
     3772                'message' => count( $changed_users ) . ' admin username(s) changed successfully.',
     3773                'changed_users' => $changed_users
     3774            ) );
     3775        } else {
     3776            wp_send_json_success( array(
     3777                'message' => 'New admin username saved. No insecure admin usernames found to change.'
     3778            ) );
     3779        }
     3780    }
     3781
     3782    /**
     3783     * Generate a random username with letters only
     3784     */
     3785    private function wpironis_generate_random_username() {
     3786        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
     3787        $username = '';
     3788        $length = rand(8, 12);
     3789
     3790        for ( $i = 0; $i < $length; $i++ ) {
     3791            $username .= $chars[rand(0, strlen($chars) - 1)];
     3792        }
     3793
     3794
     3795        return $this->wpironis_get_unique_username( $username );
     3796    }
     3797
     3798    /**
     3799     * Get a unique username by adding a number suffix if needed
     3800     */
     3801    private function wpironis_get_unique_username( $username ) {
     3802        $original_username = $username;
     3803        $suffix = 1;
     3804       
     3805        while ( username_exists( $username ) ) {
     3806            $username = $original_username . $suffix;
     3807            $suffix++;
     3808        }
     3809       
     3810        return $username;
     3811    }
     3812
     3813    /**
     3814     * Get usernames of admin users with common/default usernames
     3815     */
     3816    private function wpironis_get_admin_usernames() {
     3817        $insecure_usernames = array( 'admin', 'administrator', 'wordpress' );
     3818        $found_usernames = array();
     3819       
     3820        foreach ( $insecure_usernames as $username ) {
     3821            $user = get_user_by( 'login', $username );
     3822            if ( $user && in_array( 'administrator', $user->roles ) ) {
     3823                $found_usernames[] = $username;
     3824            }
     3825        }
     3826       
     3827        return $found_usernames;
     3828    }
     3829
    36323830}
  • iron-security/tags/2.3.3/admin/js/components/Dashboard.jsx

    r3288628 r3300103  
    135135    );
    136136
    137     console.log(settings)
    138137
    139138    if (isLoading || !settings) {
  • iron-security/tags/2.3.3/admin/js/components/FileDirectoryProectionSettings.jsx

    r3288628 r3300103  
    1212
    1313    useEffect(() => {
    14         // Update settings when they change
     14
    1515        setIsPhpUploadBlocked(settings.wpironis_options?.wpironis_block_php_uploads === 1);
    1616        setIsDirectAccessPrevented(settings.wpironis_options?.wpironis_prevent_direct_access === 1);
  • iron-security/tags/2.3.3/admin/js/components/GeneralSettings.jsx

    r3288628 r3300103  
    1515
    1616    useEffect(() => {
    17         // Update all state values when settings change
     17
    1818        setIsXmlrpcEnabled(settings.wpironis_options?.wpironis_disable_xmlrpc === 1);
    1919        setIsWpVersionHidden(settings.wpironis_options?.wpironis_hide_wp_version === 1);
     
    5757
    5858            const data = await response.json();
    59             console.log('Response:', data); // Debug log
    6059
    6160            if (data.success) {
  • iron-security/tags/2.3.3/admin/js/components/HttpSecurityHeadersSettings.jsx

    r3288628 r3300103  
    1919
    2020    useEffect(() => {
    21         // Update header settings when settings change
     21
    2222        setHeaderSettings({
    2323            contentTypeOptions: settings.wpironis_options?.wpironis_security_header_content_type_options === 1,
  • iron-security/tags/2.3.3/admin/js/components/LoginLogoutSettings.jsx

    r3288628 r3300103  
    7272   
    7373    const [isLoadingAdminInfo, setIsLoadingAdminInfo] = useState(false);
     74
     75    const [isChangeAdminUsernameEnabled, setIsChangeAdminUsernameEnabled] = useState(
     76        loginLogoutSettings.enable_change_admin_username === '1'
     77    );
     78
     79    const [newAdminUsername, setNewAdminUsername] = useState(
     80        loginLogoutSettings.wpironis_new_admin_username || ''
     81    );
     82
     83    const [adminUsernameStatus, setAdminUsernameStatus] = useState(null);
    7484
    7585    const getAdminCountStatus = () => {
     
    103113    useEffect(() => {
    104114        const loginLogoutSettings = settings?.login_logout || {};
    105         // Update all state values when settings change
     115
    106116        setIsCustomUrlEnabled(loginLogoutSettings.enable_slug_change === '1');
    107117        setCustomUrl(loginLogoutSettings.wpironis_custom_login_slug || 'wp-admin');
     
    129139            setIsAdminIdProtectionEnabled(true);
    130140        }
     141
     142        if (loginLogoutSettings.enable_change_admin_username === '1') {
     143            setIsChangeAdminUsernameEnabled(true);
     144        }
     145
     146        if (loginLogoutSettings.wpironis_new_admin_username) {
     147            setNewAdminUsername(loginLogoutSettings.wpironis_new_admin_username);
     148        }
    131149    }, [settings]);
    132150
     
    221239            console.error('Error:', error);
    222240            showNotification(error.message || 'Failed to update custom URL setting', 'error');
    223             setIsCustomUrlEnabled(!enabled); // Revert the toggle
     241            setIsCustomUrlEnabled(!enabled);
    224242        } finally {
    225243            setIsSaving(false);
     
    282300            console.error('Error:', error);
    283301            showNotification(error.message || 'Failed to update session timeout setting', 'error');
    284             setIsSessionTimeoutEnabled(!enabled); // Revert the toggle
     302            setIsSessionTimeoutEnabled(!enabled);
    285303        } finally {
    286304            setIsSaving(false);
     
    546564            setIsSaving(false);
    547565        }
     566    };
     567
     568    const handleChangeAdminUsernameToggle = async (enabled) => {
     569        setIsSaving(true);
     570        try {
     571            const formData = new FormData();
     572            formData.append('action', 'iron_security_toggle_change_admin_username');
     573            formData.append('enabled', enabled);
     574            formData.append('nonce', settings.nonce);
     575
     576            const response = await fetch(ajaxurl, {
     577                method: 'POST',
     578                body: formData,
     579                credentials: 'same-origin'
     580            });
     581
     582            const data = await response.json();
     583
     584            if (data.success) {
     585                setIsChangeAdminUsernameEnabled(enabled);
     586               
     587                if (enabled && data.data.admin_usernames && data.data.admin_usernames.length > 0) {
     588                    setAdminUsernameStatus({
     589                        message: `Insecure admin usernames detected: ${data.data.admin_usernames.join(', ')}. Change them to improve security.`,
     590                        type: 'warning'
     591                    });
     592                   
     593
     594                    if (!newAdminUsername) {
     595                        generateRandomUsername();
     596                    }
     597                } else if (enabled) {
     598                    setAdminUsernameStatus({
     599                        message: 'No insecure admin usernames detected.',
     600                        type: 'info'
     601                    });
     602                } else {
     603                    setAdminUsernameStatus(null);
     604                }
     605               
     606                showNotification(data.data.message || 'Change admin username setting updated successfully');
     607            } else {
     608                throw new Error(data.data || 'Failed to update setting');
     609            }
     610        } catch (error) {
     611            console.error('Error:', error);
     612            showNotification(error.message || 'Failed to update change admin username setting', 'error');
     613            setIsChangeAdminUsernameEnabled(!enabled);
     614        } finally {
     615            setIsSaving(false);
     616        }
     617    };
     618
     619    const handleSaveAdminUsername = async () => {
     620        setIsSaving(true);
     621        try {
     622            const formData = new FormData();
     623            formData.append('action', 'iron_security_save_change_admin_username');
     624            formData.append('new_username', newAdminUsername);
     625            formData.append('nonce', settings.nonce);
     626
     627            const response = await fetch(ajaxurl, {
     628                method: 'POST',
     629                body: formData,
     630                credentials: 'same-origin'
     631            });
     632
     633            const data = await response.json();
     634
     635            if (data.success) {
     636                if (data.data.changed_users && data.data.changed_users.length > 0) {
     637                    const changedUsers = data.data.changed_users;
     638                    let message = 'The following admin usernames were changed:\n';
     639                   
     640                    changedUsers.forEach(user => {
     641                        message += `• ${user.old_username} → ${user.new_username}`;
     642                        if (user.display_name) {
     643                            message += ` (${user.display_name})`;
     644                        }
     645                        message += '\n';
     646                    });
     647                   
     648                    setAdminUsernameStatus({
     649                        message: 'Admin usernames successfully changed.',
     650                        type: 'info'
     651                    });
     652                   
     653                    showNotification(data.data.message || 'Admin usernames changed successfully');
     654                    setTimeout(() => {
     655                        alert(message);
     656                    }, 500);
     657                } else {
     658                    setAdminUsernameStatus({
     659                        message: 'New admin username saved. No insecure admin usernames found to change.',
     660                        type: 'info'
     661                    });
     662                    showNotification(data.data.message || 'New admin username saved successfully');
     663                }
     664            } else {
     665                throw new Error(data.data || 'Failed to save new admin username');
     666            }
     667        } catch (error) {
     668            console.error('Error:', error);
     669            showNotification(error.message || 'Failed to save new admin username', 'error');
     670        } finally {
     671            setIsSaving(false);
     672        }
     673    };
     674
     675    const generateRandomUsername = () => {
     676        const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
     677        let result = '';
     678        const length = 8 + Math.floor(Math.random() * 8);
     679        for (let i = 0; i < length; i++) {
     680            result += chars.charAt(Math.floor(Math.random() * chars.length));
     681        }
     682        setNewAdminUsername(result);
    548683    };
    549684
     
    831966                        </div>
    832967                    )}
     968
     969                    <SettingToggle
     970                        label="Change Default Admin Username"
     971                        description="Change admin username if you have accounts with usernames like 'admin' or 'administrator' to prevent targeted attacks."
     972                        checked={isChangeAdminUsernameEnabled}
     973                        onChange={handleChangeAdminUsernameToggle}
     974                        disabled={isSaving}
     975                    />
     976
     977                    {isChangeAdminUsernameEnabled && (
     978                        <div className="wpironis-custom-url-input">
     979                            <div className="admin-username-info">
     980                                <p className="description">
     981                                    <strong>Default admin usernames like 'admin' or 'administrator' are frequently targeted in brute force attacks.</strong>
     982                                    Change them to enhance your site's security.
     983                                </p>
     984                                {adminUsernameStatus && (
     985                                    <p className={`description ${adminUsernameStatus.type}`}>
     986                                        <span className={`dashicons dashicons-${adminUsernameStatus.type === 'warning' ? 'warning' : 'info'}`}></span>
     987                                        {adminUsernameStatus.message}
     988                                    </p>
     989                                )}
     990                                <div className="wpironis-input-group">
     991                                    <label htmlFor="new-admin-username">New Username:</label>
     992                                    <input
     993                                        type="text"
     994                                        id="new-admin-username"
     995                                        value={newAdminUsername}
     996                                        onChange={(e) => setNewAdminUsername(e.target.value.replace(/[^a-zA-Z]/g, ''))}
     997                                        placeholder="Enter new username (letters only)"
     998                                        disabled={isSaving}
     999                                    />
     1000                                    <button
     1001                                        className="button button-secondary"
     1002                                        onClick={generateRandomUsername}
     1003                                        disabled={isSaving}
     1004                                        style={{ marginLeft: '5px' }}
     1005                                    >
     1006                                        Generate Random
     1007                                    </button>
     1008                                    <button
     1009                                        className="button button-primary"
     1010                                        onClick={handleSaveAdminUsername}
     1011                                        disabled={isSaving || !newAdminUsername}
     1012                                        style={{ marginLeft: '5px' }}
     1013                                    >
     1014                                        {isSaving ? 'Saving...' : 'Save Username'}
     1015                                    </button>
     1016                                </div>
     1017                            </div>
     1018                        </div>
     1019                    )}
    8331020                </div>
    8341021            </div>
  • iron-security/tags/2.3.3/admin/js/dist/manifest.json

    r3296251 r3300103  
    11{
    2   "dashboard.js": "dashboard.d4938437720f6eface82.js",
     2  "dashboard.js": "dashboard.409171eb914877d3afe7.js",
    33  "vendors.js": "vendors.91782840b9e9337a9a5f.js"
    44}
  • iron-security/tags/2.3.3/includes/class-iron-security.php

    r3291538 r3300103  
    8484        $this->loader->add_action( 'admin_menu', $plugin_admin, 'wpironis_plugin_menu' );
    8585
    86         // Login Logout
     86
    8787        $this->loader->add_action( 'init', $plugin_admin, 'wpironis_redirect_login' );
    8888        $this->loader->add_action( 'admin_init', $plugin_admin, 'wpironis_plugin_settings_init' );
     
    116116        $this->loader->add_filter( 'wp_handle_upload_prefilter', $plugin_admin, 'wpironis_prevent_php_upload' );
    117117
    118         // AJAX handlers
     118
    119119        $this->loader->add_action( 'wp_ajax_iron_security_toggle_xmlrpc',
    120120            $plugin_admin,
     
    149149            'wpironis_handle_core_autoupdate_toggle' );
    150150
    151         // Custom admin URL AJAX handlers
     151
    152152        $this->loader->add_action( 'wp_ajax_iron_security_toggle_custom_url',
    153153            $plugin_admin,
     
    157157            'wpironis_handle_custom_url_save' );
    158158
    159         // Session timeout AJAX handlers
     159
    160160        $this->loader->add_action( 'wp_ajax_iron_security_toggle_session_timeout',
    161161            $plugin_admin,
     
    165165            'wpironis_handle_session_timeout_save' );
    166166
    167         // Limit login attempts AJAX handlers
     167
    168168        $this->loader->add_action( 'wp_ajax_iron_security_toggle_limit_login',
    169169            $plugin_admin,
     
    173173            'wpironis_handle_limit_login_save' );
    174174
    175         // Limit admins AJAX handlers
     175
    176176        $this->loader->add_action( 'wp_ajax_iron_security_toggle_limit_admins',
    177177            $plugin_admin,
     
    181181            'wpironis_handle_limit_admins_save' );
    182182
    183         // Admin ID protection AJAX handler
     183
    184184        $this->loader->add_action( 'wp_ajax_iron_security_toggle_admin_id_protection',
    185185            $plugin_admin,
    186186            'wpironis_handle_admin_id_protection_toggle' );
    187187
    188         // Admin role limitation hook
     188        $this->loader->add_action( 'wp_ajax_iron_security_toggle_change_admin_username',
     189            $plugin_admin,
     190            'wpironis_handle_change_admin_username_toggle' );
     191        $this->loader->add_action( 'wp_ajax_iron_security_save_change_admin_username',
     192            $plugin_admin,
     193            'wpironis_handle_change_admin_username_save' );
     194
    189195        $this->loader->add_action( 'set_user_role',
    190196            $plugin_admin,
     
    192198            10,
    193199            3 );
    194         // Hook into user creation/update for admin limit
     200
    195201        $this->loader->add_action( 'user_register',
    196202            $plugin_admin,
     
    199205            1 );
    200206
    201         // REST API filter for admin limit
    202207        $this->loader->add_filter( 'rest_request_after_callbacks',
    203208            $plugin_admin,
     
    216221            'wpironis_handle_user_enum_message_save' );
    217222
    218         // Also add filters for auto-updates on plugin initialization
     223
    219224        $options = get_option( 'wpironis_options', array() );
    220225
    221         // Configure plugin auto-updates based on saved setting
     226
    222227        if ( ! empty( $options['wpironis_enable_plugin_autoupdate'] ) && $options['wpironis_enable_plugin_autoupdate'] === 1 ) {
    223228            add_filter( 'auto_update_plugin', '__return_true' );
     
    226231        }
    227232
    228         // Configure core auto-updates based on saved setting
     233
    229234        if ( ! empty( $options['wpironis_enable_core_autoupdate'] ) && $options['wpironis_enable_core_autoupdate'] === 1 ) {
    230235            add_filter( 'allow_major_auto_core_updates', '__return_true' );
     
    302307        $this->loader->add_action( 'init', $plugin_public, 'wpironis_init' );
    303308
    304         // Add REST API restriction hooks
     309
    305310        $this->loader->add_filter( 'rest_authentication_errors', $plugin_public, 'wpironis_restrict_rest_api' );
    306311        $this->loader->add_filter( 'rest_endpoints', $plugin_public, 'wpironis_disable_rest_endpoints' );
  • iron-security/tags/2.3.3/iron-security.php

    r3296251 r3300103  
    1717 * Plugin URI:        https://wpiron.com
    1818 * Description:       Iron Security is a powerful WordPress security plugin to protect your site from common threats. Lock down your site with login protection, file security, and HTTP headers — all in one lightweight plugin.
    19  * Version:           2.3.2
     19 * Version:           2.3.3
    2020 * Author:            wpiron
    2121 * Author URI:        https://wpiron.com/
     
    3131}
    3232
    33 define( 'IRON_SECURITY_VERSION', '2.3.2' );
     33define( 'IRON_SECURITY_VERSION', '2.3.3' );
    3434
    3535function wpiisec_activate_iron_security() {
  • iron-security/trunk/README.txt

    r3296251 r3300103  
    55Requires at least: 4.7
    66Tested up to: 6.8
    7 Stable tag: 2.3.2
     7Stable tag: 2.3.3
    88Requires PHP: 7.0
    99License: GPLv2 or later
     
    3535- Change default Admin ID
    3636- Block user enumeration
     37- Change Default Admin Username
    3738
    3839**Files & Directory Protection**
     
    6566
    6667= What makes Iron Security different from other WordPress security plugins? =
    67 Iron Security is designed to be lightweight, fast, and focused on practical features that matter most for securing your WordPress site.
     68Iron Security is lightweight, fast, and focused on the most effective hardening techniques. Instead of bloated features, it delivers practical tools that directly protect your WordPress site from real threats.
    6869
    6970= Is Iron Security suitable for beginners? =
    70 Yes! Iron Security comes with an intuitive dashboard and clear explanations for each option. Whether you're a WordPress beginner or an experienced developer, you'll find it easy to use and configure.
    71 
    72 = How does the custom login URL help protect my site? =
    73 Changing the default `/wp-admin` or `/wp-login.php` URL makes it harder for bots and attackers to find your login page, reducing brute force attempts. You can set your own unique login slug in a few clicks from the plugin settings.
    74 
    75 = What happens when a user exceeds the allowed login attempts? =
    76 If a user exceeds the allowed number of login attempts, their IP will be temporarily blocked based on your configured lockout settings. You can customize the number of allowed attempts, lockout duration, and view attempt logs.
    77 
    78 = How does the Admin ID protection work? =
    79 By default, WordPress assigns user ID 1 to the first admin account — a known vulnerability targeted by bots. Iron Security lets you assign a different ID to your admin account, making it harder to guess and exploit.
    80 
    81 = Does Iron Security block XML-RPC and REST API? Why? =
    82 Yes, you can optionally disable XML-RPC and REST API — two common attack vectors. XML-RPC is often used in DDoS and brute force attacks, while REST API may expose user data. Disabling them improves security, especially if you don’t use them.
    83 
    84 = What are HTTP security headers and why should I enable them? =
    85 HTTP security headers like X-Frame-Options, Content-Security-Policy, and Strict-Transport-Security provide an extra layer of browser-based protection. They help prevent XSS, clickjacking, and other code injection attacks. Iron Security lets you enable them easily from the dashboard.
     71Absolutely. Iron Security features an intuitive dashboard with clear explanations for each setting. Whether you’re new to WordPress or a seasoned developer, you’ll find it easy to set up and use.
     72
     73= How does changing the login URL protect my site? =
     74By replacing the default /wp-admin or /wp-login.php URLs, you reduce the risk of automated brute force attacks. Bots and attackers can’t easily locate your login page, adding an extra layer of protection.
     75
     76= What happens if someone exceeds the allowed login attempts? =
     77Their IP will be temporarily blocked based on your configured settings. You can set how many failed attempts are allowed, how long the lockout lasts, and monitor the attempt logs directly from the plugin dashboard.
     78
     79= How does Admin ID protection work? =
     80WordPress assigns the first admin user an ID of 1 — a known target for attacks. Iron Security helps you avoid this by allowing you to assign a different user ID to your admin account, reducing exposure to targeted exploits.
     81
     82= Can Iron Security block XML-RPC and REST API access? =
     83Yes. These features can be optionally disabled. XML-RPC and REST API are common targets for attacks like brute force or user data enumeration. If you don’t use them, disabling them can significantly reduce your attack surface.
     84
     85= What are HTTP security headers, and why should I enable them? =
     86HTTP security headers such as X-Frame-Options, Content-Security-Policy, and Strict-Transport-Security enhance browser-level security. They help protect your site against XSS, clickjacking, and other injection-based attacks. Iron Security lets you enable these headers with just a few clicks.
    8687
    8788= Will Iron Security slow down my website? =
    88 Not at all. The plugin is built to be lightweight and uses efficient code practices. It doesn’t run background scans or heavy processes, so your site’s performance remains unaffected.
    89 
    90 = Can I use Iron Security on WooCommerce stores? =
    91 Absolutely. Iron Security is fully compatible with WooCommerce and protects your login area, admin panel, and core files without affecting your store’s functionality.
    92 
    93 = Where can I get support or report a bug? =
    94 You can submit issues or ask for help via the [support forum on WordPress.org](https://wordpress.org/support/plugin/iron-security/) or by contacting us directly at [https://wpiron.com](https://wpiron.com).
     89No. Iron Security is performance-focused and doesn't include heavy scans or background processes. It’s designed to secure your site without impacting loading speed or user experience.
     90
     91= Is Iron Security compatible with WooCommerce? =
     92Yes, Iron Security works seamlessly with WooCommerce. It protects your admin area, login forms, and critical files without interfering with your store's functionality or customer experience.
     93
     94= How can I get support or report a bug? =
     95You can reach out via the WordPress.org support forum: https://wordpress.org/support/plugin/iron-security/ or contact our team directly at https://wpiron.com.
    9596
    9697= How often is Iron Security updated? =
    97 We actively maintain and improve Iron Security. You can expect regular updates for new features, security patches, and WordPress compatibility improvements.
     98We actively maintain Iron Security to ensure compatibility with the latest WordPress releases. Expect regular updates with new features, security improvements, and bug fixes.
     99
     100= What is the benefit of disabling file editing in WordPress? =
     101Disabling the file editor in the admin panel prevents attackers from injecting malicious code directly into theme or plugin files if they gain admin access. This is a simple but effective hardening step.
     102
     103= What does hiding the WordPress version do? =
     104Iron Security removes the WordPress version from your website’s source code to reduce the risk of automated attacks targeting specific versions with known vulnerabilities.
     105
     106= How does Iron Security block AI crawlers? =
     107The plugin adds specific rules to your robots.txt file to block AI and data scraping bots, helping protect your content from unauthorized use and training.
     108
     109= What does "Limit number of administrators" mean? =
     110Limiting admin users helps reduce the number of high-privilege accounts on your site. The plugin notifies you if more than one admin account exists, encouraging better access control and role management.
     111
     112= How does session timeout protect my site? =
     113If a logged-in user is inactive for a certain period, they are automatically logged out. This prevents unauthorized access from unattended sessions on shared or public devices.
     114
     115= Can I block user enumeration? =
     116Yes. Iron Security prevents bots from discovering usernames through the `?author=` query parameter, a common tactic used in brute force and targeted attacks.
     117
     118= What does “Change default admin username” do? =
     119If your admin account uses "admin" as the username, it becomes an easy target. Iron Security helps you safely change this default to something unique and secure.
     120
     121= What does "Prevent direct file access" mean? =
     122The plugin restricts access to sensitive files like PHP templates and system files that should not be accessed directly, protecting your site from unauthorized requests.
     123
     124= Can I block PHP file uploads? =
     125Yes. Iron Security allows you to block PHP file uploads in forms and media to prevent malicious scripts from being uploaded to your site.
     126
     127= What are plugin and core auto-updates, and why enable them? =
     128Keeping your WordPress core and plugins updated is critical to staying secure. Iron Security allows you to enable automatic updates, so your site is always protected with the latest patches.
    98129
    99130
     
    111142== Changelog ==
    112143
     144= 2.3.3 =
     145* Add functionality to change default admin username
     146
    113147= 2.3.2 =
    114148* Gutenberg some blocks were disabled - fixed it.
  • iron-security/trunk/admin/class-iron-security-admin.php

    r3291538 r3300103  
    4747                plugin_dir_url( __FILE__ ) . 'css/admin.css',
    4848                array(),
    49                 time(),
     49                $this->version,
    5050                'all' );
    5151            wp_enqueue_style( $this->plugin_name . '-dashboard',
     
    5757                plugin_dir_url( __FILE__ ) . 'css/transitions.css',
    5858                array(),
    59                 time(),
     59                $this->version,
    6060                'all' );
    6161        }
     
    8181            plugin_dir_url( __FILE__ ) . 'js/iron-security-admin.js',
    8282            array( 'jquery', 'wp-element', 'wp-components' ),
    83 //          $this->version,
    84             time(),
     83            $this->version,
     84//          time(),
    8585            false
    8686        );
     
    9292                plugin_dir_url( __FILE__ ) . 'js/session-timeout.js',
    9393                array( 'jquery' ),
    94 //              $this->version,
    95                 time(),
     94                $this->version,
     95//              time(),
    9696                true
    9797            );
     
    114114                array( 'wp-element', 'wp-components', 'wp-i18n' ),
    115115                $this->version,
     116//                time(),
    116117                true
    117118            );
     
    122123                array( 'wp-element', 'wp-components', 'wp-i18n' ),
    123124                $this->version,
     125//                time(),
    124126                true
    125127            );
     
    171173                // Your content for login/logout tab
    172174                echo '<h3>Login/Logout Settings</h3>';
    173                 // add your logic here
    174             }
    175             // Add other tabs content conditions
     175
     176            }
     177
    176178            ?>
    177179        </div>
     
    223225        $sanitized_input = array();
    224226        foreach ( $input as $key => $value ) {
    225             // Special handling for checkbox/toggle fields
     227
    226228            if ( in_array( $key, array(
    227229                'wpironis_disable_xmlrpc',
     
    346348            $secret = get_user_meta( $user->ID, 'google_authenticator_secret', true );
    347349            if ( ! $secret ) {
    348                 return $user; // No 2FA setup for the user
     350                return $user;
    349351            }
    350352
     
    478480        }
    479481
    480         // When enabled is true, we want to disable XML-RPC
     482
    481483        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    482484        $options = get_option( 'wpironis_options', array() );
    483485
    484         // Set to 1 to disable XML-RPC when toggle is enabled
     486
    485487        $options['wpironis_disable_xmlrpc'] = $enabled ? 1 : 0;
    486488
     
    494496            ) );
    495497        } else {
    496             // Check if the value was actually the same (no update needed)
     498
    497499            $current_options = get_option( 'wpironis_options' );
    498500            if ( isset( $current_options['wpironis_disable_xmlrpc'] ) &&
     
    520522        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    521523
    522         // Get current options or initialize array
     524
    523525        $options = get_option( 'wpironis_options', array() );
    524526
    525         // Update the WordPress version hiding setting
     527
    526528        $options['wpironis_hide_wp_version'] = $enabled ? 1 : 0;
    527529
    528         // Save the updated options
     530
    529531        $updated = update_option( 'wpironis_options', $options );
    530532
     
    536538            ) );
    537539        } else {
    538             // Check if the value was actually the same (no update needed)
     540
    539541            $current_options = get_option( 'wpironis_options' );
    540542            if ( isset( $current_options['wpironis_hide_wp_version'] ) &&
     
    562564        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    563565
    564         // Get current options or initialize array
     566
    565567        $options = get_option( 'wpironis_options', array() );
    566568
    567         // Update the security headers setting
     569
    568570        $options['wpironis_security_headers'] = $enabled ? 1 : 0;
    569571
    570         // Save the updated options
     572
    571573        $updated = update_option( 'wpironis_options', $options );
    572574
     
    578580            ) );
    579581        } else {
    580             // Check if the value was actually the same (no update needed)
     582
    581583            $current_options = get_option( 'wpironis_options' );
    582584            if ( isset( $current_options['wpironis_security_headers'] ) &&
     
    604606        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    605607
    606         // Get current options or initialize array
     608
    607609        $options = get_option( 'wpironis_options', array() );
    608610
    609         // Update the PHP uploads blocking setting
     611
    610612        $options['wpironis_block_php_uploads'] = $enabled ? 1 : 0;
    611613
    612         // Save the updated options
     614
    613615        $updated = update_option( 'wpironis_options', $options );
    614616
    615         // Handle .htaccess rules
     617
    616618        if ( $enabled ) {
    617619            $this->wpironis_create_upload_protection();
     
    627629            ) );
    628630        } else {
    629             // Check if the value was actually the same (no update needed)
     631
    630632            $current_options = get_option( 'wpironis_options' );
    631633            if ( isset( $current_options['wpironis_block_php_uploads'] ) &&
     
    646648        $htaccess_path = $upload_dir['basedir'] . '/.htaccess';
    647649
    648         // Define the rules
     650
    649651        $rules = "
    650652# Iron Security
     
    675677";
    676678
    677         // Create or update .htaccess file
     679
    678680        if ( ! file_exists( $htaccess_path ) || is_writable( $htaccess_path ) ) {
    679681            file_put_contents( $htaccess_path, $rules, LOCK_EX );
    680682        }
    681683
    682         // Also create an index.php file to prevent directory listing
     684
    683685        $index_path = $upload_dir['basedir'] . '/index.php';
    684686        if ( ! file_exists( $index_path ) ) {
     
    697699
    698700    public function wpironis_prevent_php_upload( $file ) {
    699         // Get the current options
     701
    700702        $options = get_option( 'wpironis_options', array() );
    701703
    702         // Check if PHP upload blocking is enabled
     704
    703705        if ( ! isset( $options['wpironis_block_php_uploads'] ) || $options['wpironis_block_php_uploads'] !== 1 ) {
    704706            return $file;
    705707        }
    706708
    707         // Ensure the file name is set and is a string
     709
    708710        if ( ! isset( $file['name'] ) || empty( $file['name'] ) || ! is_string( $file['name'] ) ) {
    709711            $file['error'] = __( 'Invalid file name detected. Upload blocked.', 'iron-security' );
     
    712714        }
    713715
    714         // List of blocked extensions
     716
    715717        $blocked_extensions = array(
    716718            'php',
     
    734736        $extension = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) ?: '' );
    735737
    736         // Check if the file extension is in the blocked list
     738
    737739        if ( in_array( $extension, $blocked_extensions, true ) ) {
    738740            $file['error'] = sprintf(
     
    745747        }
    746748
    747         // Check for double extensions (e.g., file.jpg.php)
     749
    748750        $all_extensions = explode( '.', $file_path );
    749751        if ( count( $all_extensions ) > 2 ) {
     
    789791            ) );
    790792        } else {
    791             // Check if the value was actually the same (no update needed)
     793
    792794            $current_options = get_option( 'wpironis_options' );
    793795            if ( isset( $current_options['wpironis_prevent_direct_access'] ) &&
     
    842844";
    843845
    844         // Create or update root .htaccess file
     846
    845847        if ( ! file_exists( $root_htaccess_path ) || is_writable( $root_htaccess_path ) ) {
    846             // If .htaccess exists, read its content
     848
    847849            $existing_content = file_exists( $root_htaccess_path ) ? file_get_contents( $root_htaccess_path ) : '';
    848850
    849             // Check if our rules are already present
     851
    850852            if ( strpos( $existing_content, 'Iron Security - Prevent Direct File Access' ) === false ) {
    851                 // Add our rules after WordPress rules if they exist
     853
    852854                if ( strpos( $existing_content, '# END WordPress' ) !== false ) {
    853855                    $existing_content = str_replace( '# END WordPress',
     
    862864        }
    863865
    864         // Also protect wp-content directory
     866
    865867        $content_htaccess_path = WP_CONTENT_DIR . '/.htaccess';
    866868        $content_rules         = "
     
    918920        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    919921
    920         // Get current options or initialize array
     922
    921923        $options = get_option( 'wpironis_options', array() );
    922924
    923         // Update the file editor setting
     925
    924926        $options['wpironis_disable_file_editor'] = $enabled ? 1 : 0;
    925927
    926         // Save the updated options
     928
    927929        $updated = update_option( 'wpironis_options', $options );
    928930
    929         // Update wp-config.php to disable file editor
     931
    930932        if ( $enabled ) {
    931933            $this->wpironis_disable_file_editor_in_config();
     
    941943            ) );
    942944        } else {
    943             // Check if the value was actually the same (no update needed)
     945
    944946            $current_options = get_option( 'wpironis_options' );
    945947            if ( isset( $current_options['wpironis_disable_file_editor'] ) &&
     
    965967        $config_content = file_get_contents( $wp_config_path );
    966968
    967         // Check if the constant is already defined
     969
    968970        if ( strpos( $config_content, 'DISALLOW_FILE_EDIT' ) === false ) {
    969             // Find the line where we should add our constant
     971
    970972            $insert_point = strpos( $config_content, "/* That's all, stop editing!" );
    971973
     
    981983            }
    982984        } else {
    983             // Update existing constant if it's set to false
     985
    984986            $config_content = preg_replace(
    985987                "/define\(\s*'DISALLOW_FILE_EDIT'\s*,\s*false\s*\);/",
     
    10001002        $config_content = file_get_contents( $wp_config_path );
    10011003
    1002         // Remove the constant definition if it exists
     1004
    10031005        $config_content = preg_replace(
    10041006            "/define\(\s*'DISALLOW_FILE_EDIT'\s*,\s*true\s*\);(\r\n|\n|\r)?/",
     
    10211023        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    10221024
    1023         // Get current options or initialize array
     1025
    10241026        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    10251027
    1026         // Update the custom URL setting
     1028
    10271029        $options['enable_slug_change'] = $enabled ? '1' : '0';
    10281030
    1029         // Save the updated options
     1031
    10301032        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    10311033
     
    10371039            ) );
    10381040        } else {
    1039             // Check if the value was actually the same (no update needed)
     1041
    10401042            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    10411043            if ( isset( $current_options['enable_slug_change'] ) &&
     
    10671069        }
    10681070
    1069         $url = sanitize_title( $_POST['url'] ); // Sanitize and convert spaces to hyphens
     1071        $url = sanitize_title( $_POST['url'] );
    10701072
    10711073        if ( empty( $url ) ) {
     
    10751077        }
    10761078
    1077         // Get current options
     1079
    10781080        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    10791081
    1080         // Update the custom URL
     1082
    10811083        $options['wpironis_custom_login_slug'] = $url;
    10821084
    1083         // Save the updated options
     1085
    10841086        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    10851087
     
    10911093            ) );
    10921094        } else {
    1093             // Check if the value was actually the same (no update needed)
     1095
    10941096            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    10951097            if ( isset( $current_options['wpironis_custom_login_slug'] ) &&
     
    11171119        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    11181120
    1119         // Get current options or initialize array
     1121
    11201122        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    11211123
    1122         // Update the session timeout setting
     1124
    11231125        $options['enable_session_timeout'] = $enabled ? '1' : '0';
    11241126
    1125         // Save the updated options
     1127
    11261128        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    11271129
     
    11331135            ) );
    11341136        } else {
    1135             // Check if the value was actually the same (no update needed)
     1137
    11361138            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    11371139            if ( isset( $current_options['enable_session_timeout'] ) &&
     
    11721174        }
    11731175
    1174         // Get current options
     1176
    11751177        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    11761178
    1177         // Update the timeout value
     1179
    11781180        $options['session_timeout_value'] = $timeout;
    11791181
    1180         // Save the updated options
     1182
    11811183        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    11821184
     
    11881190            ) );
    11891191        } else {
    1190             // Check if the value was actually the same (no update needed)
     1192
    11911193            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    11921194            if ( isset( $current_options['session_timeout_value'] ) &&
     
    12141216        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    12151217
    1216         // Get current options
     1218
    12171219        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    12181220
    1219         // Update the limit login attempts setting
     1221
    12201222        $options['enable_limit_login_attempts'] = $enabled ? '1' : '0';
    12211223
    1222         // Save the updated options
     1224
    12231225        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    12241226
     
    12301232            ) );
    12311233        } else {
    1232             // Check if the value was actually the same (no update needed)
     1234
    12331235            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    12341236            if ( isset( $current_options['enable_limit_login_attempts'] ) &&
     
    12771279        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    12781280
    1279         // Update the settings
     1281
    12801282        $options['wpironis_limit_login_attempts'] = (string) $attempts;
    12811283        $options['wpironis_lockout_duration']     = (string) $duration;
    12821284
    1283         // Save the updated options
     1285
    12841286        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    12851287
     
    12921294            ) );
    12931295        } else {
    1294             // Check if the values were actually the same (no update needed)
     1296
    12951297            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    12961298            if ( isset( $current_options['wpironis_limit_login_attempts'] ) &&
     
    13211323        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    13221324
    1323         // Get current options
     1325
    13241326        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    13251327
    1326         // Update the user enumeration protection setting
     1328
    13271329        $options['enable_user_enumeration'] = $enabled ? '1' : '0';
    13281330
    1329         // Save the updated options
     1331
    13301332        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    13311333
     
    13371339            ) );
    13381340        } else {
    1339             // Check if the value was actually the same (no update needed)
     1341
    13401342            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    13411343            if ( isset( $current_options['enable_user_enumeration'] ) &&
     
    13751377        }
    13761378
    1377         // Get current options
     1379
    13781380        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    13791381
    1380         // Update the custom error message
     1382
    13811383        $options['user_enumeration_message'] = $message;
    13821384
    1383         // Save the updated options
     1385
    13841386        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    13851387
     
    13911393            ) );
    13921394        } else {
    1393             // Check if the value was actually the same (no update needed)
     1395
    13941396            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    13951397            if ( isset( $current_options['user_enumeration_message'] ) &&
     
    14071409
    14081410    public function wpironis_modify_login_errors( $error ) {
    1409         // Get the options
     1411
    14101412        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    14111413
    1412         // Check if user enumeration protection is enabled
     1414
    14131415        if ( ! empty( $options['enable_user_enumeration'] ) && $options['enable_user_enumeration'] === '1' ) {
    1414             // Get the custom message or use default
     1416
    14151417            $custom_message = ! empty( $options['user_enumeration_message'] )
    14161418                ? $options['user_enumeration_message']
    14171419                : 'Wrong Login credentials';
    14181420
    1419             // Check if this is a password-related error for an existing username
     1421
    14201422            if ( strpos( $error, 'password you entered for' ) !== false ) {
    14211423                return __( $custom_message, 'iron-security' );
     
    14271429
    14281430    public function wpironis_block_user_enumeration() {
    1429         // Get the options
     1431
    14301432        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    14311433
    1432         // Check if user enumeration protection is enabled
     1434
    14331435        if ( ! empty( $options['enable_user_enumeration'] ) && $options['enable_user_enumeration'] === '1' ) {
    1434             // Block access to author pages
     1436
    14351437            if ( is_author() ) {
    14361438                wp_redirect( home_url(), 301 );
     
    14381440            }
    14391441
    1440             // Block user enumeration through REST API
     1442
    14411443            add_filter( 'rest_endpoints', function ( $endpoints ) {
    14421444                if ( isset( $endpoints['/wp/v2/users'] ) ) {
     
    14501452            } );
    14511453
    1452             // Block ?author=N queries
     1454
    14531455            if ( isset( $_GET['author'] ) && is_numeric( $_GET['author'] ) ) {
    14541456                wp_redirect( home_url(), 301 );
     
    14691471        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    14701472
    1471         // Get current options or initialize array
     1473
    14721474        $options = get_option( 'wpironis_options', array() );
    14731475
    1474         // Update the REST API setting
     1476
    14751477        $options['wpironis_disable_rest_api'] = $enabled ? 1 : 0;
    14761478
    1477         // Save the updated options
     1479
    14781480        $updated = update_option( 'wpironis_options', $options );
    14791481
     
    14851487            ) );
    14861488        } else {
    1487             // Check if the value was actually the same (no update needed)
     1489
    14881490            $current_options = get_option( 'wpironis_options' );
    14891491            if ( isset( $current_options['wpironis_disable_rest_api'] ) &&
     
    15111513        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    15121514
    1513         // Get current options or initialize array
     1515
    15141516        $options = get_option( 'wpironis_options', array() );
    15151517
    1516         // Update the plugin auto-update setting
     1518
    15171519        $options['wpironis_enable_plugin_autoupdate'] = $enabled ? 1 : 0;
    15181520
    1519         // Save the updated options
     1521
    15201522        $updated = update_option( 'wpironis_options', $options );
    15211523
    1522         // Configure plugin auto-updates
     1524
    15231525        $this->wpironis_configure_plugin_autoupdates( $enabled );
    15241526
     
    15301532            ) );
    15311533        } else {
    1532             // Check if the value was actually the same (no update needed)
     1534
    15331535            $current_options = get_option( 'wpironis_options' );
    15341536            if ( isset( $current_options['wpironis_enable_plugin_autoupdate'] ) &&
     
    15561558        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    15571559
    1558         // Get current options or initialize array
     1560
    15591561        $options = get_option( 'wpironis_options', array() );
    15601562
    1561         // Update the core auto-update setting
     1563
    15621564        $options['wpironis_enable_core_autoupdate'] = $enabled ? 1 : 0;
    15631565
    1564         // Save the updated options
     1566
    15651567        $updated = update_option( 'wpironis_options', $options );
    15661568
    1567         // Configure core auto-updates
     1569
    15681570        $this->wpironis_configure_core_autoupdates( $enabled );
    15691571
     
    15751577            ) );
    15761578        } else {
    1577             // Check if the value was actually the same (no update needed)
     1579
    15781580            $current_options = get_option( 'wpironis_options' );
    15791581            if ( isset( $current_options['wpironis_enable_core_autoupdate'] ) &&
     
    15921594    private function wpironis_configure_plugin_autoupdates( $enabled ) {
    15931595        if ( $enabled ) {
    1594             // Enable auto-updates for all plugins using filter
     1596
    15951597            add_filter( 'auto_update_plugin', '__return_true' );
    15961598
    1597             // Also set the auto_update_plugins option
     1599
    15981600            $all_plugins = array_keys( get_plugins() );
    15991601            update_site_option( 'auto_update_plugins', $all_plugins );
    16001602        } else {
    1601             // Disable auto-updates for all plugins
     1603
    16021604            add_filter( 'auto_update_plugin', '__return_false' );
    16031605
    1604             // Clear the auto_update_plugins option
     1606
    16051607            update_site_option( 'auto_update_plugins', array() );
    16061608        }
     
    16091611    private function wpironis_configure_core_autoupdates( $enabled ) {
    16101612        if ( $enabled ) {
    1611             // Enable core auto-updates for all update types
     1613
    16121614            add_filter( 'allow_major_auto_core_updates', '__return_true' );
    16131615            add_filter( 'allow_minor_auto_core_updates', '__return_true' );
     
    16151617            add_filter( 'auto_update_translation', '__return_true' );
    16161618
    1617             // Update the core update settings
     1619
    16181620            update_site_option( 'auto_update_core_major', 'enabled' );
    16191621            update_site_option( 'auto_update_core_minor', 'enabled' );
    16201622            update_site_option( 'auto_update_core_dev', 'enabled' );
    16211623        } else {
    1622             // Disable core auto-updates
     1624
    16231625            add_filter( 'allow_major_auto_core_updates', '__return_false' );
    16241626            add_filter( 'allow_minor_auto_core_updates', '__return_false' );
     
    16261628            add_filter( 'auto_update_translation', '__return_false' );
    16271629
    1628             // Update the core update settings
     1630
    16291631            update_site_option( 'auto_update_core_major', 'disabled' );
    16301632            update_site_option( 'auto_update_core_minor', 'disabled' );
     
    16601662        }
    16611663
    1662         // Update last activity time
     1664
    16631665        update_user_meta( get_current_user_id(), 'iron_security_last_activity', time() );
    16641666    }
     
    17041706        }
    17051707
    1706         // Get filter parameters
     1708
    17071709        $log_type   = isset( $_POST['log_type'] ) ? sanitize_text_field( $_POST['log_type'] ) : '';
    17081710        $status     = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : '';
     
    17141716        $per_page   = isset( $_POST['per_page'] ) ? intval( $_POST['per_page'] ) : 50;
    17151717
    1716         // Build filter arguments
     1718
    17171719        $args = array(
    17181720            'log_type'   => $log_type,
     
    17261728        );
    17271729
    1728         // Get logs with the specified filters
     1730
    17291731        $logs_data = Iron_Security_Logger::get_logs( $args );
    17301732
    1731         // Get log summary
     1733
    17321734        $summary = $this->get_logs_summary();
    17331735
     
    17491751        $table_name = $wpdb->prefix . 'iron_security_logs';
    17501752
    1751         // Get total count
     1753
    17521754        $total = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name" );
    17531755
    1754         // Get count by status
     1756
    17551757        $success_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'success'" );
    17561758        $failure_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'failure'" );
     
    17771779        }
    17781780
    1779         // Get filter parameters
     1781
    17801782        $log_type   = isset( $_POST['log_type'] ) ? sanitize_text_field( $_POST['log_type'] ) : '';
    17811783        $status     = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : '';
     
    17851787        $date_to    = isset( $_POST['date_to'] ) ? sanitize_text_field( $_POST['date_to'] ) : '';
    17861788
    1787         // Build filter arguments (with large per_page for export)
     1789
    17881790        $args = array(
    17891791            'log_type'   => $log_type,
     
    17931795            'date_from'  => $date_from,
    17941796            'date_to'    => $date_to,
    1795             'per_page'   => 10000, // Large number to get most/all logs
     1797            'per_page'   => 10000,
    17961798            'page'       => 1
    17971799        );
    17981800
    1799         // Get all logs with the specified filters
     1801
    18001802        $logs_data = Iron_Security_Logger::get_logs( $args );
    18011803        $logs      = $logs_data['logs'];
    18021804
    1803         // Set headers for CSV download
     1805
    18041806        header( 'Content-Type: text/csv' );
    18051807        header( 'Content-Disposition: attachment; filename="iron-security-logs-' . date( 'Y-m-d' ) . '.csv"' );
     
    18071809        header( 'Expires: 0' );
    18081810
    1809         // Create a file pointer connected to the output stream
     1811
    18101812        $output = fopen( 'php://output', 'w' );
    18111813
    1812         // Output the column headings
     1814
    18131815        fputcsv( $output,
    18141816            array( 'ID', 'Date', 'Type', 'Username', 'User ID', 'IP Address', 'Message', 'Status', 'Extra Data' ) );
    18151817
    1816         // Output each row of the data
     1818
    18171819        foreach ( $logs as $log ) {
    18181820            $log_type_labels = Iron_Security_Logger::get_log_types();
    18191821            $log_type_label  = isset( $log_type_labels[ $log['log_type'] ] ) ? $log_type_labels[ $log['log_type'] ] : $log['log_type'];
    18201822
    1821             // Format extra data for CSV
     1823
    18221824            $extra_data = '';
    18231825            if ( ! empty( $log['extra_data'] ) ) {
     
    18421844        }
    18431845
    1844         // Close the file pointer
     1846
    18451847        fclose( $output );
    18461848        exit;
     
    18951897        }
    18961898
    1897         // Map the JavaScript camelCase to the database option name
     1899
    18981900        $option_map = [
    18991901            'contentTypeOptions'      => 'wpironis_security_header_content_type_options',
     
    19141916        $option_name = $option_map[ $header_name ];
    19151917
    1916         // Get current options
     1918
    19171919        $options = get_option( 'wpironis_options', array() );
    19181920
    1919         // Update the specific security header setting
     1921
    19201922        $options[ $option_name ] = $enabled ? 1 : 0;
    19211923
    1922         // Save the updated options
     1924
    19231925        $updated = update_option( 'wpironis_options', $options );
    19241926
    1925         // Generate a friendly name for the header for use in messages
     1927
    19261928        $header_friendly_names = [
    19271929            'contentTypeOptions'      => 'X-Content-Type-Options',
     
    19451947            ) );
    19461948        } else {
    1947             // Check if the value was actually the same (no update needed)
     1949
    19481950            $current_options = get_option( 'wpironis_options' );
    19491951            if ( isset( $current_options[ $option_name ] ) && $current_options[ $option_name ] === ( $enabled ? 1 : 0 ) ) {
     
    19751977        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    19761978
    1977         // Get current settings
     1979
    19781980        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    19791981
    1980         // Update the limit admins setting
     1982
    19811983        $options['enable_limit_admins'] = $enabled ? '1' : '0';
    19821984
    1983         // Save settings
     1985
    19841986        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    19851987
     
    19901992            );
    19911993
    1992             // If the feature is enabled, include admin count and users list
     1994
    19931995            if ( $enabled ) {
    19941996                $admin_users = get_users( array(
     
    20142016            wp_send_json_success( $response_data );
    20152017        } else {
    2016             // Check if the value was actually the same (no update needed)
     2018
    20172019            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    20182020            if ( isset( $current_options['enable_limit_admins'] ) &&
     
    20232025                );
    20242026
    2025                 // If the feature is enabled, include admin count and users list
     2027
    20262028                if ( $enabled ) {
    20272029                    $admin_users = get_users( array(
     
    20642066        }
    20652067
    2066         // Validate and sanitize inputs
     2068
    20672069        $max_admins    = isset( $_POST['max_admins'] ) ? absint( $_POST['max_admins'] ) : 1;
    20682070        $fallback_role = isset( $_POST['fallback_role'] ) ? sanitize_text_field( $_POST['fallback_role'] ) : 'editor';
    20692071
    2070         // Ensure max_admins is at least 1
     2072
    20712073        if ( $max_admins < 1 ) {
    20722074            $max_admins = 1;
    20732075        }
    20742076
    2075         // Validate fallback role is a valid WordPress role
     2077
    20762078        $valid_roles = array( 'editor', 'author', 'contributor', 'subscriber' );
    20772079        if ( ! in_array( $fallback_role, $valid_roles ) ) {
     
    20792081        }
    20802082
    2081         // Get current settings
     2083
    20822084        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    20832085
    2084         // Update the limit admins settings
     2086
    20852087        $options['wpironis_max_admins']          = (string) $max_admins;
    20862088        $options['wpironis_admin_fallback_role'] = $fallback_role;
    20872089
    2088         // Save settings
     2090
    20892091        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $options );
    20902092
    2091         // Get admin users for the response
     2093
    20922094        $admin_users = get_users( array(
    20932095            'role' => 'administrator',
    20942096        ) );
    20952097
    2096         // Count admins
     2098
    20972099        $admin_count = count( $admin_users );
    20982100
    2099         // Prepare the user data to return
     2101
    21002102        $users = array();
    21012103        foreach ( $admin_users as $user ) {
     
    21092111
    21102112        if ( $updated ) {
    2111             // Enforce the limit now by checking current admin count
     2113
    21122114            $this->wpironis_enforce_admin_limit_now();
    21132115
    2114             // Get updated admin count after enforcement
     2116
    21152117            $updated_admin_users = get_users( array(
    21162118                'role' => 'administrator',
     
    21182120            $updated_admin_count = count( $updated_admin_users );
    21192121
    2120             // Prepare updated user list
     2122
    21212123            $updated_users = array();
    21222124            foreach ( $updated_admin_users as $user ) {
     
    21382140            ) );
    21392141        } else {
    2140             // Check if the value was actually the same (no update needed)
     2142
    21412143            $current_options = get_option( 'wpironis_plugin_settings_loginlogout' );
    21422144            if ( isset( $current_options['wpironis_max_admins'] ) &&
     
    21652167     */
    21662168    public function wpironis_enforce_admin_limit( $user_id, $role, $old_roles ) {
    2167         // Check if the new role is administrator
     2169
    21682170        if ( $role !== 'administrator' ) {
    21692171            return;
    21702172        }
    21712173
    2172         // Get the settings
     2174
    21732175        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    21742176
    2175         // Check if the feature is enabled
     2177
    21762178        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    21772179            return;
    21782180        }
    21792181
    2180         // Get maximum allowed admins
     2182
    21812183        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    21822184
    2183         // Get the fallback role
     2185
    21842186        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    21852187
    2186         // Count current admins
     2188
    21872189        $admin_count = $this->wpironis_count_admins();
    21882190
    2189         // If the max has been reached, change this user's role to the fallback
     2191
    21902192        if ( $admin_count > $max_admins ) {
    2191             // Skip the current hook to avoid infinite loop
     2193
    21922194            remove_action( 'set_user_role', array( $this, 'wpironis_enforce_admin_limit' ), 10 );
    21932195
    2194             // Downgrade the user to the fallback role
     2196
    21952197            $user = new WP_User( $user_id );
    21962198            $user->set_role( $fallback_role );
    21972199
    2198             // Log the event safely
     2200
    21992201            $this->safe_log_security_event(
    22002202                'admin_limit_enforced',
     
    22072209            );
    22082210
    2209             // Add admin notice
     2211
    22102212            add_action( 'admin_notices', function () use ( $user, $fallback_role, $max_admins ) {
    22112213                echo '<div class="notice notice-warning is-dismissible">';
     
    22202222            } );
    22212223
    2222             // Re-add the hook
     2224
    22232225            add_action( 'set_user_role', array( $this, 'wpironis_enforce_admin_limit' ), 10, 3 );
    22242226        }
     
    22312233     */
    22322234    public function wpironis_check_admin_limit_on_register( $user_id ) {
    2233         // Get the user object
     2235
    22342236        $user = new WP_User( $user_id );
    22352237
    2236         // Check if the user is being registered as an admin
     2238
    22372239        if ( ! in_array( 'administrator', $user->roles ) ) {
    22382240            return;
    22392241        }
    22402242
    2241         // Get the settings
     2243
    22422244        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    22432245
    2244         // Check if the feature is enabled
     2246
    22452247        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    22462248            return;
    22472249        }
    22482250
    2249         // Get maximum allowed admins
     2251
    22502252        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    22512253
    2252         // Get the fallback role
     2254
    22532255        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    22542256
    2255         // Count current admins
     2257
    22562258        $admin_count = $this->wpironis_count_admins();
    22572259
    2258         // If the max has been reached, change this user's role to the fallback
     2260
    22592261        if ( $admin_count > $max_admins ) {
    2260             // Downgrade the user to the fallback role
     2262
    22612263            $user->set_role( $fallback_role );
    22622264
    2263             // Log the event safely
     2265
    22642266            $this->safe_log_security_event(
    22652267                'admin_limit_enforced',
     
    22812283     */
    22822284    private function safe_log_security_event( $event_type, $message ) {
    2283         // Try to use the Logger class if it exists and has the method
     2285
    22842286        if ( class_exists( 'Iron_Security_Logger' ) && method_exists( 'Iron_Security_Logger', 'log_security_event' ) ) {
    22852287            Iron_Security_Logger::log_security_event( $event_type, $message );
    22862288        } else {
    2287             // Fallback logging to the WordPress error log
     2289
    22882290            error_log( sprintf( '[Iron Security] %s: %s', $event_type, $message ) );
    22892291
    2290             // Attempt to log to a custom database table if it exists
     2292
    22912293            global $wpdb;
    22922294            $table_name = $wpdb->prefix . 'iron_security_logs';
    22932295
    2294             // Check if the table exists before attempting to insert
     2296
    22952297            if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) === $table_name ) {
    22962298                $wpdb->insert(
     
    23302332     */
    23312333    private function wpironis_enforce_admin_limit_now() {
    2332         // Get the settings
     2334
    23332335        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    23342336
    2335         // Check if the feature is enabled
     2337
    23362338        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    23372339            return;
    23382340        }
    23392341
    2340         // Get maximum allowed admins
     2342
    23412343        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    23422344
    2343         // Get the fallback role
     2345
    23442346        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    23452347
    2346         // Get all admins
     2348
    23472349        $admin_users = get_users( array(
    23482350            'role'    => 'administrator',
     
    23512353        ) );
    23522354
    2353         // Skip if we have fewer admins than the limit
     2355
    23542356        if ( count( $admin_users ) <= $max_admins ) {
    23552357            return;
    23562358        }
    23572359
    2358         // Keep track of how many admins we've processed
     2360
    23592361        $admin_count = 0;
    23602362
    2361         // Process each admin user
     2363
    23622364        foreach ( $admin_users as $admin_user ) {
    23632365            $admin_count ++;
    23642366
    2365             // Skip the first max_admins administrators (ordered by ID)
     2367
    23662368            if ( $admin_count <= $max_admins ) {
    23672369                continue;
    23682370            }
    23692371
    2370             // Downgrade the user to the fallback role
     2372
    23712373            $admin_user->set_role( $fallback_role );
    23722374
    2373             // Log the event safely
     2375
    23742376            $this->safe_log_security_event(
    23752377                'admin_limit_enforced',
     
    24152417        }
    24162418
    2417         // Check if this is a user create/update request
     2419
    24182420        $route = $request->get_route();
    24192421        if ( strpos( $route, '/wp/v2/users' ) === false ) {
     
    24212423        }
    24222424
    2423         // Check if operation involves setting a role to administrator
     2425
    24242426        $params = $request->get_params();
    24252427        if ( ! isset( $params['roles'] ) || ! in_array( 'administrator', (array) $params['roles'] ) ) {
     
    24272429        }
    24282430
    2429         // Get the settings
     2431
    24302432        $options = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    24312433
    2432         // Check if the feature is enabled
     2434
    24332435        if ( ! isset( $options['enable_limit_admins'] ) || $options['enable_limit_admins'] !== '1' ) {
    24342436            return $response;
    24352437        }
    24362438
    2437         // Get maximum allowed admins
     2439
    24382440        $max_admins = isset( $options['wpironis_max_admins'] ) ? absint( $options['wpironis_max_admins'] ) : 1;
    24392441
    2440         // Get the fallback role
     2442
    24412443        $fallback_role = isset( $options['wpironis_admin_fallback_role'] ) ? $options['wpironis_admin_fallback_role'] : 'editor';
    24422444
    2443         // Count current admins
     2445
    24442446        $admin_count = $this->wpironis_count_admins();
    24452447
    2446         // Get the user ID from the request
     2448
    24472449        $user_id = isset( $params['id'] ) ? $params['id'] : null;
    24482450
    2449         // For user creation, the ID will be in the response
     2451
    24502452        if ( ! $user_id && isset( $response->data['id'] ) ) {
    24512453            $user_id = $response->data['id'];
    24522454        }
    24532455
    2454         // Get the user to check if they were already an admin
     2456
    24552457        $user      = get_user_by( 'id', $user_id );
    24562458        $was_admin = $user && in_array( 'administrator', $user->roles );
    24572459
    2458         // If the user was not already an admin and the limit has been reached
     2460
    24592461        if ( ! $was_admin && $admin_count >= $max_admins ) {
    2460             // Update the user to the fallback role
     2462
    24612463            $user = new WP_User( $user_id );
    24622464            $user->set_role( $fallback_role );
    24632465
    2464             // Log the event safely
     2466
    24652467            $this->safe_log_security_event(
    24662468                'admin_limit_enforced_api',
     
    24732475            );
    24742476
    2475             // Update the response to reflect the actual role
     2477
    24762478            if ( isset( $response->data['roles'] ) ) {
    24772479                $response->data['roles'] = array( $fallback_role );
    24782480            }
    24792481
    2480             // Add a message to the response
     2482
    24812483            if ( ! isset( $response->data['iron_security_message'] ) ) {
    24822484                $response->data['iron_security_message'] = sprintf(
     
    24962498     */
    24972499    public function wpironsec_get_admin_info() {
    2498         // Verify nonce
     2500
    24992501        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'iron_security_nonce' ) ) {
    25002502            wp_send_json_error( 'Invalid security token sent.' );
     
    25022504        }
    25032505
    2504         // Check if user is authorized
     2506
    25052507        if ( ! current_user_can( 'manage_options' ) ) {
    25062508            wp_send_json_error( 'You do not have permission to perform this action.' );
     
    25082510        }
    25092511
    2510         // Get administrator users
     2512
    25112513        $admin_users = get_users( array(
    25122514            'role' => 'administrator',
    25132515        ) );
    25142516
    2515         // Prepare the user data to return
     2517
    25162518        $users = array();
    25172519        foreach ( $admin_users as $user ) {
     
    25242526        }
    25252527
    2526         // Count
     2528
    25272529        $count = count( $admin_users );
    25282530
    2529         // Send the response
     2531
    25302532        wp_send_json_success( array(
    25312533            'count'   => $count,
     
    25672569        }
    25682570
    2569         // Get the differences to determine what was updated
     2571
    25702572        $changes = array();
    25712573
    2572         // Check email change
     2574
    25732575        if ( $old_user_data->user_email !== $user->user_email ) {
    25742576            $changes['email'] = array(
     
    25782580        }
    25792581
    2580         // Check display name change
     2582
    25812583        if ( $old_user_data->display_name !== $user->display_name ) {
    25822584            $changes['display_name'] = array(
     
    25862588        }
    25872589
    2588         // Check URL change
     2590
    25892591        if ( $old_user_data->user_url !== $user->user_url ) {
    25902592            $changes['url'] = array(
     
    25952597
    25962598        if ( empty( $changes ) ) {
    2597             // Log general profile update if no specific changes detected
     2599
    25982600            $message = sprintf( 'User "%s" profile was updated', $user->user_login );
    25992601        } else {
    2600             // Log specific changes
     2602
    26012603            $fields  = array_keys( $changes );
    26022604            $message = sprintf( 'User "%s" profile was updated. Changed fields: %s',
     
    28222824        global $wp_version;
    28232825
    2824         // Only log this occasionally to avoid excessive logs
     2826
    28252827        $last_check = get_option( 'wpironis_last_version_check_log', 0 );
    2826         if ( time() - $last_check < 86400 ) { // Once per day max
     2828        if ( time() - $last_check < 86400 ) {
    28272829            return;
    28282830        }
     
    28552857        global $pagenow;
    28562858
    2857         // Only log certain admin actions to avoid excessive logging
     2859
    28582860        if ( ! is_admin() || ! current_user_can( 'edit_posts' ) ) {
    28592861            return;
     
    28622864        $action = isset( $_REQUEST['action'] ) ? sanitize_text_field( $_REQUEST['action'] ) : '';
    28632865
    2864         // Skip logging for common, routine actions
     2866
    28652867        $skip_actions = array( 'heartbeat', 'wp-refresh-post-lock', 'ajax-tag-search' );
    28662868        if ( in_array( $action, $skip_actions ) ) {
     
    28682870        }
    28692871
    2870         // Log specific high-value actions only
     2872
    28712873        $important_actions = array(
    28722874            'update'            => 'Update',
     
    28932895            $details = array(
    28942896                'page'       => $pagenow,
    2895                 'query_args' => $_GET // Including all query parameters for context
     2897                'query_args' => $_GET
    28962898            );
    28972899
     
    28992901        }
    29002902
    2901         // Log access to sensitive admin pages
     2903
    29022904        $sensitive_pages = array(
    29032905            'options.php'                            => 'Settings Update',
     
    29222924
    29232925        foreach ( $sensitive_pages as $page => $description ) {
    2924             // Check if we're on this page or if page with query matches
     2926
    29252927            if ( $pagenow === $page || ( strpos( $page, '?' ) !== false && strpos( $_SERVER['REQUEST_URI'],
    29262928                        $page ) !== false ) ) {
     
    29922994     */
    29932995    public function detect_frame_embedding() {
    2994         // Only run this occasionally to avoid performance issues
     2996
    29952997        if ( ! is_admin() || mt_rand( 1, 10 ) !== 1 ) {
    29962998            return;
     
    30023004            $home_url = home_url();
    30033005
    3004             // Check if the referer is from a different domain
     3006
    30053007            if ( strpos( $referer, $site_url ) !== 0 && strpos( $referer, $home_url ) !== 0 ) {
    3006                 // This could potentially be a clickjacking attempt
     3008
    30073009                $referer_host = parse_url( $referer, PHP_URL_HOST );
    30083010                $site_host    = parse_url( $site_url, PHP_URL_HOST );
     
    30203022     */
    30213023    public function detect_suspicious_requests() {
    3022         // Only run this occasionally to avoid performance issues
     3024
    30233025        if ( mt_rand( 1, 10 ) !== 1 ) {
    30243026            return;
    30253027        }
    30263028
    3027         // Detect common attack patterns in URLs
     3029
    30283030        $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';
    30293031        $user_agent  = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
    30303032
    3031         // List of suspicious URL patterns with attack type and whether it's a regex
     3033
    30323034        $suspicious_patterns = array(
    30333035            'eval\('             => array( 'type' => 'PHP Injection', 'is_regex' => true ),
     
    30773079        }
    30783080
    3079         // Check for known malicious or scanning user agents
     3081
    30803082        $malicious_agents = array(
    30813083            'nikto'           => 'Security Scanner',
     
    31023104                    sprintf( 'Detected %s user agent: %s', $agent_type, $agent )
    31033105                );
    3104                 break; // Only log once per request
     3106                break;
    31053107            }
    31063108        }
     
    31113113     */
    31123114    public function monitor_request_methods() {
    3113         // Only monitor in admin area or for login/register pages
     3115
    31143116        if ( ! is_admin() && ! in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ) ) ) {
    31153117            return;
     
    31183120        $method = isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : '';
    31193121
    3120         // Unusual request methods for WordPress admin
     3122
    31213123        $unusual_methods = array( 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH' );
    31223124
     
    31563158     */
    31573159    public function setup_rest_logging() {
    3158         // Add a filter to log API requests
     3160
    31593161        add_filter( 'rest_pre_dispatch', array( $this, 'log_rest_api_request' ), 10, 3 );
    31603162    }
     
    31723174        $route = $request->get_route();
    31733175
    3174         // Skip some common, noisy routes
     3176
    31753177        $skip_routes = array(
    31763178            '/wp/v2/users/me',
     
    31823184        }
    31833185
    3184         // Sensitive routes that should always be logged
     3186
    31853187        $sensitive_routes = array(
    31863188            '/wp/v2/users',
     
    32013203        }
    32023204
    3203         // Always log writes (POST, PUT, DELETE) or sensitive routes
     3205
    32043206        $method = $request->get_method();
    32053207        if ( $is_sensitive || in_array( $method, array( 'POST', 'PUT', 'DELETE', 'PATCH' ) ) ) {
     
    32433245     */
    32443246    public function log_http_api_errors( $response, $context, $class, $parsed_args, $url ) {
    3245         // Only log errors
     3247
    32463248        if ( ! is_wp_error( $response ) ) {
    32473249            return;
     
    32513253        $error_message = $response->get_error_message();
    32523254
    3253         // Don't log common timeout errors which may be normal
     3255
    32543256        if ( strpos( $error_code, 'timeout' ) !== false ) {
    32553257            return;
     
    32633265     */
    32643266    public function detect_security_scanners() {
    3265         // Only run this check occasionally to avoid performance impact
     3267
    32663268        if ( mt_rand( 1, 20 ) !== 1 ) {
    32673269            return;
    32683270        }
    32693271
    3270         // Check for rapid-fire requests from the same IP
     3272
    32713273        $ip = $this->get_ip_address();
    32723274
    3273         // Get the last time we recorded this IP
     3275
    32743276        $last_checked = get_transient( 'wpironis_ip_check_' . md5( $ip ) );
    32753277        $now          = time();
    32763278
    32773279        if ( $last_checked ) {
    3278             // If this IP made a request very recently (within 1 second)
     3280
    32793281            if ( $now - $last_checked < 1 ) {
    3280                 // Increment the counter for this IP
     3282
    32813283                $count = get_transient( 'wpironis_ip_count_' . md5( $ip ) );
    32823284                $count = $count ? $count + 1 : 1;
    3283                 set_transient( 'wpironis_ip_count_' . md5( $ip ), $count, 300 ); // Keep count for 5 minutes
    3284 
    3285                 // If we've seen too many rapid requests, log it as a scanner
     3285                set_transient( 'wpironis_ip_count_' . md5( $ip ), $count, 300 );
     3286
     3287
    32863288                if ( $count > 10 ) {
    32873289                    Iron_Security_Logger::log_suspicious_behavior(
     
    32903292                    );
    32913293
    3292                     // Reset counter to avoid logging repeatedly
     3294
    32933295                    set_transient( 'wpironis_ip_count_' . md5( $ip ), 0, 300 );
    32943296                }
     
    32963298        }
    32973299
    3298         // Update the last checked time for this IP
    3299         set_transient( 'wpironis_ip_check_' . md5( $ip ), $now, 300 ); // Keep for 5 minutes
     3300
     3301        set_transient( 'wpironis_ip_check_' . md5( $ip ), $now, 300 );
    33003302    }
    33013303
     
    33053307     */
    33063308    public function wpironis_handle_admin_id_protection_toggle() {
    3307         // Verify nonce
     3309
    33083310        if ( ! check_ajax_referer( 'iron_security_nonce', 'nonce', false ) ) {
    33093311            wp_send_json_error( 'Invalid security token. Please refresh the page and try again.' );
     
    33203322        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
    33213323
    3322         // Get current settings
     3324
    33233325        $loginlogout_settings = get_option( 'wpironis_plugin_settings_loginlogout', array() );
    33243326
    3325         // Update the settings
     3327
    33263328        $loginlogout_settings['enable_admin_id_protection'] = $enabled ? '1' : '0';
    33273329
    3328         $admin_id = '1'; // Default admin ID
    3329 
    3330         // If enabling the feature
     3330        $admin_id = '1';
     3331
     3332
    33313333        if ( $enabled ) {
    3332             // Check if we already have a stored admin ID in settings
     3334
    33333335            if ( ! empty( $loginlogout_settings['current_admin_id'] ) ) {
    33343336                $admin_id = $loginlogout_settings['current_admin_id'];
    33353337                $message  = 'Admin ID protection enabled. Using current administrator ID: ' . $admin_id;
    33363338
    3337                 // Log this security event
     3339
    33383340                $this->safe_log_security_event(
    33393341                    'settings_change',
     
    33423344                    'success'
    33433345                );
    3344             } // Check if admin with ID 1 still exists
     3346            }
    33453347            elseif ( ! get_userdata( 1 ) ) {
    3346                 // Admin ID 1 doesn't exist, try to find the first admin user
     3348
    33473349                $admins = get_users( array( 'role' => 'administrator', 'number' => 1 ) );
    33483350                if ( ! empty( $admins ) ) {
     
    33513353                    $message                                  = 'Admin ID protection enabled. Using existing administrator ID: ' . $admin_id;
    33523354
    3353                     // Log this security event
     3355
    33543356                    $this->safe_log_security_event(
    33553357                        'settings_change',
     
    33593361                    );
    33603362                } else {
    3361                     // No admin users found - this is unlikely but handle it
     3363
    33623364                    $loginlogout_settings['enable_admin_id_protection'] = '0';
    33633365                    wp_send_json_error( 'No administrator accounts found.' );
     
    33653367                    return;
    33663368                }
    3367             } // If admin ID 1 exists and needs to be changed
     3369            }
    33683370            else {
    33693371                try {
     
    33803382                        $message = 'Admin ID protection enabled. Administrator ID changed from 1 to ' . $admin_id;
    33813383                    } else {
    3382                         // If we couldn't change the ID, disable the feature
     3384
    33833385                        $loginlogout_settings['enable_admin_id_protection'] = '0';
    33843386                        wp_send_json_error( 'Failed to change admin ID. Please try again.' );
     
    33943396            }
    33953397        } else {
    3396             // When disabling, just update the setting but don't change the ID back
    3397             // since that would be complex and potentially disruptive
     3398
     3399
    33983400            $admin_id = ! empty( $loginlogout_settings['current_admin_id'] ) ? $loginlogout_settings['current_admin_id'] : '1';
    33993401            $message  = 'Admin ID protection disabled. Note that the admin ID will remain changed.';
    34003402
    3401             // Log this security event
     3403
    34023404            $this->safe_log_security_event(
    34033405                'settings_change',
     
    34083410        }
    34093411
    3410         // Save updated settings
     3412
    34113413        update_option( 'wpironis_plugin_settings_loginlogout', $loginlogout_settings );
    34123414
    3413         // Return success with the admin ID
     3415
    34143416        wp_send_json_success( array(
    34153417            'message'  => $message,
     
    34263428        global $wpdb;
    34273429
    3428         // First check if user ID 1 exists
     3430
    34293431        $user = get_userdata( 1 );
    34303432        if ( ! $user ) {
     
    34343436        }
    34353437
    3436         // Check if user is an administrator
     3438
    34373439        if ( ! in_array( 'administrator', $user->roles ) ) {
    34383440            error_log( 'Iron Security: Cannot change admin ID - User ID 1 is not an administrator' );
     
    34413443        }
    34423444
    3443         // Start a transaction
     3445
    34443446        $wpdb->query( 'START TRANSACTION' );
    34453447
    34463448        try {
    3447             // Get the highest user ID in the database
     3449
    34483450            $highest_id = $wpdb->get_var( "SELECT MAX(ID) FROM {$wpdb->users}" );
    34493451
    3450             // New ID will be highest + a random offset between 100-999
     3452
    34513453            $new_id = (int) $highest_id + 1;
    34523454
    3453             // Update user ID in users table
     3455
    34543456            $result = $wpdb->query( $wpdb->prepare(
    34553457                "UPDATE {$wpdb->users} SET ID = %d WHERE ID = 1",
     
    34613463            }
    34623464
    3463             // Update user ID in usermeta table
     3465
    34643466            $result = $wpdb->query( $wpdb->prepare(
    34653467                "UPDATE {$wpdb->usermeta} SET user_id = %d WHERE user_id = 1",
     
    34713473            }
    34723474
    3473             // Update user ID in posts table (for post author)
     3475
    34743476            $result = $wpdb->query( $wpdb->prepare(
    34753477                "UPDATE {$wpdb->posts} SET post_author = %d WHERE post_author = 1",
     
    34813483            }
    34823484
    3483             // Update user ID in comments table
     3485
    34843486            $result = $wpdb->query( $wpdb->prepare(
    34853487                "UPDATE {$wpdb->comments} SET user_id = %d WHERE user_id = 1",
     
    34913493            }
    34923494
    3493             // Additional tables to check for user_id references
    3494             // WooCommerce orders if present
     3495
     3496
    34953497            if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_orders'" ) ) {
    34963498                $wpdb->query( $wpdb->prepare(
     
    35003502            }
    35013503
    3502             // If using BuddyPress
     3504
    35033505            if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}bp_activity'" ) ) {
    35043506                $wpdb->query( $wpdb->prepare(
     
    35083510            }
    35093511
    3510             // If we're here, everything succeeded
     3512
    35113513            $wpdb->query( 'COMMIT' );
    35123514
    3513             // Clear caches
     3515
    35143516            wp_cache_delete( 1, 'users' );
    35153517            wp_cache_delete( 1, 'user_meta' );
     
    35173519            wp_cache_delete( $new_id, 'user_meta' );
    35183520
    3519             // Return the new ID
     3521
    35203522            return (string) $new_id;
    35213523        } catch ( Exception $e ) {
    3522             // Something went wrong, rollback
     3524
    35233525            $wpdb->query( 'ROLLBACK' );
    35243526
    3525             // Log the error
     3527
    35263528            error_log( 'Iron Security: Failed to change admin ID: ' . $e->getMessage() );
    35273529
    3528             // Rethrow the exception
     3530
    35293531            throw $e;
    35303532        }
     
    35783580        }
    35793581
    3580         // Correctly interpret "true" or "false" string from JS
     3582
    35813583        $enabled = isset( $_POST['enabled'] ) && $_POST['enabled'] === 'true';
    35823584
    3583         // Save proper value to DB
     3585
    35843586        $general_settings                           = get_option( 'wpironis_options', array() );
    35853587        $general_settings['wpironis_block_ai_bots'] = $enabled ? 1 : 0;
    35863588        update_option( 'wpironis_options', $general_settings );
    35873589
    3588         // Modify .htaccess
     3590
    35893591        $this->general_security->modifyHtaccessForAiBots( $enabled );
    35903592
    3591         // Prepare response message
     3593
    35923594        $message = $enabled
    35933595            ? __( 'AI bot blocking has been enabled. Your site is now protected from AI bot crawlers.',
     
    36163618        }
    36173619
    3618         // Fallback to glob if manifest doesn't exist or entry not found
     3620
    36193621        $dist_path = plugin_dir_path( __FILE__ ) . 'js/dist/';
    36203622        $files     = glob( $dist_path . $base_name . '.*.js' );
     
    36263628        }
    36273629
    3628         // Final fallback to non-hashed filename
     3630
    36293631        return $base_name . '.bundle.js';
    36303632    }
    36313633
     3634    /**
     3635     * Handle toggle for changing admin username
     3636     */
     3637    public function wpironis_handle_change_admin_username_toggle() {
     3638
     3639        if ( ! check_ajax_referer( 'iron_security_nonce', 'nonce', false ) ) {
     3640            wp_send_json_error( 'Invalid security token. Please refresh the page and try again.' );
     3641            return;
     3642        }
     3643
     3644        if ( ! current_user_can( 'manage_options' ) ) {
     3645            wp_send_json_error( 'You do not have permission to change this setting.' );
     3646            return;
     3647        }
     3648
     3649        $enabled = isset( $_POST['enabled'] ) ? filter_var( $_POST['enabled'], FILTER_VALIDATE_BOOLEAN ) : false;
     3650
     3651
     3652        $loginlogout_settings = get_option( 'wpironis_plugin_settings_loginlogout', array() );
     3653
     3654
     3655        $loginlogout_settings['enable_change_admin_username'] = $enabled ? '1' : '0';
     3656
     3657
     3658        if ( $enabled && empty( $loginlogout_settings['wpironis_new_admin_username'] ) ) {
     3659            $loginlogout_settings['wpironis_new_admin_username'] = $this->wpironis_generate_random_username();
     3660        }
     3661
     3662
     3663        $updated = update_option( 'wpironis_plugin_settings_loginlogout', $loginlogout_settings );
     3664
     3665
     3666        $admin_usernames = $this->wpironis_get_admin_usernames();
     3667
     3668        if ( $updated ) {
     3669            $message = 'Admin username protection setting updated successfully';
     3670            $status = 'success';
     3671
     3672            if ( $enabled && !empty( $admin_usernames ) ) {
     3673                $message = 'Admin username protection enabled. Insecure usernames detected: ' . implode(', ', $admin_usernames);
     3674                $status = 'warning';
     3675            }
     3676
     3677            wp_send_json_success( array(
     3678                'message' => $message,
     3679                'status' => $status,
     3680                'admin_usernames' => $admin_usernames
     3681            ) );
     3682        } else {
     3683            wp_send_json_error( 'Failed to update admin username protection setting' );
     3684        }
     3685    }
     3686
     3687    /**
     3688     * Handle saving new admin username
     3689     */
     3690    public function wpironis_handle_change_admin_username_save() {
     3691
     3692        if ( ! check_ajax_referer( 'iron_security_nonce', 'nonce', false ) ) {
     3693            wp_send_json_error( 'Invalid security token. Please refresh the page and try again.' );
     3694            return;
     3695        }
     3696
     3697        if ( ! current_user_can( 'manage_options' ) ) {
     3698            wp_send_json_error( 'You do not have permission to change admin username.' );
     3699            return;
     3700        }
     3701
     3702
     3703        $new_username = isset( $_POST['new_username'] ) ? sanitize_user( $_POST['new_username'] ) : '';
     3704
     3705        if ( empty( $new_username ) ) {
     3706            wp_send_json_error( 'Username cannot be empty.' );
     3707            return;
     3708        }
     3709
     3710
     3711        if ( !validate_username( $new_username ) ) {
     3712            wp_send_json_error( 'Invalid username. Please use only letters.' );
     3713            return;
     3714        }
     3715
     3716
     3717        if ( username_exists( $new_username ) ) {
     3718            wp_send_json_error( 'Username already exists. Please choose another username.' );
     3719            return;
     3720        }
     3721
     3722
     3723        $loginlogout_settings = get_option( 'wpironis_plugin_settings_loginlogout', array() );
     3724
     3725
     3726        $loginlogout_settings['wpironis_new_admin_username'] = $new_username;
     3727        update_option( 'wpironis_plugin_settings_loginlogout', $loginlogout_settings );
     3728
     3729
     3730        $admin_usernames = $this->wpironis_get_admin_usernames();
     3731
     3732
     3733        $changed_users = array();
     3734
     3735        if ( !empty( $admin_usernames ) ) {
     3736            foreach ( $admin_usernames as $username ) {
     3737                $user = get_user_by( 'login', $username );
     3738                if ( $user ) {
     3739
     3740                    $unique_username = $this->wpironis_get_unique_username( $new_username );
     3741
     3742
     3743                    global $wpdb;
     3744                    $wpdb->update(
     3745                        $wpdb->users,
     3746                        array( 'user_login' => $unique_username ),
     3747                        array( 'ID' => $user->ID )
     3748                    );
     3749
     3750
     3751                    $this->safe_log_security_event( 'username_change', sprintf(
     3752                        'Changed username from %s to %s for user ID %d',
     3753                        $username,
     3754                        $unique_username,
     3755                        $user->ID
     3756                    ));
     3757
     3758
     3759                    clean_user_cache( $user->ID );
     3760
     3761                    $changed_users[] = array(
     3762                        'old_username' => $username,
     3763                        'new_username' => $unique_username,
     3764                        'display_name' => $user->display_name
     3765                    );
     3766                }
     3767            }
     3768        }
     3769
     3770        if ( !empty( $changed_users ) ) {
     3771            wp_send_json_success( array(
     3772                'message' => count( $changed_users ) . ' admin username(s) changed successfully.',
     3773                'changed_users' => $changed_users
     3774            ) );
     3775        } else {
     3776            wp_send_json_success( array(
     3777                'message' => 'New admin username saved. No insecure admin usernames found to change.'
     3778            ) );
     3779        }
     3780    }
     3781
     3782    /**
     3783     * Generate a random username with letters only
     3784     */
     3785    private function wpironis_generate_random_username() {
     3786        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
     3787        $username = '';
     3788        $length = rand(8, 12);
     3789
     3790        for ( $i = 0; $i < $length; $i++ ) {
     3791            $username .= $chars[rand(0, strlen($chars) - 1)];
     3792        }
     3793
     3794
     3795        return $this->wpironis_get_unique_username( $username );
     3796    }
     3797
     3798    /**
     3799     * Get a unique username by adding a number suffix if needed
     3800     */
     3801    private function wpironis_get_unique_username( $username ) {
     3802        $original_username = $username;
     3803        $suffix = 1;
     3804       
     3805        while ( username_exists( $username ) ) {
     3806            $username = $original_username . $suffix;
     3807            $suffix++;
     3808        }
     3809       
     3810        return $username;
     3811    }
     3812
     3813    /**
     3814     * Get usernames of admin users with common/default usernames
     3815     */
     3816    private function wpironis_get_admin_usernames() {
     3817        $insecure_usernames = array( 'admin', 'administrator', 'wordpress' );
     3818        $found_usernames = array();
     3819       
     3820        foreach ( $insecure_usernames as $username ) {
     3821            $user = get_user_by( 'login', $username );
     3822            if ( $user && in_array( 'administrator', $user->roles ) ) {
     3823                $found_usernames[] = $username;
     3824            }
     3825        }
     3826       
     3827        return $found_usernames;
     3828    }
     3829
    36323830}
  • iron-security/trunk/admin/js/components/Dashboard.jsx

    r3288628 r3300103  
    135135    );
    136136
    137     console.log(settings)
    138137
    139138    if (isLoading || !settings) {
  • iron-security/trunk/admin/js/components/FileDirectoryProectionSettings.jsx

    r3288628 r3300103  
    1212
    1313    useEffect(() => {
    14         // Update settings when they change
     14
    1515        setIsPhpUploadBlocked(settings.wpironis_options?.wpironis_block_php_uploads === 1);
    1616        setIsDirectAccessPrevented(settings.wpironis_options?.wpironis_prevent_direct_access === 1);
  • iron-security/trunk/admin/js/components/GeneralSettings.jsx

    r3288628 r3300103  
    1515
    1616    useEffect(() => {
    17         // Update all state values when settings change
     17
    1818        setIsXmlrpcEnabled(settings.wpironis_options?.wpironis_disable_xmlrpc === 1);
    1919        setIsWpVersionHidden(settings.wpironis_options?.wpironis_hide_wp_version === 1);
     
    5757
    5858            const data = await response.json();
    59             console.log('Response:', data); // Debug log
    6059
    6160            if (data.success) {
  • iron-security/trunk/admin/js/components/HttpSecurityHeadersSettings.jsx

    r3288628 r3300103  
    1919
    2020    useEffect(() => {
    21         // Update header settings when settings change
     21
    2222        setHeaderSettings({
    2323            contentTypeOptions: settings.wpironis_options?.wpironis_security_header_content_type_options === 1,
  • iron-security/trunk/admin/js/components/LoginLogoutSettings.jsx

    r3288628 r3300103  
    7272   
    7373    const [isLoadingAdminInfo, setIsLoadingAdminInfo] = useState(false);
     74
     75    const [isChangeAdminUsernameEnabled, setIsChangeAdminUsernameEnabled] = useState(
     76        loginLogoutSettings.enable_change_admin_username === '1'
     77    );
     78
     79    const [newAdminUsername, setNewAdminUsername] = useState(
     80        loginLogoutSettings.wpironis_new_admin_username || ''
     81    );
     82
     83    const [adminUsernameStatus, setAdminUsernameStatus] = useState(null);
    7484
    7585    const getAdminCountStatus = () => {
     
    103113    useEffect(() => {
    104114        const loginLogoutSettings = settings?.login_logout || {};
    105         // Update all state values when settings change
     115
    106116        setIsCustomUrlEnabled(loginLogoutSettings.enable_slug_change === '1');
    107117        setCustomUrl(loginLogoutSettings.wpironis_custom_login_slug || 'wp-admin');
     
    129139            setIsAdminIdProtectionEnabled(true);
    130140        }
     141
     142        if (loginLogoutSettings.enable_change_admin_username === '1') {
     143            setIsChangeAdminUsernameEnabled(true);
     144        }
     145
     146        if (loginLogoutSettings.wpironis_new_admin_username) {
     147            setNewAdminUsername(loginLogoutSettings.wpironis_new_admin_username);
     148        }
    131149    }, [settings]);
    132150
     
    221239            console.error('Error:', error);
    222240            showNotification(error.message || 'Failed to update custom URL setting', 'error');
    223             setIsCustomUrlEnabled(!enabled); // Revert the toggle
     241            setIsCustomUrlEnabled(!enabled);
    224242        } finally {
    225243            setIsSaving(false);
     
    282300            console.error('Error:', error);
    283301            showNotification(error.message || 'Failed to update session timeout setting', 'error');
    284             setIsSessionTimeoutEnabled(!enabled); // Revert the toggle
     302            setIsSessionTimeoutEnabled(!enabled);
    285303        } finally {
    286304            setIsSaving(false);
     
    546564            setIsSaving(false);
    547565        }
     566    };
     567
     568    const handleChangeAdminUsernameToggle = async (enabled) => {
     569        setIsSaving(true);
     570        try {
     571            const formData = new FormData();
     572            formData.append('action', 'iron_security_toggle_change_admin_username');
     573            formData.append('enabled', enabled);
     574            formData.append('nonce', settings.nonce);
     575
     576            const response = await fetch(ajaxurl, {
     577                method: 'POST',
     578                body: formData,
     579                credentials: 'same-origin'
     580            });
     581
     582            const data = await response.json();
     583
     584            if (data.success) {
     585                setIsChangeAdminUsernameEnabled(enabled);
     586               
     587                if (enabled && data.data.admin_usernames && data.data.admin_usernames.length > 0) {
     588                    setAdminUsernameStatus({
     589                        message: `Insecure admin usernames detected: ${data.data.admin_usernames.join(', ')}. Change them to improve security.`,
     590                        type: 'warning'
     591                    });
     592                   
     593
     594                    if (!newAdminUsername) {
     595                        generateRandomUsername();
     596                    }
     597                } else if (enabled) {
     598                    setAdminUsernameStatus({
     599                        message: 'No insecure admin usernames detected.',
     600                        type: 'info'
     601                    });
     602                } else {
     603                    setAdminUsernameStatus(null);
     604                }
     605               
     606                showNotification(data.data.message || 'Change admin username setting updated successfully');
     607            } else {
     608                throw new Error(data.data || 'Failed to update setting');
     609            }
     610        } catch (error) {
     611            console.error('Error:', error);
     612            showNotification(error.message || 'Failed to update change admin username setting', 'error');
     613            setIsChangeAdminUsernameEnabled(!enabled);
     614        } finally {
     615            setIsSaving(false);
     616        }
     617    };
     618
     619    const handleSaveAdminUsername = async () => {
     620        setIsSaving(true);
     621        try {
     622            const formData = new FormData();
     623            formData.append('action', 'iron_security_save_change_admin_username');
     624            formData.append('new_username', newAdminUsername);
     625            formData.append('nonce', settings.nonce);
     626
     627            const response = await fetch(ajaxurl, {
     628                method: 'POST',
     629                body: formData,
     630                credentials: 'same-origin'
     631            });
     632
     633            const data = await response.json();
     634
     635            if (data.success) {
     636                if (data.data.changed_users && data.data.changed_users.length > 0) {
     637                    const changedUsers = data.data.changed_users;
     638                    let message = 'The following admin usernames were changed:\n';
     639                   
     640                    changedUsers.forEach(user => {
     641                        message += `• ${user.old_username} → ${user.new_username}`;
     642                        if (user.display_name) {
     643                            message += ` (${user.display_name})`;
     644                        }
     645                        message += '\n';
     646                    });
     647                   
     648                    setAdminUsernameStatus({
     649                        message: 'Admin usernames successfully changed.',
     650                        type: 'info'
     651                    });
     652                   
     653                    showNotification(data.data.message || 'Admin usernames changed successfully');
     654                    setTimeout(() => {
     655                        alert(message);
     656                    }, 500);
     657                } else {
     658                    setAdminUsernameStatus({
     659                        message: 'New admin username saved. No insecure admin usernames found to change.',
     660                        type: 'info'
     661                    });
     662                    showNotification(data.data.message || 'New admin username saved successfully');
     663                }
     664            } else {
     665                throw new Error(data.data || 'Failed to save new admin username');
     666            }
     667        } catch (error) {
     668            console.error('Error:', error);
     669            showNotification(error.message || 'Failed to save new admin username', 'error');
     670        } finally {
     671            setIsSaving(false);
     672        }
     673    };
     674
     675    const generateRandomUsername = () => {
     676        const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
     677        let result = '';
     678        const length = 8 + Math.floor(Math.random() * 8);
     679        for (let i = 0; i < length; i++) {
     680            result += chars.charAt(Math.floor(Math.random() * chars.length));
     681        }
     682        setNewAdminUsername(result);
    548683    };
    549684
     
    831966                        </div>
    832967                    )}
     968
     969                    <SettingToggle
     970                        label="Change Default Admin Username"
     971                        description="Change admin username if you have accounts with usernames like 'admin' or 'administrator' to prevent targeted attacks."
     972                        checked={isChangeAdminUsernameEnabled}
     973                        onChange={handleChangeAdminUsernameToggle}
     974                        disabled={isSaving}
     975                    />
     976
     977                    {isChangeAdminUsernameEnabled && (
     978                        <div className="wpironis-custom-url-input">
     979                            <div className="admin-username-info">
     980                                <p className="description">
     981                                    <strong>Default admin usernames like 'admin' or 'administrator' are frequently targeted in brute force attacks.</strong>
     982                                    Change them to enhance your site's security.
     983                                </p>
     984                                {adminUsernameStatus && (
     985                                    <p className={`description ${adminUsernameStatus.type}`}>
     986                                        <span className={`dashicons dashicons-${adminUsernameStatus.type === 'warning' ? 'warning' : 'info'}`}></span>
     987                                        {adminUsernameStatus.message}
     988                                    </p>
     989                                )}
     990                                <div className="wpironis-input-group">
     991                                    <label htmlFor="new-admin-username">New Username:</label>
     992                                    <input
     993                                        type="text"
     994                                        id="new-admin-username"
     995                                        value={newAdminUsername}
     996                                        onChange={(e) => setNewAdminUsername(e.target.value.replace(/[^a-zA-Z]/g, ''))}
     997                                        placeholder="Enter new username (letters only)"
     998                                        disabled={isSaving}
     999                                    />
     1000                                    <button
     1001                                        className="button button-secondary"
     1002                                        onClick={generateRandomUsername}
     1003                                        disabled={isSaving}
     1004                                        style={{ marginLeft: '5px' }}
     1005                                    >
     1006                                        Generate Random
     1007                                    </button>
     1008                                    <button
     1009                                        className="button button-primary"
     1010                                        onClick={handleSaveAdminUsername}
     1011                                        disabled={isSaving || !newAdminUsername}
     1012                                        style={{ marginLeft: '5px' }}
     1013                                    >
     1014                                        {isSaving ? 'Saving...' : 'Save Username'}
     1015                                    </button>
     1016                                </div>
     1017                            </div>
     1018                        </div>
     1019                    )}
    8331020                </div>
    8341021            </div>
  • iron-security/trunk/admin/js/dist/manifest.json

    r3296251 r3300103  
    11{
    2   "dashboard.js": "dashboard.d4938437720f6eface82.js",
     2  "dashboard.js": "dashboard.409171eb914877d3afe7.js",
    33  "vendors.js": "vendors.91782840b9e9337a9a5f.js"
    44}
  • iron-security/trunk/includes/class-iron-security.php

    r3291538 r3300103  
    8484        $this->loader->add_action( 'admin_menu', $plugin_admin, 'wpironis_plugin_menu' );
    8585
    86         // Login Logout
     86
    8787        $this->loader->add_action( 'init', $plugin_admin, 'wpironis_redirect_login' );
    8888        $this->loader->add_action( 'admin_init', $plugin_admin, 'wpironis_plugin_settings_init' );
     
    116116        $this->loader->add_filter( 'wp_handle_upload_prefilter', $plugin_admin, 'wpironis_prevent_php_upload' );
    117117
    118         // AJAX handlers
     118
    119119        $this->loader->add_action( 'wp_ajax_iron_security_toggle_xmlrpc',
    120120            $plugin_admin,
     
    149149            'wpironis_handle_core_autoupdate_toggle' );
    150150
    151         // Custom admin URL AJAX handlers
     151
    152152        $this->loader->add_action( 'wp_ajax_iron_security_toggle_custom_url',
    153153            $plugin_admin,
     
    157157            'wpironis_handle_custom_url_save' );
    158158
    159         // Session timeout AJAX handlers
     159
    160160        $this->loader->add_action( 'wp_ajax_iron_security_toggle_session_timeout',
    161161            $plugin_admin,
     
    165165            'wpironis_handle_session_timeout_save' );
    166166
    167         // Limit login attempts AJAX handlers
     167
    168168        $this->loader->add_action( 'wp_ajax_iron_security_toggle_limit_login',
    169169            $plugin_admin,
     
    173173            'wpironis_handle_limit_login_save' );
    174174
    175         // Limit admins AJAX handlers
     175
    176176        $this->loader->add_action( 'wp_ajax_iron_security_toggle_limit_admins',
    177177            $plugin_admin,
     
    181181            'wpironis_handle_limit_admins_save' );
    182182
    183         // Admin ID protection AJAX handler
     183
    184184        $this->loader->add_action( 'wp_ajax_iron_security_toggle_admin_id_protection',
    185185            $plugin_admin,
    186186            'wpironis_handle_admin_id_protection_toggle' );
    187187
    188         // Admin role limitation hook
     188        $this->loader->add_action( 'wp_ajax_iron_security_toggle_change_admin_username',
     189            $plugin_admin,
     190            'wpironis_handle_change_admin_username_toggle' );
     191        $this->loader->add_action( 'wp_ajax_iron_security_save_change_admin_username',
     192            $plugin_admin,
     193            'wpironis_handle_change_admin_username_save' );
     194
    189195        $this->loader->add_action( 'set_user_role',
    190196            $plugin_admin,
     
    192198            10,
    193199            3 );
    194         // Hook into user creation/update for admin limit
     200
    195201        $this->loader->add_action( 'user_register',
    196202            $plugin_admin,
     
    199205            1 );
    200206
    201         // REST API filter for admin limit
    202207        $this->loader->add_filter( 'rest_request_after_callbacks',
    203208            $plugin_admin,
     
    216221            'wpironis_handle_user_enum_message_save' );
    217222
    218         // Also add filters for auto-updates on plugin initialization
     223
    219224        $options = get_option( 'wpironis_options', array() );
    220225
    221         // Configure plugin auto-updates based on saved setting
     226
    222227        if ( ! empty( $options['wpironis_enable_plugin_autoupdate'] ) && $options['wpironis_enable_plugin_autoupdate'] === 1 ) {
    223228            add_filter( 'auto_update_plugin', '__return_true' );
     
    226231        }
    227232
    228         // Configure core auto-updates based on saved setting
     233
    229234        if ( ! empty( $options['wpironis_enable_core_autoupdate'] ) && $options['wpironis_enable_core_autoupdate'] === 1 ) {
    230235            add_filter( 'allow_major_auto_core_updates', '__return_true' );
     
    302307        $this->loader->add_action( 'init', $plugin_public, 'wpironis_init' );
    303308
    304         // Add REST API restriction hooks
     309
    305310        $this->loader->add_filter( 'rest_authentication_errors', $plugin_public, 'wpironis_restrict_rest_api' );
    306311        $this->loader->add_filter( 'rest_endpoints', $plugin_public, 'wpironis_disable_rest_endpoints' );
  • iron-security/trunk/iron-security.php

    r3296251 r3300103  
    1717 * Plugin URI:        https://wpiron.com
    1818 * Description:       Iron Security is a powerful WordPress security plugin to protect your site from common threats. Lock down your site with login protection, file security, and HTTP headers — all in one lightweight plugin.
    19  * Version:           2.3.2
     19 * Version:           2.3.3
    2020 * Author:            wpiron
    2121 * Author URI:        https://wpiron.com/
     
    3131}
    3232
    33 define( 'IRON_SECURITY_VERSION', '2.3.2' );
     33define( 'IRON_SECURITY_VERSION', '2.3.3' );
    3434
    3535function wpiisec_activate_iron_security() {
Note: See TracChangeset for help on using the changeset viewer.