| @@ -17,11 +17,11 @@ | ||
| 17 | 17 | use WP_Defender\Traits\Country; |
| 18 | 18 | use WP_Defender\Behavior\WPMUDEV; |
| 19 | 19 | use WP_Defender\Model\Lockout_Ip; |
| 20 | 20 | use WP_Defender\Traits\Continent; |
| 21 | -use WP_Defender\Controller\Firewall; | |
| 22 | 21 | use WP_Defender\Component\Blacklist_Lockout; |
| 23 | -use MaxMind\Db\Reader\InvalidDatabaseException; | |
| 22 | +use WP_DEFENDER_VENDOR\MaxMind\Db\Reader\InvalidDatabaseException; | |
| 23 | +use WP_Defender\Component\Network_Cron_Manager; | |
| 24 | 24 | use WP_Defender\Integrations\MaxMind_Geolocation; |
| 25 | 25 | use WP_Defender\Component\Config\Config_Hub_Helper; |
| 26 | 26 | use WP_Defender\Model\Setting\Blacklist_Lockout as Model_Blacklist_Lockout; |
| 27 | 27 | |
| @@ -64,17 +64,26 @@ | ||
| 64 | 64 | $this->model = wd_di()->get( Model_Blacklist_Lockout::class ); |
| 65 | 65 | $this->service = wd_di()->get( Blacklist_Lockout::class ); |
| 66 | 66 | add_action( 'wd_blacklist_this_ip', array( $this, 'blacklist_an_ip' ) ); |
| 67 | 67 | // Update MaxMind's DB. |
| 68 | - if ( ! empty( $this->model->maxmind_license_key ) ) { | |
| 69 | - if ( ! wp_next_scheduled( 'wpdef_update_geoip' ) ) { | |
| 70 | - wp_schedule_event( strtotime( 'next Thursday' ), 'weekly', 'wpdef_update_geoip' ); | |
| 71 | - } | |
| 68 | + if ( '' !== $this->model->maxmind_license_key ) { | |
| 72 | 69 | // @since 2.8.0 Allows update or remove the database of MaxMind automatic and periodically (MaxMind's TOS). |
| 73 | - $bind_updater = (bool) apply_filters( 'wd_update_maxmind_database', true ); | |
| 74 | - // Bind to the scheduled updater action. | |
| 70 | + $bind_updater = apply_filters( 'wd_update_maxmind_database', true ); | |
| 71 | + $bind_updater = is_bool( $bind_updater ) ? $bind_updater : (bool) $bind_updater; | |
| 72 | + | |
| 75 | 73 | if ( $bind_updater ) { |
| 76 | - add_action( 'wpdef_update_geoip', array( $this, 'update_database' ) ); | |
| 74 | + /** | |
| 75 | + * Network Cron Manager | |
| 76 | + * | |
| 77 | + * @var Network_Cron_Manager $network_cron_manager | |
| 78 | + */ | |
| 79 | + $network_cron_manager = wd_di()->get( Network_Cron_Manager::class ); | |
| 80 | + $network_cron_manager->register_callback( | |
| 81 | + 'wpdef_update_geoip', | |
| 82 | + array( $this, 'update_database' ), | |
| 83 | + WEEK_IN_SECONDS, | |
| 84 | + 'next Thursday' | |
| 85 | + ); | |
| 77 | 86 | } |
| 78 | 87 | } |
| 79 | 88 | } |
| 80 | 89 | |
| @@ -86,8 +95,11 @@ | ||
| 86 | 95 | * @return void |
| 87 | 96 | */ |
| 88 | 97 | public function blacklist_an_ip( string $ip ): void { |
| 89 | 98 | $this->model->add_to_list( $ip, 'blocklist' ); |
| 99 | + if ( defender_is_wp_org_version() ) { | |
| 100 | + \WP_Defender\Component\Rate::run_counter_of_ip_lockouts(); | |
| 101 | + } | |
| 90 | 102 | } |
| 91 | 103 | |
| 92 | 104 | /** |
| 93 | 105 | * Enqueues scripts and styles for this page. |
| @@ -98,9 +110,8 @@ | ||
| 98 | 110 | public function enqueue_assets() { |
| 99 | 111 | if ( ! $this->is_page_active() ) { |
| 100 | 112 | return; |
| 101 | 113 | } |
| 102 | - wp_localize_script( 'def-iplockout', 'blacklist', $this->data_frontend() ); | |
| 103 | 114 | } |
| 104 | 115 | |
| 105 | 116 | /** |
| 106 | 117 | * Provides data for the frontend. |
| @@ -134,22 +145,24 @@ | ||
| 134 | 145 | foreach ( $user_ip as $ip ) { |
| 135 | 146 | $current_country[] = $this->get_current_country( $ip ); |
| 136 | 147 | } |
| 137 | 148 | |
| 149 | + $misc = array( | |
| 150 | + 'user_ip' => implode( ', ', $user_ip ), | |
| 151 | + 'is_geodb_downloaded' => $exist_geodb, | |
| 152 | + 'blacklist_countries' => $blacklist_countries, | |
| 153 | + 'whitelist_countries' => $whitelist_countries, | |
| 154 | + 'current_country' => $current_country, | |
| 155 | + 'no_ips' => '' === $arr_model['ip_blacklist'] && '' === $arr_model['ip_whitelist'], | |
| 156 | + 'countries_with_continents_list' => $countries_with_continents_list, | |
| 157 | + 'geodb_license_key' => $this->mask_license_key( $this->model->maxmind_license_key ), | |
| 158 | + 'module_name' => Model_Blacklist_Lockout::get_module_name(), | |
| 159 | + ); | |
| 160 | + | |
| 138 | 161 | return array_merge( |
| 139 | 162 | array( |
| 140 | 163 | 'model' => $arr_model, |
| 141 | - 'misc' => array( | |
| 142 | - 'user_ip' => implode( ',', $user_ip ), | |
| 143 | - 'is_geodb_downloaded' => $exist_geodb, | |
| 144 | - 'blacklist_countries' => $blacklist_countries, | |
| 145 | - 'whitelist_countries' => $whitelist_countries, | |
| 146 | - 'current_country' => $current_country, | |
| 147 | - 'no_ips' => '' === $arr_model['ip_blacklist'] && '' === $arr_model['ip_whitelist'], | |
| 148 | - 'countries_with_continents_list' => $countries_with_continents_list, | |
| 149 | - 'geodb_license_key' => $this->mask_license_key( $this->model->maxmind_license_key ), | |
| 150 | - 'module_name' => Model_Blacklist_Lockout::get_module_name(), | |
| 151 | - ), | |
| 164 | + 'misc' => $misc, | |
| 152 | 165 | ), |
| 153 | 166 | $this->dump_routes_and_nonces() |
| 154 | 167 | ); |
| 155 | 168 | } |
| @@ -161,9 +174,9 @@ | ||
| 161 | 174 | * |
| 162 | 175 | * @return string The masked license key. |
| 163 | 176 | */ |
| 164 | 177 | private function mask_license_key( $maxmind_license_key ): string { |
| 165 | - if ( ! is_string( $maxmind_license_key ) || empty( $maxmind_license_key ) ) { | |
| 178 | + if ( ! is_string( $maxmind_license_key ) || '' === $maxmind_license_key ) { | |
| 166 | 179 | return $maxmind_license_key; |
| 167 | 180 | } |
| 168 | 181 | // Get the length of the license key. |
| 169 | 182 | $key_length = strlen( $maxmind_license_key ); |
| @@ -215,8 +228,10 @@ | ||
| 215 | 228 | 'sanitize' => 'sanitize_textarea_field', |
| 216 | 229 | ), |
| 217 | 230 | ) |
| 218 | 231 | ); |
| 232 | + $data = $this->remove_country_list_conflicts( $data ); | |
| 233 | + | |
| 219 | 234 | $this->model->import( $data ); |
| 220 | 235 | if ( $this->model->validate() ) { |
| 221 | 236 | $this->model->save(); |
| 222 | 237 | Config_Hub_Helper::set_clear_active_flag(); |
| @@ -242,10 +257,8 @@ | ||
| 242 | 257 | $this->model->save(); |
| 243 | 258 | Config_Hub_Helper::set_clear_active_flag(); |
| 244 | 259 | } |
| 245 | 260 | |
| 246 | - $this->model->import( $data ); | |
| 247 | - | |
| 248 | 261 | return new Response( |
| 249 | 262 | false, |
| 250 | 263 | array_merge( |
| 251 | 264 | array( 'message' => $this->model->get_formatted_errors() ), |
| @@ -254,8 +267,44 @@ | ||
| 254 | 267 | ); |
| 255 | 268 | } |
| 256 | 269 | |
| 257 | 270 | /** |
| 271 | + * Prevent the same country from being saved in both blocked and allowed lists. | |
| 272 | + * | |
| 273 | + * @param array $data Settings data. | |
| 274 | + * | |
| 275 | + * @return array | |
| 276 | + */ | |
| 277 | + private function remove_country_list_conflicts( array $data ): array { | |
| 278 | + if ( ! isset( $data['country_blacklist'], $data['country_whitelist'] ) ) { | |
| 279 | + return $data; | |
| 280 | + } | |
| 281 | + | |
| 282 | + $country_blacklist = array_values( array_unique( $data['country_blacklist'] ) ); | |
| 283 | + $country_whitelist = array_values( array_unique( $data['country_whitelist'] ) ); | |
| 284 | + $added_blacklist = array_diff( $country_blacklist, $this->model->country_blacklist ); | |
| 285 | + $added_whitelist = array_diff( $country_whitelist, $this->model->country_whitelist ); | |
| 286 | + | |
| 287 | + if ( array() !== $added_blacklist ) { | |
| 288 | + $country_whitelist = array_values( array_diff( $country_whitelist, $added_blacklist ) ); | |
| 289 | + } | |
| 290 | + | |
| 291 | + if ( array() !== $added_whitelist ) { | |
| 292 | + $country_blacklist = array_values( array_diff( $country_blacklist, $added_whitelist ) ); | |
| 293 | + } | |
| 294 | + | |
| 295 | + $remaining_conflicts = array_intersect( $country_blacklist, $country_whitelist ); | |
| 296 | + if ( array() !== $remaining_conflicts ) { | |
| 297 | + $country_whitelist = array_values( array_diff( $country_whitelist, $remaining_conflicts ) ); | |
| 298 | + } | |
| 299 | + | |
| 300 | + $data['country_blacklist'] = $country_blacklist; | |
| 301 | + $data['country_whitelist'] = $country_whitelist; | |
| 302 | + | |
| 303 | + return $data; | |
| 304 | + } | |
| 305 | + | |
| 306 | + /** | |
| 258 | 307 | * Download the GEODB IP from Maxmind. |
| 259 | 308 | * |
| 260 | 309 | * @param Request $request The request object containing the license key. |
| 261 | 310 | * |
| @@ -292,9 +341,9 @@ | ||
| 292 | 341 | } |
| 293 | 342 | |
| 294 | 343 | foreach ( $this->get_user_ip() as $ip ) { |
| 295 | 344 | $country = $this->get_current_country( $ip ); |
| 296 | - if ( ! empty( $country ) && ! empty( $country['iso'] ) ) { | |
| 345 | + if ( isset( $country['iso'] ) && '' !== (string) $country['iso'] ) { | |
| 297 | 346 | $this->model = $this->service->add_default_whitelisted_country( $this->model, $country['iso'] ); |
| 298 | 347 | } |
| 299 | 348 | } |
| 300 | 349 | $this->model->maxmind_license_key = $license_key; |
| @@ -311,19 +360,23 @@ | ||
| 311 | 360 | ) |
| 312 | 361 | ); |
| 313 | 362 | } else { |
| 314 | 363 | $this->log( 'Error from MaxMind: ' . $tmp->get_error_message(), Firewall::FIREWALL_LOG ); |
| 315 | - $string = sprintf( | |
| 316 | - /* translators: 1. License key with link. */ | |
| 317 | - esc_html__( | |
| 318 | - 'You have entered an invalid %1$s. If you just created the key, please wait 5 minutes before trying to activate it.', | |
| 319 | - 'defender-security' | |
| 320 | - ), | |
| 321 | - '<a target="_blank" href="https://www.maxmind.com/en/accounts/current/license-key">' . esc_html__( 'license key', 'defender-security' ) . '</a>' | |
| 364 | + $string = esc_html__( | |
| 365 | + 'The license key you entered isn\'t valid. If you recently created it, allow up to 5 minutes for activation before trying again.', | |
| 366 | + 'defender-security' | |
| 322 | 367 | ); |
| 323 | 368 | |
| 324 | 369 | if ( ( new WPMUDEV() )->show_support_links() ) { |
| 325 | - $string .= defender_support_ticket_text(); | |
| 370 | + $string = sprintf( | |
| 371 | + /* translators: 1. Opening support link, 2. Closing support link. */ | |
| 372 | + esc_html__( | |
| 373 | + 'The license key you entered isn\'t valid. If you recently created it, allow up to 5 minutes for activation before trying again. Need help? %1$sContact support%2$s.', | |
| 374 | + 'defender-security' | |
| 375 | + ), | |
| 376 | + '<a target="_blank" href="' . WP_DEFENDER_SUPPORT_LINK . '">', | |
| 377 | + '</a>' | |
| 378 | + ); | |
| 326 | 379 | } |
| 327 | 380 | |
| 328 | 381 | return new Response( false, array( 'invalid_text' => $string ) ); |
| 329 | 382 | } |
| @@ -456,9 +509,9 @@ | ||
| 456 | 509 | $ips = null; |
| 457 | 510 | $bulk_ips = null; |
| 458 | 511 | $limit = 50; |
| 459 | 512 | |
| 460 | - if ( ! empty( $data['ips'] ) ) { | |
| 513 | + if ( isset( $data['ips'] ) && is_string( $data['ips'] ) && '' !== $data['ips'] ) { | |
| 461 | 514 | $ips = json_decode( $data['ips'] ); |
| 462 | 515 | $first_nth_ips = array_slice( $ips, 0, $limit ); |
| 463 | 516 | $bulk_ips = wp_list_pluck( $first_nth_ips, 'ip' ); |
| 464 | 517 | } |
| @@ -508,9 +561,9 @@ | ||
| 508 | 561 | */ |
| 509 | 562 | public function query_locked_ips() { |
| 510 | 563 | $results = Lockout_Ip::query_locked_ip(); |
| 511 | 564 | $locked_ips = array(); |
| 512 | - if ( ! empty( $results ) ) { | |
| 565 | + if ( array() !== $results ) { | |
| 513 | 566 | foreach ( $results as $key => $locked_ip ) { |
| 514 | 567 | $locked_ips[] = array( |
| 515 | 568 | 'id' => $locked_ip['id'], |
| 516 | 569 | 'ip' => $locked_ip['ip'], |
| @@ -559,9 +612,9 @@ | ||
| 559 | 612 | 'ip_blacklist' => $data['ip_blacklist'], |
| 560 | 613 | 'ip_whitelist' => $data['ip_whitelist'], |
| 561 | 614 | 'ip_lockout_message' => $data['ip_lockout_message'], |
| 562 | 615 | ); |
| 563 | - if ( isset( $data['geoIP_db'] ) && file_exists( $data['geoIP_db'] ) ) { | |
| 616 | + if ( isset( $data['geoIP_db'] ) && is_string( $data['geoIP_db'] ) && file_exists( $data['geoIP_db'] ) ) { | |
| 564 | 617 | $adapted_data['geodb_path'] = $data['geoIP_db']; |
| 565 | 618 | if ( isset( $data['country_blacklist'] ) ) { |
| 566 | 619 | $adapted_data['country_blacklist'] = $data['country_blacklist']; |
| 567 | 620 | } |
| @@ -580,9 +633,9 @@ | ||
| 580 | 633 | * |
| 581 | 634 | * @throws Exception If table is not defined. |
| 582 | 635 | */ |
| 583 | 636 | public function import_data( array $data ) { |
| 584 | - if ( ! empty( $data ) ) { | |
| 637 | + if ( array() !== $data ) { | |
| 585 | 638 | // Upgrade for old versions. |
| 586 | 639 | $data = $this->adapt_data( $data ); |
| 587 | 640 | $model = $this->model; |
| 588 | 641 | $model->import( $data ); |
| @@ -628,20 +681,29 @@ | ||
| 628 | 681 | 'type' => 'int', |
| 629 | 682 | ), |
| 630 | 683 | ) |
| 631 | 684 | ); |
| 632 | - $attached_id = $data['id']; | |
| 633 | - if ( ! is_object( get_post( $attached_id ) ) ) { | |
| 634 | - return new Response( | |
| 635 | - false, | |
| 636 | - array( | |
| 637 | - 'message' => esc_html__( 'Your file is invalid!', 'defender-security' ), | |
| 638 | - ) | |
| 639 | - ); | |
| 685 | + $file = ''; | |
| 686 | + $attached_id = $data['id'] ?? 0; | |
| 687 | + if ( 0 < $attached_id ) { | |
| 688 | + if ( ! is_object( get_post( $attached_id ) ) ) { | |
| 689 | + return new Response( | |
| 690 | + false, | |
| 691 | + array( | |
| 692 | + 'message' => esc_html__( 'Your file is invalid!', 'defender-security' ), | |
| 693 | + ) | |
| 694 | + ); | |
| 695 | + } | |
| 696 | + | |
| 697 | + $file = get_attached_file( $attached_id ); | |
| 698 | + } else { | |
| 699 | + $file_data = defender_get_data_from_request( 'file', 'f' ); | |
| 700 | + if ( is_array( $file_data ) && isset( $file_data['tmp_name'] ) && is_uploaded_file( $file_data['tmp_name'] ) ) { | |
| 701 | + $file = $file_data['tmp_name']; | |
| 702 | + } | |
| 640 | 703 | } |
| 641 | 704 | |
| 642 | - $file = get_attached_file( $attached_id ); | |
| 643 | - if ( ! is_file( $file ) ) { | |
| 705 | + if ( ! is_string( $file ) || '' === $file || ! is_file( $file ) ) { | |
| 644 | 706 | return new Response( |
| 645 | 707 | false, |
| 646 | 708 | array( |
| 647 | 709 | 'message' => esc_html__( 'Your file is invalid!', 'defender-security' ), |
| @@ -665,11 +727,14 @@ | ||
| 665 | 727 | } |
| 666 | 728 | |
| 667 | 729 | return new Response( |
| 668 | 730 | true, |
| 669 | - array( | |
| 670 | - 'message' => esc_html__( 'Your allowlist/blocklist has been successfully imported.', 'defender-security' ), | |
| 671 | - 'interval' => 1, | |
| 731 | + array_merge( | |
| 732 | + array( | |
| 733 | + 'message' => esc_html__( 'Your allowlist/blocklist has been successfully imported.', 'defender-security' ), | |
| 734 | + 'auto_close' => true, | |
| 735 | + ), | |
| 736 | + $this->data_frontend() | |
| 672 | 737 | ) |
| 673 | 738 | ); |
| 674 | 739 | } |
| 675 | 740 | |
| @@ -680,9 +745,9 @@ | ||
| 680 | 745 | * @throws Exception If table is not defined. |
| 681 | 746 | * @since 2.8.0 |
| 682 | 747 | */ |
| 683 | 748 | public function update_database() { |
| 684 | - if ( empty( $this->model->maxmind_license_key ) ) { | |
| 749 | + if ( '' === $this->model->maxmind_license_key ) { | |
| 685 | 750 | return; |
| 686 | 751 | } |
| 687 | 752 | |
| 688 | 753 | $service_geo = wd_di()->get( MaxMind_Geolocation::class ); |