Plugin Directory

Changeset 3480349


Ignore:
Timestamp:
03/11/2026 04:10:04 PM (3 weeks ago)
Author:
shift8
Message:

Extend ban, ban mechanism improvements, additional tests

Location:
atomic-edge-security/trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • atomic-edge-security/trunk/admin/js/adaptive-defense.js

    r3476055 r3480349  
    547547                html += '<td>';
    548548                if (!isPermanent) {
    549                     html += '<button type="button" class="button button-small atomicedge-ad-extend-block-btn" data-ip="' + self.escapeHtml(ip) + '" title="Extend +1 day">';
     549                    html += '<button type="button" class="button button-small atomicedge-ad-extend-block-btn" data-ip="' + self.escapeHtml(ip) + '" title="Extend block">';
    550550                    html += '<span class="dashicons dashicons-clock" style="margin-top:3px;"></span></button> ';
    551                     html += '<button type="button" class="button button-small atomicedge-ad-make-permanent-btn" data-ip="' + self.escapeHtml(ip) + '" title="Make permanent">';
    552                     html += '<span class="dashicons dashicons-lock" style="margin-top:3px;"></span></button> ';
     551                    if (atomicedge_admin.can_permanent_block) {
     552                        html += '<button type="button" class="button button-small atomicedge-ad-make-permanent-btn" data-ip="' + self.escapeHtml(ip) + '" title="Make permanent">';
     553                        html += '<span class="dashicons dashicons-lock" style="margin-top:3px;"></span></button> ';
     554                    } else {
     555                        html += '<button type="button" class="button button-small" disabled title="Permanent blocks are a Pro feature. Upgrade your plan to enable this." style="opacity:0.5;cursor:not-allowed;">';
     556                        html += '<span class="dashicons dashicons-lock" style="margin-top:3px;"></span></button> ';
     557                    }
    553558                }
    554559                html += '<button type="button" class="button button-small atomicedge-ad-unblock-btn" data-ip="' + self.escapeHtml(ip) + '" title="Unblock">';
     
    595600
    596601        /**
    597          * Extend a timed block by 1 day.
     602         * Extend a timed block using the site's configured duration.
    598603         *
    599604         * @param {string} ip IP address
     
    608613                    action: 'atomicedge_extend_block',
    609614                    nonce: atomicedge_admin.nonce,
    610                     ip: ip,
    611                     days: 1
     615                    ip: ip
    612616                },
    613617                success: function(response) {
    614618                    if (response.success) {
    615                         self.showNotice('Block for ' + ip + ' extended by 1 day.', 'success');
     619                        var msg = response.data && response.data.message
     620                            ? response.data.message
     621                            : 'Block for ' + ip + ' has been extended.';
     622                        self.showNotice(msg, 'success');
    616623                        self.loadBlockedIps();
    617624                    } else {
     
    632639        makePermanent: function(ip) {
    633640            var self = this;
     641
     642            // Client-side plan gate (defense in depth — server also enforces).
     643            if (!atomicedge_admin.can_permanent_block) {
     644                self.showNotice('Permanent blocks are a Pro feature. Please upgrade your plan to enable this.', 'warning');
     645                return;
     646            }
    634647
    635648            if (!confirm('Make the block for ' + ip + ' permanent?')) {
     
    650663                        self.loadBlockedIps();
    651664                    } else {
    652                         self.showNotice(response.data ? response.data.message : 'Failed to make block permanent', 'error');
     665                        var msg = response.data ? response.data.message : 'Failed to make block permanent';
     666                        var type = 'error';
     667                        // Detect plan-limit error from API (defense-in-depth).
     668                        if (response.data && response.data.error_code === 'plan_limit') {
     669                            msg = 'Permanent blocks are a Pro feature. Please upgrade your plan to enable this.';
     670                            type = 'warning';
     671                        }
     672                        self.showNotice(msg, type);
    653673                    }
    654674                },
  • atomic-edge-security/trunk/admin/js/admin.js

    r3476684 r3480349  
    595595            this.ajax('atomicedge_block_ip', {
    596596                ip: ip,
    597                 duration_hours: 24,
    598597                reason: 'Blocked from WAF logs'
    599598            }, function() {
  • atomic-edge-security/trunk/includes/class-atomicedge-ajax.php

    r3476684 r3480349  
    10131013     */
    10141014    public function ajax_block_ip() {
    1015         $post = $this->get_verified_post_fields( array( 'ip', 'duration_hours', 'permanent', 'reason' ) );
     1015        $post = $this->get_verified_post_fields( array( 'ip', 'permanent', 'reason' ) );
    10161016
    10171017        if ( empty( $post['ip'] ) ) {
     
    10191019        }
    10201020
    1021         $ip             = $post['ip'];
    1022         $duration_hours = isset( $post['duration_hours'] ) ? absint( $post['duration_hours'] ) : 24;
    1023         $permanent      = isset( $post['permanent'] ) && 'true' === $post['permanent'];
    1024         $reason         = isset( $post['reason'] ) ? sanitize_text_field( $post['reason'] ) : '';
     1021        $ip        = $post['ip'];
     1022        $permanent = isset( $post['permanent'] ) && 'true' === $post['permanent'];
     1023        $reason    = isset( $post['reason'] ) ? sanitize_text_field( $post['reason'] ) : '';
    10251024
    10261025        // Dev mode: return simulated success (only when not connected to real API).
     
    10361035        }
    10371036
    1038         $response = $this->api->block_ip( $ip, $duration_hours, $permanent, $reason );
     1037        $response = $this->api->block_ip( $ip, $permanent, $reason );
    10391038
    10401039        if ( $response['success'] ) {
     
    10921091    /**
    10931092     * Extend the block duration for a blocked IP.
     1093     * Duration is server-authoritative (uses the site's configured auto_block_ttl_hours).
    10941094     *
    10951095     * @return void
    10961096     */
    10971097    public function ajax_extend_block() {
    1098         $post = $this->get_verified_post_fields( array( 'ip', 'days' ) );
     1098        $post = $this->get_verified_post_fields( array( 'ip' ) );
    10991099
    11001100        if ( empty( $post['ip'] ) ) {
     
    11021102        }
    11031103
    1104         $ip   = $post['ip'];
    1105         $days = isset( $post['days'] ) ? max( 1, absint( $post['days'] ) ) : 1;
     1104        $ip = $post['ip'];
    11061105
    11071106        // Dev mode: return simulated success (only when not connected to real API).
     
    11091108            wp_send_json_success( array(
    11101109                'message' => sprintf(
    1111                     /* translators: 1: IP address, 2: number of days */
    1112                     __( '[Dev Mode] Block for %1$s extended by %2$d day(s).', 'atomic-edge-security' ),
    1113                     esc_html( $ip ),
    1114                     $days
     1110                    /* translators: %s: IP address */
     1111                    __( '[Dev Mode] Block for %s has been extended.', 'atomic-edge-security' ),
     1112                    esc_html( $ip )
    11151113                ),
    1116                 'data' => AtomicEdge_Dev_Mode::simulate_extend_block( $ip, $days ),
    1117             ) );
    1118         }
    1119 
    1120         $response = $this->api->extend_block( $ip, $days );
     1114                'data' => AtomicEdge_Dev_Mode::simulate_extend_block( $ip ),
     1115            ) );
     1116        }
     1117
     1118        $response = $this->api->extend_block( $ip );
    11211119
    11221120        if ( $response['success'] ) {
    11231121            wp_send_json_success( array(
    11241122                'message' => sprintf(
    1125                     /* translators: 1: IP address, 2: number of days */
    1126                     __( 'Block for %1$s extended by %2$d day(s).', 'atomic-edge-security' ),
    1127                     esc_html( $ip ),
    1128                     $days
     1123                    /* translators: %s: IP address */
     1124                    __( 'Block for %s has been extended.', 'atomic-edge-security' ),
     1125                    esc_html( $ip )
    11291126                ),
    11301127                'data' => $response['data'] ?? array(),
     
    11731170            ) );
    11741171        } else {
    1175             wp_send_json_error( array( 'message' => $response['error'] ?? __( 'Failed to make block permanent.', 'atomic-edge-security' ) ) );
     1172            $error_data = array( 'message' => $response['error'] ?? __( 'Failed to make block permanent.', 'atomic-edge-security' ) );
     1173            if ( ! empty( $response['error_code'] ) ) {
     1174                $error_data['error_code'] = sanitize_text_field( $response['error_code'] );
     1175            }
     1176            wp_send_json_error( $error_data );
    11761177        }
    11771178    }
  • atomic-edge-security/trunk/includes/class-atomicedge-api.php

    r3476684 r3480349  
    622622    /**
    623623     * Block an IP address via Adaptive Defense.
    624      *
    625      * @param string $ip             IP address to block.
    626      * @param int    $duration_hours Duration in hours (default 24).
    627      * @param bool   $permanent      Whether the block is permanent.
     624     * Duration is server-authoritative (uses the site's configured auto_block_ttl_hours).
     625     *
     626     * @param string $ip        IP address to block.
     627     * @param bool   $permanent Whether the block is permanent.
     628     * @param string $reason    Optional reason for blocking.
    628629     * @return array Result.
    629630     */
    630     public function block_ip( $ip, $duration_hours = 24, $permanent = false, $reason = '' ) {
     631    public function block_ip( $ip, $permanent = false, $reason = '' ) {
    631632        $data = array(
    632             'ip'             => $ip,
    633             'duration_hours' => $duration_hours,
    634             'permanent'      => $permanent,
     633            'ip'        => $ip,
     634            'permanent' => $permanent,
    635635        );
    636636
     
    670670    /**
    671671     * Extend the block duration for a blocked IP address.
    672      *
    673      * @param string $ip   IP address.
    674      * @param int    $days Number of days to extend (default 1).
     672     * Duration is server-authoritative (uses the site's configured auto_block_ttl_hours).
     673     *
     674     * @param string $ip IP address.
    675675     * @return array Result.
    676676     */
    677     public function extend_block( $ip, $days = 1 ) {
     677    public function extend_block( $ip ) {
    678678        $response = $this->request( 'POST', '/adaptive-defense/extend-block', array(
    679             'ip'   => $ip,
    680             'days' => $days,
     679            'ip' => $ip,
    681680        ) );
    682681
     
    819818        // Handle HTTP errors.
    820819        if ( $code >= 400 ) {
    821             $error_message = isset( $data['error'] ) ? $data['error'] : __( 'An error occurred.', 'atomic-edge-security' );
     820            $error_code    = isset( $data['error'] ) ? $data['error'] : null;
     821            $error_message = $error_code ?? __( 'An error occurred.', 'atomic-edge-security' );
    822822            if ( isset( $data['message'] ) ) {
    823823                $error_message = $data['message'];
    824824            }
    825825            AtomicEdge::log( "API Error ({$code})", $error_message );
    826             return array(
     826            $result = array(
    827827                'success' => false,
    828828                'error'   => $error_message,
    829829                'code'    => $code,
    830830            );
     831            // Preserve the original error identifier (e.g. 'plan_limit') when
     832            // the human-readable message is available separately.
     833            if ( null !== $error_code && isset( $data['message'] ) && $error_code !== $data['message'] ) {
     834                $result['error_code'] = $error_code;
     835            }
     836            return $result;
    831837        }
    832838
  • atomic-edge-security/trunk/includes/class-atomicedge-dev-mode.php

    r3476055 r3480349  
    844844    /**
    845845     * Simulate a successful extend block response.
    846      *
    847      * @param string $ip   IP address.
    848      * @param int    $days Number of days to extend.
    849      * @return array
    850      */
    851     public static function simulate_extend_block( $ip, $days = 1 ) {
     846     * Duration is server-authoritative; simulates a 24h extension for dev mode.
     847     *
     848     * @param string $ip IP address.
     849     * @return array
     850     */
     851    public static function simulate_extend_block( $ip ) {
    852852        return array(
    853853            'message'          => sprintf(
    854                 /* translators: 1: IP address, 2: number of days */
    855                 __( '[Dev Mode] Block for %1$s extended by %2$d day(s).', 'atomic-edge-security' ),
    856                 $ip,
    857                 $days
     854                /* translators: %s: IP address */
     855                __( '[Dev Mode] Block for %s has been extended.', 'atomic-edge-security' ),
     856                $ip
    858857            ),
    859858            'ip'               => $ip,
    860859            'is_blocked'       => true,
    861860            'blocked_at'       => gmdate( 'c', time() - 3600 ),
    862             'block_expires_at' => gmdate( 'c', time() + ( $days * 86400 ) ),
     861            'block_expires_at' => gmdate( 'c', time() + 86400 ),
    863862        );
    864863    }
  • atomic-edge-security/trunk/includes/class-atomicedge.php

    r3473194 r3480349  
    217217
    218218            // Localize script with data for adaptive-defense.js.
     219            $site_data           = get_option( 'atomicedge_site_data', array() );
     220            $can_permanent_block = true; // Default: allow (server-side is the ultimate gate).
     221            if ( is_array( $site_data ) ) {
     222                if ( isset( $site_data['features']['adaptive_defense_permanent_blocks'] ) ) {
     223                    $can_permanent_block = (bool) $site_data['features']['adaptive_defense_permanent_blocks'];
     224                } elseif ( isset( $site_data['plan_tier'] ) ) {
     225                    $can_permanent_block = ( 'free' !== $site_data['plan_tier'] );
     226                }
     227            }
     228
    219229            wp_localize_script(
    220230                'atomicedge-adaptive-defense',
    221231                'atomicedge_admin',
    222232                array(
    223                     'ajax_url'           => admin_url( 'admin-ajax.php' ),
    224                     'nonce'              => wp_create_nonce( 'atomicedge_ajax' ),
    225                     'access_control_url' => admin_url( 'admin.php?page=atomicedge-access-control&tab=blacklist' ),
     233                    'ajax_url'            => admin_url( 'admin-ajax.php' ),
     234                    'nonce'               => wp_create_nonce( 'atomicedge_ajax' ),
     235                    'access_control_url'  => admin_url( 'admin.php?page=atomicedge-access-control&tab=blacklist' ),
     236                    'can_permanent_block' => $can_permanent_block,
    226237                )
    227238            );
Note: See TracChangeset for help on using the changeset viewer.