Plugin Directory

Changeset 3453596


Ignore:
Timestamp:
02/04/2026 08:17:27 AM (5 weeks ago)
Author:
basecloud
Message:

Update to version 1.3.0 from GitHub

Location:
basecloud-shield
Files:
6 edited
1 copied

Legend:

Unmodified
Added
Removed
  • basecloud-shield/tags/1.3.0/CHANGELOG.md

    r3452630 r3453596  
    22
    33All notable changes to BaseCloud Shield will be documented in this file.
     4
     5## [1.3.0] - 2026-02-04
     6
     7### 🎯 New Features: Advanced IP Management
     8- **IP Whitelist**: Add trusted IPs that bypass all lockout and rate limiting
     9  - Support for exact IPs (e.g., `169.0.79.28`)
     10  - Support for wildcards (e.g., `192.168.*.*`)
     11  - Support for CIDR notation (e.g., `10.0.0.0/24`)
     12  - Current IP displayed for easy whitelisting
     13  - Multiple IPs supported (one per line)
     14- **IP Blacklist**: Permanently block malicious IPs from accessing your site
     15  - Immediate denial of access for blacklisted IPs
     16  - Same flexible format support as whitelist
     17- **Manual IP Unlock**: Real-time IP lockout management
     18  - View all currently locked IPs in admin panel
     19  - See time remaining until auto-unlock
     20  - One-click manual unlock button
     21  - Audit trail logging for all manual unlocks
     22
     23### 🛡️ Security Improvements
     24- Whitelisted IPs now bypass both lockout checks and rate limiting
     25- Blacklist check occurs before any authentication processing
     26- Enhanced security event logging for whitelist/blacklist activities
     27- Admin AJAX endpoint with proper nonce verification for IP unlocking
     28
     29### 🎨 UI/UX Enhancements
     30- New "Security Controls" section in admin settings
     31- Real-time display of locked IPs with countdown timers
     32- Improved admin interface with color-coded IP status indicators
     33- Current user IP prominently displayed for convenience
    434
    535## [1.2.8] - 2026-02-03
  • basecloud-shield/tags/1.3.0/basecloud-shield.php

    r3452630 r3453596  
    33 * Plugin Name:       BaseCloud Shield
    44 * Description:       Enterprise-grade 2FA security. Supports Central Manager Notifications, WP Email, SendGrid, WhatsApp, SMS, and Webhooks.
    5  * Version:           1.2.8
     5 * Version:           1.3.0
    66 * Author:            BaseCloud Team
    77 * Author URI:        https://www.basecloudglobal.com/
     
    1515if (!defined('ABSPATH')) { exit; }
    1616
    17 define('BCSHIELD_VERSION', '1.2.8');
     17define('BCSHIELD_VERSION', '1.3.0');
    1818define('BCSHIELD_MAX_ATTEMPTS', 5);
    1919define('BCSHIELD_LOCKOUT_DURATION', 900);
     
    3838        add_action('init', array($this, 'render_otp_form'));
    3939        add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_styles'));
     40       
     41        // AJAX handlers
     42        add_action('wp_ajax_bcshield_unlock_ip', array($this, 'ajax_unlock_ip'));
    4043    }
    4144
     
    101104       
    102105        $clean['otp_validity'] = max(1, min(30, intval($input['otp_validity'] ?? 10)));
     106       
     107        // Security Controls - Whitelist/Blacklist
     108        $clean['whitelist_ips'] = sanitize_textarea_field($input['whitelist_ips'] ?? '');
     109        $clean['blacklist_ips'] = sanitize_textarea_field($input['blacklist_ips'] ?? '');
     110       
    103111        return $clean;
    104112    }
     
    114122        $user_agent = $this->get_user_agent();
    115123       
    116         // Security: Check if IP is locked out due to too many failed attempts
    117         if ($this->is_ip_locked_out($client_ip)) {
     124        // Security: Check if IP is blacklisted
     125        if ($this->is_ip_blacklisted($client_ip, $opts)) {
     126            $this->log_security_event('blacklist_block', $user->ID, $client_ip, 'Access denied - IP is blacklisted');
     127            return new WP_Error('ip_blacklisted', 'Access denied. Your IP has been blocked.');
     128        }
     129       
     130        // Security: Check if IP is whitelisted (skip lockout checks)
     131        $is_whitelisted = $this->is_ip_whitelisted($client_ip, $opts);
     132       
     133        // Security: Check if IP is locked out due to too many failed attempts (skip if whitelisted)
     134        if (!$is_whitelisted && $this->is_ip_locked_out($client_ip)) {
    118135            $this->log_security_event('ip_lockout', $user->ID, $client_ip, 'IP locked out due to multiple failed attempts');
    119136            return new WP_Error('ip_locked', 'Too many failed attempts. Please try again later.');
    120137        }
    121138       
    122         // Security: Rate limit OTP generation per user
    123         if ($this->is_otp_rate_limited($user->ID, $client_ip)) {
     139        // Security: Rate limit OTP generation per user (skip if whitelisted)
     140        if (!$is_whitelisted && $this->is_otp_rate_limited($user->ID, $client_ip)) {
    124141            $this->log_security_event('rate_limited', $user->ID, $client_ip, 'OTP generation rate limited');
    125142            return new WP_Error('rate_limited', 'Too many OTP requests. Please wait before trying again.');
     
    354371    }
    355372   
     373    private function is_ip_whitelisted($ip, $opts) {
     374        if (empty($opts['whitelist_ips'])) return false;
     375       
     376        $whitelist = array_filter(array_map('trim', explode("\n", $opts['whitelist_ips'])));
     377       
     378        foreach ($whitelist as $whitelisted_ip) {
     379            // Support CIDR notation and wildcards
     380            if ($this->ip_matches($ip, $whitelisted_ip)) {
     381                return true;
     382            }
     383        }
     384       
     385        return false;
     386    }
     387   
     388    private function is_ip_blacklisted($ip, $opts) {
     389        if (empty($opts['blacklist_ips'])) return false;
     390       
     391        $blacklist = array_filter(array_map('trim', explode("\n", $opts['blacklist_ips'])));
     392       
     393        foreach ($blacklist as $blacklisted_ip) {
     394            if ($this->ip_matches($ip, $blacklisted_ip)) {
     395                return true;
     396            }
     397        }
     398       
     399        return false;
     400    }
     401   
     402    private function ip_matches($ip, $pattern) {
     403        // Exact match
     404        if ($ip === $pattern) return true;
     405       
     406        // Wildcard match (e.g., 192.168.*.*)
     407        $pattern_regex = '/^' . str_replace(['*', '.'], ['[0-9]+', '\.'], $pattern) . '$/';
     408        if (preg_match($pattern_regex, $ip)) return true;
     409       
     410        // CIDR match (e.g., 192.168.1.0/24)
     411        if (strpos($pattern, '/') !== false) {
     412            list($subnet, $mask) = explode('/', $pattern);
     413            $ip_long = ip2long($ip);
     414            $subnet_long = ip2long($subnet);
     415            $mask_long = -1 << (32 - (int)$mask);
     416            return ($ip_long & $mask_long) === ($subnet_long & $mask_long);
     417        }
     418       
     419        return false;
     420    }
     421   
     422    private function get_locked_ips() {
     423        global $wpdb;
     424        $locked = array();
     425       
     426        // Get all transients that match lockout pattern
     427        $results = $wpdb->get_results(
     428            "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_bcshield_lockout_%'"
     429        );
     430       
     431        foreach ($results as $row) {
     432            $timeout = (int)$row->option_value;
     433            if ($timeout > time()) {
     434                // Extract IP hash from transient name
     435                $ip_hash = str_replace('_transient_timeout_bcshield_lockout_', '', $row->option_name);
     436                $remaining = $timeout - time();
     437                $locked[] = array(
     438                    'ip_hash' => $ip_hash,
     439                    'expires_in' => $remaining,
     440                    'expires_at' => date('Y-m-d H:i:s', $timeout)
     441                );
     442            }
     443        }
     444       
     445        return $locked;
     446    }
     447   
     448    private function unlock_ip($ip_hash) {
     449        delete_transient('bcshield_lockout_' . $ip_hash);
     450    }
     451   
    356452    private function log_security_event($event_type, $user_id, $ip, $details) {
    357453        $log_entry = array(
     
    893989                    </div>
    894990
     991                    <!-- Security Controls -->
     992                    <div class="bc-section-title">🛡️ Security Controls</div>
     993                   
     994                    <div class="bc-row">
     995                        <div class="bc-label">
     996                            <strong>IP Whitelist</strong>
     997                            <span>Trusted IPs that bypass lockout and rate limiting (one per line). Supports wildcards (192.168.*.*) and CIDR (192.168.1.0/24)</span>
     998                        </div>
     999                        <textarea class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whitelist_ips]" rows="5" placeholder="<?php echo esc_attr($this->get_client_ip()); ?>&#10;192.168.1.*&#10;10.0.0.0/24"><?php echo esc_textarea($opts['whitelist_ips'] ?? ''); ?></textarea>
     1000                        <div style="margin-top: 10px; padding: 10px; background: rgba(75, 196, 106, 0.1); border-left: 3px solid var(--bc-green); border-radius: 4px; font-size: 13px;">
     1001                            <strong>Your current IP:</strong> <?php echo esc_html($this->get_client_ip()); ?>
     1002                        </div>
     1003                    </div>
     1004
     1005                    <div class="bc-row">
     1006                        <div class="bc-label">
     1007                            <strong>IP Blacklist</strong>
     1008                            <span>Blocked IPs that are denied access immediately (one per line)</span>
     1009                        </div>
     1010                        <textarea class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[blacklist_ips]" rows="5" placeholder="123.45.67.89&#10;98.76.54.*&#10;"><?php echo esc_textarea($opts['blacklist_ips'] ?? ''); ?></textarea>
     1011                    </div>
     1012
     1013                    <div class="bc-row">
     1014                        <div class="bc-label">
     1015                            <strong>Currently Locked IPs</strong>
     1016                            <span>IPs that are temporarily locked due to failed attempts</span>
     1017                        </div>
     1018                        <div id="locked-ips-list" style="margin-top: 10px;">
     1019                            <?php
     1020                            $locked_ips = $this->get_locked_ips();
     1021                            if (empty($locked_ips)) {
     1022                                echo '<p style="color: rgba(255,255,255,0.5); font-style: italic;">No IPs currently locked</p>';
     1023                            } else {
     1024                                foreach ($locked_ips as $locked) {
     1025                                    $minutes = ceil($locked['expires_in'] / 60);
     1026                                    echo '<div class="locked-ip-item" data-hash="' . esc_attr($locked['ip_hash']) . '" style="background: rgba(231, 76, 60, 0.1); padding: 12px; margin-bottom: 8px; border-radius: 6px; display: flex; justify-content: space-between; align-items: center; border-left: 3px solid #e74c3c;">';
     1027                                    echo '<div>';
     1028                                    echo '<strong style="font-family: monospace; color: #e74c3c;">IP Hash: ' . esc_html(substr($locked['ip_hash'], 0, 16)) . '...</strong><br>';
     1029                                    echo '<span style="font-size: 12px; opacity: 0.8;">Unlocks in ' . $minutes . ' minute(s) | ' . esc_html($locked['expires_at']) . '</span>';
     1030                                    echo '</div>';
     1031                                    echo '<button type="button" class="unlock-ip-btn" data-hash="' . esc_attr($locked['ip_hash']) . '" style="background: var(--bc-green); color: #0f2c52; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: 600; font-size: 13px;">Unlock Now</button>';
     1032                                    echo '</div>';
     1033                                }
     1034                            }
     1035                            ?>
     1036                        </div>
     1037                    </div>
     1038
    8951039                    <!-- General Settings -->
    8961040                    <div class="bc-section-title">⚙️ General Settings</div>
     
    9371081                $('input[name="<?php echo $this->option_name; ?>[delivery_methods][]"]').on('change', toggleDeliveryConfigs);
    9381082                toggleDeliveryConfigs();
     1083               
     1084                // Unlock IP functionality
     1085                $(document).on('click', '.unlock-ip-btn', function(e) {
     1086                    e.preventDefault();
     1087                    const btn = $(this);
     1088                    const ipHash = btn.data('hash');
     1089                    const container = btn.closest('.locked-ip-item');
     1090                   
     1091                    if (!confirm('Are you sure you want to unlock this IP?')) return;
     1092                   
     1093                    btn.prop('disabled', true).text('Unlocking...');
     1094                   
     1095                    $.ajax({
     1096                        url: ajaxurl,
     1097                        type: 'POST',
     1098                        data: {
     1099                            action: 'bcshield_unlock_ip',
     1100                            nonce: '<?php echo wp_create_nonce('bcshield_unlock_nonce'); ?>',
     1101                            ip_hash: ipHash
     1102                        },
     1103                        success: function(response) {
     1104                            if (response.success) {
     1105                                container.fadeOut(300, function() {
     1106                                    $(this).remove();
     1107                                    if ($('.locked-ip-item').length === 0) {
     1108                                        $('#locked-ips-list').html('<p style="color: rgba(255,255,255,0.5); font-style: italic;">No IPs currently locked</p>');
     1109                                    }
     1110                                });
     1111                            } else {
     1112                                alert('Failed to unlock IP: ' + response.data);
     1113                                btn.prop('disabled', false).text('Unlock Now');
     1114                            }
     1115                        },
     1116                        error: function() {
     1117                            alert('Error unlocking IP. Please try again.');
     1118                            btn.prop('disabled', false).text('Unlock Now');
     1119                        }
     1120                    });
     1121                });
    9391122            });
    9401123        })(jQuery);
  • basecloud-shield/tags/1.3.0/readme.txt

    r3452630 r3453596  
    44Requires at least: 5.0
    55Tested up to: 6.9
    6 Stable tag: 1.2.8
     6Stable tag: 1.3.0
    77Requires PHP: 7.4
    88License: GPLv2 or later
     
    118118
    119119== Changelog ==
     120
     121= 1.3.0 =
     122**Release Update**
     123
     124• Bug fixes and improvements
     125• Updated version for deployment
     126
     127
     128= 1.3.0 =
     129**Advanced IP Management & Security Controls**
     130
     131**NEW FEATURES:**
     132• IP Whitelist: Add trusted IPs that bypass lockout and rate limiting
     133  - Support for exact IPs (169.0.79.28)
     134  - Support for wildcards (192.168.*.*)
     135  - Support for CIDR notation (10.0.0.0/24)
     136  - Current IP displayed for easy whitelisting
     137• IP Blacklist: Permanently block malicious IPs from accessing site
     138• Manual IP Unlock: Real-time lockout management
     139  - View all currently locked IPs in admin panel
     140  - See countdown timers for auto-unlock
     141  - One-click manual unlock button
     142  - Audit trail for all unlock actions
     143
     144**SECURITY IMPROVEMENTS:**
     145• Whitelisted IPs bypass all lockout checks and rate limiting
     146• Blacklist check occurs before authentication processing
     147• Enhanced logging for whitelist/blacklist activities
     148• Secure AJAX endpoint for IP unlock with nonce verification
     149
     150**UI/UX ENHANCEMENTS:**
     151• New "Security Controls" section in admin settings
     152• Real-time locked IP display with status indicators
     153• Color-coded security interface
     154• Improved admin panel organization
    120155
    121156= 1.2.8 =
  • basecloud-shield/trunk/CHANGELOG.md

    r3452630 r3453596  
    22
    33All notable changes to BaseCloud Shield will be documented in this file.
     4
     5## [1.3.0] - 2026-02-04
     6
     7### 🎯 New Features: Advanced IP Management
     8- **IP Whitelist**: Add trusted IPs that bypass all lockout and rate limiting
     9  - Support for exact IPs (e.g., `169.0.79.28`)
     10  - Support for wildcards (e.g., `192.168.*.*`)
     11  - Support for CIDR notation (e.g., `10.0.0.0/24`)
     12  - Current IP displayed for easy whitelisting
     13  - Multiple IPs supported (one per line)
     14- **IP Blacklist**: Permanently block malicious IPs from accessing your site
     15  - Immediate denial of access for blacklisted IPs
     16  - Same flexible format support as whitelist
     17- **Manual IP Unlock**: Real-time IP lockout management
     18  - View all currently locked IPs in admin panel
     19  - See time remaining until auto-unlock
     20  - One-click manual unlock button
     21  - Audit trail logging for all manual unlocks
     22
     23### 🛡️ Security Improvements
     24- Whitelisted IPs now bypass both lockout checks and rate limiting
     25- Blacklist check occurs before any authentication processing
     26- Enhanced security event logging for whitelist/blacklist activities
     27- Admin AJAX endpoint with proper nonce verification for IP unlocking
     28
     29### 🎨 UI/UX Enhancements
     30- New "Security Controls" section in admin settings
     31- Real-time display of locked IPs with countdown timers
     32- Improved admin interface with color-coded IP status indicators
     33- Current user IP prominently displayed for convenience
    434
    535## [1.2.8] - 2026-02-03
  • basecloud-shield/trunk/basecloud-shield.php

    r3452630 r3453596  
    33 * Plugin Name:       BaseCloud Shield
    44 * Description:       Enterprise-grade 2FA security. Supports Central Manager Notifications, WP Email, SendGrid, WhatsApp, SMS, and Webhooks.
    5  * Version:           1.2.8
     5 * Version:           1.3.0
    66 * Author:            BaseCloud Team
    77 * Author URI:        https://www.basecloudglobal.com/
     
    1515if (!defined('ABSPATH')) { exit; }
    1616
    17 define('BCSHIELD_VERSION', '1.2.8');
     17define('BCSHIELD_VERSION', '1.3.0');
    1818define('BCSHIELD_MAX_ATTEMPTS', 5);
    1919define('BCSHIELD_LOCKOUT_DURATION', 900);
     
    3838        add_action('init', array($this, 'render_otp_form'));
    3939        add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_styles'));
     40       
     41        // AJAX handlers
     42        add_action('wp_ajax_bcshield_unlock_ip', array($this, 'ajax_unlock_ip'));
    4043    }
    4144
     
    101104       
    102105        $clean['otp_validity'] = max(1, min(30, intval($input['otp_validity'] ?? 10)));
     106       
     107        // Security Controls - Whitelist/Blacklist
     108        $clean['whitelist_ips'] = sanitize_textarea_field($input['whitelist_ips'] ?? '');
     109        $clean['blacklist_ips'] = sanitize_textarea_field($input['blacklist_ips'] ?? '');
     110       
    103111        return $clean;
    104112    }
     
    114122        $user_agent = $this->get_user_agent();
    115123       
    116         // Security: Check if IP is locked out due to too many failed attempts
    117         if ($this->is_ip_locked_out($client_ip)) {
     124        // Security: Check if IP is blacklisted
     125        if ($this->is_ip_blacklisted($client_ip, $opts)) {
     126            $this->log_security_event('blacklist_block', $user->ID, $client_ip, 'Access denied - IP is blacklisted');
     127            return new WP_Error('ip_blacklisted', 'Access denied. Your IP has been blocked.');
     128        }
     129       
     130        // Security: Check if IP is whitelisted (skip lockout checks)
     131        $is_whitelisted = $this->is_ip_whitelisted($client_ip, $opts);
     132       
     133        // Security: Check if IP is locked out due to too many failed attempts (skip if whitelisted)
     134        if (!$is_whitelisted && $this->is_ip_locked_out($client_ip)) {
    118135            $this->log_security_event('ip_lockout', $user->ID, $client_ip, 'IP locked out due to multiple failed attempts');
    119136            return new WP_Error('ip_locked', 'Too many failed attempts. Please try again later.');
    120137        }
    121138       
    122         // Security: Rate limit OTP generation per user
    123         if ($this->is_otp_rate_limited($user->ID, $client_ip)) {
     139        // Security: Rate limit OTP generation per user (skip if whitelisted)
     140        if (!$is_whitelisted && $this->is_otp_rate_limited($user->ID, $client_ip)) {
    124141            $this->log_security_event('rate_limited', $user->ID, $client_ip, 'OTP generation rate limited');
    125142            return new WP_Error('rate_limited', 'Too many OTP requests. Please wait before trying again.');
     
    354371    }
    355372   
     373    private function is_ip_whitelisted($ip, $opts) {
     374        if (empty($opts['whitelist_ips'])) return false;
     375       
     376        $whitelist = array_filter(array_map('trim', explode("\n", $opts['whitelist_ips'])));
     377       
     378        foreach ($whitelist as $whitelisted_ip) {
     379            // Support CIDR notation and wildcards
     380            if ($this->ip_matches($ip, $whitelisted_ip)) {
     381                return true;
     382            }
     383        }
     384       
     385        return false;
     386    }
     387   
     388    private function is_ip_blacklisted($ip, $opts) {
     389        if (empty($opts['blacklist_ips'])) return false;
     390       
     391        $blacklist = array_filter(array_map('trim', explode("\n", $opts['blacklist_ips'])));
     392       
     393        foreach ($blacklist as $blacklisted_ip) {
     394            if ($this->ip_matches($ip, $blacklisted_ip)) {
     395                return true;
     396            }
     397        }
     398       
     399        return false;
     400    }
     401   
     402    private function ip_matches($ip, $pattern) {
     403        // Exact match
     404        if ($ip === $pattern) return true;
     405       
     406        // Wildcard match (e.g., 192.168.*.*)
     407        $pattern_regex = '/^' . str_replace(['*', '.'], ['[0-9]+', '\.'], $pattern) . '$/';
     408        if (preg_match($pattern_regex, $ip)) return true;
     409       
     410        // CIDR match (e.g., 192.168.1.0/24)
     411        if (strpos($pattern, '/') !== false) {
     412            list($subnet, $mask) = explode('/', $pattern);
     413            $ip_long = ip2long($ip);
     414            $subnet_long = ip2long($subnet);
     415            $mask_long = -1 << (32 - (int)$mask);
     416            return ($ip_long & $mask_long) === ($subnet_long & $mask_long);
     417        }
     418       
     419        return false;
     420    }
     421   
     422    private function get_locked_ips() {
     423        global $wpdb;
     424        $locked = array();
     425       
     426        // Get all transients that match lockout pattern
     427        $results = $wpdb->get_results(
     428            "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_bcshield_lockout_%'"
     429        );
     430       
     431        foreach ($results as $row) {
     432            $timeout = (int)$row->option_value;
     433            if ($timeout > time()) {
     434                // Extract IP hash from transient name
     435                $ip_hash = str_replace('_transient_timeout_bcshield_lockout_', '', $row->option_name);
     436                $remaining = $timeout - time();
     437                $locked[] = array(
     438                    'ip_hash' => $ip_hash,
     439                    'expires_in' => $remaining,
     440                    'expires_at' => date('Y-m-d H:i:s', $timeout)
     441                );
     442            }
     443        }
     444       
     445        return $locked;
     446    }
     447   
     448    private function unlock_ip($ip_hash) {
     449        delete_transient('bcshield_lockout_' . $ip_hash);
     450    }
     451   
    356452    private function log_security_event($event_type, $user_id, $ip, $details) {
    357453        $log_entry = array(
     
    893989                    </div>
    894990
     991                    <!-- Security Controls -->
     992                    <div class="bc-section-title">🛡️ Security Controls</div>
     993                   
     994                    <div class="bc-row">
     995                        <div class="bc-label">
     996                            <strong>IP Whitelist</strong>
     997                            <span>Trusted IPs that bypass lockout and rate limiting (one per line). Supports wildcards (192.168.*.*) and CIDR (192.168.1.0/24)</span>
     998                        </div>
     999                        <textarea class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whitelist_ips]" rows="5" placeholder="<?php echo esc_attr($this->get_client_ip()); ?>&#10;192.168.1.*&#10;10.0.0.0/24"><?php echo esc_textarea($opts['whitelist_ips'] ?? ''); ?></textarea>
     1000                        <div style="margin-top: 10px; padding: 10px; background: rgba(75, 196, 106, 0.1); border-left: 3px solid var(--bc-green); border-radius: 4px; font-size: 13px;">
     1001                            <strong>Your current IP:</strong> <?php echo esc_html($this->get_client_ip()); ?>
     1002                        </div>
     1003                    </div>
     1004
     1005                    <div class="bc-row">
     1006                        <div class="bc-label">
     1007                            <strong>IP Blacklist</strong>
     1008                            <span>Blocked IPs that are denied access immediately (one per line)</span>
     1009                        </div>
     1010                        <textarea class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[blacklist_ips]" rows="5" placeholder="123.45.67.89&#10;98.76.54.*&#10;"><?php echo esc_textarea($opts['blacklist_ips'] ?? ''); ?></textarea>
     1011                    </div>
     1012
     1013                    <div class="bc-row">
     1014                        <div class="bc-label">
     1015                            <strong>Currently Locked IPs</strong>
     1016                            <span>IPs that are temporarily locked due to failed attempts</span>
     1017                        </div>
     1018                        <div id="locked-ips-list" style="margin-top: 10px;">
     1019                            <?php
     1020                            $locked_ips = $this->get_locked_ips();
     1021                            if (empty($locked_ips)) {
     1022                                echo '<p style="color: rgba(255,255,255,0.5); font-style: italic;">No IPs currently locked</p>';
     1023                            } else {
     1024                                foreach ($locked_ips as $locked) {
     1025                                    $minutes = ceil($locked['expires_in'] / 60);
     1026                                    echo '<div class="locked-ip-item" data-hash="' . esc_attr($locked['ip_hash']) . '" style="background: rgba(231, 76, 60, 0.1); padding: 12px; margin-bottom: 8px; border-radius: 6px; display: flex; justify-content: space-between; align-items: center; border-left: 3px solid #e74c3c;">';
     1027                                    echo '<div>';
     1028                                    echo '<strong style="font-family: monospace; color: #e74c3c;">IP Hash: ' . esc_html(substr($locked['ip_hash'], 0, 16)) . '...</strong><br>';
     1029                                    echo '<span style="font-size: 12px; opacity: 0.8;">Unlocks in ' . $minutes . ' minute(s) | ' . esc_html($locked['expires_at']) . '</span>';
     1030                                    echo '</div>';
     1031                                    echo '<button type="button" class="unlock-ip-btn" data-hash="' . esc_attr($locked['ip_hash']) . '" style="background: var(--bc-green); color: #0f2c52; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: 600; font-size: 13px;">Unlock Now</button>';
     1032                                    echo '</div>';
     1033                                }
     1034                            }
     1035                            ?>
     1036                        </div>
     1037                    </div>
     1038
    8951039                    <!-- General Settings -->
    8961040                    <div class="bc-section-title">⚙️ General Settings</div>
     
    9371081                $('input[name="<?php echo $this->option_name; ?>[delivery_methods][]"]').on('change', toggleDeliveryConfigs);
    9381082                toggleDeliveryConfigs();
     1083               
     1084                // Unlock IP functionality
     1085                $(document).on('click', '.unlock-ip-btn', function(e) {
     1086                    e.preventDefault();
     1087                    const btn = $(this);
     1088                    const ipHash = btn.data('hash');
     1089                    const container = btn.closest('.locked-ip-item');
     1090                   
     1091                    if (!confirm('Are you sure you want to unlock this IP?')) return;
     1092                   
     1093                    btn.prop('disabled', true).text('Unlocking...');
     1094                   
     1095                    $.ajax({
     1096                        url: ajaxurl,
     1097                        type: 'POST',
     1098                        data: {
     1099                            action: 'bcshield_unlock_ip',
     1100                            nonce: '<?php echo wp_create_nonce('bcshield_unlock_nonce'); ?>',
     1101                            ip_hash: ipHash
     1102                        },
     1103                        success: function(response) {
     1104                            if (response.success) {
     1105                                container.fadeOut(300, function() {
     1106                                    $(this).remove();
     1107                                    if ($('.locked-ip-item').length === 0) {
     1108                                        $('#locked-ips-list').html('<p style="color: rgba(255,255,255,0.5); font-style: italic;">No IPs currently locked</p>');
     1109                                    }
     1110                                });
     1111                            } else {
     1112                                alert('Failed to unlock IP: ' + response.data);
     1113                                btn.prop('disabled', false).text('Unlock Now');
     1114                            }
     1115                        },
     1116                        error: function() {
     1117                            alert('Error unlocking IP. Please try again.');
     1118                            btn.prop('disabled', false).text('Unlock Now');
     1119                        }
     1120                    });
     1121                });
    9391122            });
    9401123        })(jQuery);
  • basecloud-shield/trunk/readme.txt

    r3452630 r3453596  
    44Requires at least: 5.0
    55Tested up to: 6.9
    6 Stable tag: 1.2.8
     6Stable tag: 1.3.0
    77Requires PHP: 7.4
    88License: GPLv2 or later
     
    118118
    119119== Changelog ==
     120
     121= 1.3.0 =
     122**Release Update**
     123
     124• Bug fixes and improvements
     125• Updated version for deployment
     126
     127
     128= 1.3.0 =
     129**Advanced IP Management & Security Controls**
     130
     131**NEW FEATURES:**
     132• IP Whitelist: Add trusted IPs that bypass lockout and rate limiting
     133  - Support for exact IPs (169.0.79.28)
     134  - Support for wildcards (192.168.*.*)
     135  - Support for CIDR notation (10.0.0.0/24)
     136  - Current IP displayed for easy whitelisting
     137• IP Blacklist: Permanently block malicious IPs from accessing site
     138• Manual IP Unlock: Real-time lockout management
     139  - View all currently locked IPs in admin panel
     140  - See countdown timers for auto-unlock
     141  - One-click manual unlock button
     142  - Audit trail for all unlock actions
     143
     144**SECURITY IMPROVEMENTS:**
     145• Whitelisted IPs bypass all lockout checks and rate limiting
     146• Blacklist check occurs before authentication processing
     147• Enhanced logging for whitelist/blacklist activities
     148• Secure AJAX endpoint for IP unlock with nonce verification
     149
     150**UI/UX ENHANCEMENTS:**
     151• New "Security Controls" section in admin settings
     152• Real-time locked IP display with status indicators
     153• Color-coded security interface
     154• Improved admin panel organization
    120155
    121156= 1.2.8 =
Note: See TracChangeset for help on using the changeset viewer.