PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/model/class-lockout-log.php +93 -19 6.0.1 → 6.3.0 View file →
@@ -9,8 +9,9 @@
9 9
10 10 use WP_Defender\DB;
11 11 use Calotes\Base\Model;
12 12 use WP_Defender\Traits\Formats;
13 +use WP_Defender\Traits\Country;
13 14 use WP_Defender\Component\User_Agent;
14 15 use WP_Defender\Component\Table_Lockout;
15 16 use WP_Defender\Model\Setting\Blacklist_Lockout;
16 17 use WP_Defender\Model\Setting\User_Agent_Lockout;
@@ -20,8 +21,9 @@
20 21 */
21 22 class Lockout_Log extends DB {
22 23
23 24 use Formats;
25 + use Country;
24 26
25 27 // The Firewall log hierarchy. Parent items: 'login', '404', 'bots', 'unlock' and 'custom'.
26 28 // Group of 'login' cases.
27 29 public const AUTH_FAIL = 'auth_fail', AUTH_LOCK = 'auth_lock';
@@ -38,10 +40,8 @@
38 40 public const IP_UNLOCK = 'ip_unlock';
39 41 // Group of 'custom' cases.
40 42 public const LOCKOUT_IP_CUSTOM = 'custom_lockout';
41 43
42 - public const INFINITE_SCROLL_SIZE = 50;
43 -
44 44 /**
45 45 * Table name.
46 46 *
47 47 * @var string
@@ -112,8 +112,34 @@
112 112 */
113 113 public $country_iso_code;
114 114
115 115 /**
116 + * Factory method to instantiate and populate a Lockout_Log model with request data.
117 + *
118 + * @param string $ip The IP address.
119 + * @param string|null $tried The attempted URI, username, or user agent.
120 + *
121 + * @return self
122 + */
123 + public static function create( string $ip, ?string $tried = null ): self {
124 + $model = new self();
125 + $model->ip = $ip;
126 + $user_agent = defender_get_data_from_request( 'HTTP_USER_AGENT', 's' );
127 + $model->user_agent = '' !== $user_agent ? User_Agent::fast_cleaning( $user_agent ) : '';
128 + $model->date = time();
129 + $model->tried = $tried ?? $model->user_agent;
130 + $model->blog_id = get_current_blog_id();
131 +
132 + $ip_to_country = $model->ip_to_country( $ip );
133 +
134 + if ( isset( $ip_to_country['iso'] ) ) {
135 + $model->country_iso_code = $ip_to_country['iso'];
136 + }
137 +
138 + return $model;
139 + }
140 +
141 + /**
116 142 * Query the logs based on the provided filters and pagination settings.
117 143 *
118 144 * @param array $filters An array of filters to apply to the query. Default is an empty array.
119 145 * The following filters are supported:
@@ -234,13 +260,13 @@
234 260 /**
235 261 * Get all lockout types.
236 262 */
237 263 public static function get_all_lockout_types(): array {
238 - $arr = self::get_404_lockout_types();
239 - $arr[] = self::AUTH_LOCK;
240 - $arr[] = self::LOCKOUT_UA;
241 -
242 - return $arr;
264 + return array_merge(
265 + self::get_404_lockout_types(),
266 + array( self::AUTH_LOCK ),
267 + self::get_ua_lockout_types()
268 + );
243 269 }
244 270
245 271 /**
246 272 * Count login lockout in the last 7 days.
@@ -266,9 +292,9 @@
266 292 return self::count( $start, $end, self::get_404_lockout_types() );
267 293 }
268 294
269 295 /**
270 - * Count UA lockout in the last 7 days.
296 + * Count all UA lockouts in the last 7 days.
271 297 *
272 298 * @return string|null
273 299 */
274 300 public static function count_ua_lockout_last_7_days(): ?string {
@@ -274,16 +300,28 @@
274 300 public static function count_ua_lockout_last_7_days(): ?string {
275 301 $start = strtotime( '-7 days' );
276 302 $end = time();
277 303
278 - return self::count( $start, $end, self::LOCKOUT_UA );
304 + return self::count( $start, $end, self::get_ua_lockout_types() );
279 305 }
280 306
281 307 /**
282 - * A shortcut for quickly count lockout in last 24 hours.
308 + * Count all UA lockouts in the last 24 hours.
283 309 *
284 310 * @return string|null
285 311 */
312 + public static function count_ua_lockouts_in_24_hours(): ?string {
313 + $start = strtotime( '-24 hours' );
314 + $end = time();
315 +
316 + return self::count( $start, $end, self::get_ua_lockout_types() );
317 + }
318 +
319 + /**
320 + * A shortcut for quickly count ALL lockouts in last 24 hours.
321 + *
322 + * @return string|null
323 + */
286 324 public static function count_lockout_in_24_hours(): ?string {
287 325 $start = strtotime( '-24 hours' );
288 326 $end = time();
289 327
@@ -367,8 +405,37 @@
367 405 ->truncate();
368 406 }
369 407
370 408 /**
409 + * Get log types that are attempts/errors (not lockout events).
410 + *
411 + * @return array
412 + */
413 + public static function get_attempt_types(): array {
414 + return array(
415 + self::AUTH_FAIL,
416 + self::ERROR_404,
417 + self::ERROR_404_IGNORE,
418 + self::ERROR_404_XSS,
419 + self::ERROR_404_NON_EXISTENT_PLUGIN,
420 + self::ERROR_404_NON_EXISTENT_THEME,
421 + );
422 + }
423 +
424 + /**
425 + * Delete only lockout-type log records, preserving attempt/error entries.
426 + *
427 + * @return int|false Number of rows deleted or false on failure.
428 + */
429 + public static function delete_lockout_records() {
430 + $orm = self::get_orm();
431 +
432 + return $orm->get_repository( self::class )
433 + ->where( 'type', 'not in', self::get_attempt_types() )
434 + ->delete_all();
435 + }
436 +
437 + /**
371 438 * Remove logs based on timestamp and limit.
372 439 *
373 440 * @param int $timestamp The timestamp to filter logs by.
374 441 * @param int $limit The maximum number of logs to delete.
@@ -395,8 +462,11 @@
395 462 $today_midnight = strtotime( '-24 hours', $current_time );
396 463 $first_this_week = strtotime( '-7 days', $current_time );
397 464 $first_this_month = strtotime( '-30 days', $current_time );
398 465
466 + $ua_types_sql = "'" . implode( "', '", self::get_ua_lockout_types() ) . "'";
467 + $types_404_sql = "'" . implode( "', '", self::get_404_lockout_types() ) . "'";
468 +
399 469 // Prepare columns.
400 470 $select = array(
401 471 'MAX(date) as lockout_last',
402 472 'COUNT(*) as lockout_this_month',
@@ -401,19 +471,19 @@
401 471 'MAX(date) as lockout_last',
402 472 'COUNT(*) as lockout_this_month',
403 473 // 24 hours
404 474 "COUNT(IF(date > {$today_midnight}, 1, NULL)) as lockout_today",
405 - "COUNT(IF(date > {$today_midnight} AND type IN ('" . self::LOCKOUT_404 . "', '" . self::LOCKOUT_404_XSS . "', '" . self::LOCKOUT_404_NON_EXISTENT_THEME . "', '" . self::LOCKOUT_404_NON_EXISTENT_PLUGIN . "'), 1, NULL)) as lockout_404_today",
475 + "COUNT(IF(date > {$today_midnight} AND type IN ({$types_404_sql}), 1, NULL)) as lockout_404_today",
406 476 "COUNT(IF(date > {$today_midnight} AND type = '" . self::AUTH_LOCK . "', 1, NULL)) as lockout_login_today",
407 - "COUNT(IF(date > {$today_midnight} AND type = '" . self::LOCKOUT_UA . "', 1, NULL)) as lockout_ua_today",
477 + "COUNT(IF(date > {$today_midnight} AND type IN ({$ua_types_sql}), 1, NULL)) as lockout_ua_today",
408 478 // 7 days
409 - "COUNT(IF(date > {$first_this_week} AND type IN ('" . self::LOCKOUT_404 . "', '" . self::LOCKOUT_404_XSS . "', '" . self::LOCKOUT_404_NON_EXISTENT_THEME . "', '" . self::LOCKOUT_404_NON_EXISTENT_PLUGIN . "'), 1, NULL)) as lockout_404_this_week",
479 + "COUNT(IF(date > {$first_this_week} AND type IN ({$types_404_sql}), 1, NULL)) as lockout_404_this_week",
410 480 "COUNT(IF(date > {$first_this_week} AND type = '" . self::AUTH_LOCK . "', 1, NULL)) as lockout_login_this_week",
411 - "COUNT(IF(date > {$first_this_week} AND type = '" . self::LOCKOUT_UA . "', 1, NULL)) as lockout_ua_this_week",
481 + "COUNT(IF(date > {$first_this_week} AND type IN ({$ua_types_sql}), 1, NULL)) as lockout_ua_this_week",
412 482 // 30 days
413 - "COUNT(IF(date > {$first_this_month} AND type IN ('" . self::LOCKOUT_404 . "', '" . self::LOCKOUT_404_XSS . "', '" . self::LOCKOUT_404_NON_EXISTENT_THEME . "', '" . self::LOCKOUT_404_NON_EXISTENT_PLUGIN . "'), 1, NULL)) as lockout_404_this_month",
483 + "COUNT(IF(date > {$first_this_month} AND type IN ({$types_404_sql}), 1, NULL)) as lockout_404_this_month",
414 484 "COUNT(IF(date > {$first_this_month} AND type = '" . self::AUTH_LOCK . "', 1, NULL)) as lockout_login_this_month",
415 - "COUNT(IF(date > {$first_this_month} AND type = '" . self::LOCKOUT_UA . "', 1, NULL)) as lockout_ua_this_month",
485 + "COUNT(IF(date > {$first_this_month} AND type IN ({$ua_types_sql}), 1, NULL)) as lockout_ua_this_month",
416 486 );
417 487 $select = implode( ',', $select );
418 488
419 489 $orm = self::get_orm();
@@ -587,11 +657,15 @@
587 657 $ban_status = $filters['ban_status'];
588 658 $type = $filters['type'] ?? '';
589 659 $ua_types = self::get_ua_lockout_types();
590 660
591 - if ( in_array( $type, $ua_types, true ) ) {
592 - $where = self::ban_status_where( $ban_status );
593 - if ( 3 === count( $where ) ) {
661 + $where = self::ban_status_where( $ban_status );
662 +
663 + if ( 3 === count( $where ) ) {
664 + if ( in_array( $type, $ua_types, true ) ) {
665 + $orm->where( ...$where );
666 + } elseif ( '' === $type ) {
667 + $orm->where( 'type', 'in', $ua_types );
594 668 $orm->where( ...$where );
595 669 }
596 670 return;
597 671 }