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/controller/class-firewall-logs.php +168 -366 5.7.0 → 6.3.0 View file →
@@ -8,9 +8,9 @@
8 8 namespace WP_Defender\Controller;
9 9
10 10 use DateTime;
11 11 use Exception;
12 -use Countable;
12 +use Generator;
13 13 use Valitron\Validator;
14 14 use Calotes\Helper\HTTP;
15 15 use WP_Defender\Controller;
16 16 use Calotes\Component\Request;
@@ -15,15 +15,16 @@
15 15 use WP_Defender\Controller;
16 16 use Calotes\Component\Request;
17 17 use Calotes\Component\Response;
18 18 use WP_Defender\Traits\Formats;
19 +use WP_Defender\Traits\Country;
19 20 use WP_Defender\Behavior\WPMUDEV;
20 21 use WP_Defender\Model\Lockout_Log;
21 22 use WP_Defender\Component\User_Agent;
22 -use WP_Defender\Component\IP\Global_IP;
23 23 use WP_Defender\Component\Table_Lockout;
24 24 use WP_Defender\Component\Network_Cron_Manager;
25 25 use WP_Defender\Integrations\Antibot_Global_Firewall_Client;
26 +use WP_Defender\Component\Export\Csv;
26 27 use WP_Defender\Model\Setting\Blacklist_Lockout;
27 28 use WP_Defender\Model\Setting\User_Agent_Lockout;
28 29 use WP_Defender\Component\Firewall_Logs as Firewall_Logs_Component;
29 30
@@ -33,8 +34,9 @@
33 34 */
34 35 class Firewall_Logs extends Controller {
35 36
36 37 use Formats;
38 + use Country;
37 39
38 40 /**
39 41 * The slug identifier for this controller.
40 42 *
@@ -64,8 +66,13 @@
64 66 */
65 67 const AKISMET_BLOCKED_IPS = 'defender_akismet_blocked_ips';
66 68
67 69 /**
70 + * Default number of items per page.
71 + */
72 + public const DEFAULT_PER_PAGE = 10;
73 +
74 + /**
68 75 * Constructor for the class.
69 76 *
70 77 * @param Antibot_Global_Firewall_Client $antibot_client The client for interacting with the Block list API service.
71 78 */
@@ -101,9 +108,9 @@
101 108 *
102 109 * @return Response The response object with the result of the bulk action.
103 110 * @defender_route
104 111 */
105 - public function bulk( Request $request ) {
112 + public function bulk( Request $request ): Response {
106 113 $data = $request->get_data(
107 114 array(
108 115 'action' => array(
109 116 'type' => 'string',
@@ -108,153 +115,70 @@
108 115 'action' => array(
109 116 'type' => 'string',
110 117 'sanitize' => 'sanitize_text_field',
111 118 ),
112 - 'ids' => array(
119 + 'ips' => array(
113 120 'type' => 'array',
114 121 ),
115 122 )
116 123 );
117 - $ids = $data['ids'];
118 - $ips = array();
119 - $logs = array();
120 - if ( is_array( $ids ) || $ids instanceof Countable ? count( $ids ) : 0 ) {
121 - foreach ( $ids as $id ) {
122 - $model = Lockout_Log::find_by_id( $id );
123 - if ( is_object( $model ) ) {
124 - $bl = wd_di()->get( Blacklist_Lockout::class );
125 - switch ( $data['action'] ) {
126 - case 'ban':
127 - $bl->remove_from_list( $model->ip, 'allowlist' );
128 - $bl->add_to_list( $model->ip, 'blocklist' );
129 - $ips[ $model->ip ] = $model->ip;
130 - $logs[] = $model;
131 - break;
132 - case 'allowlist':
133 - $bl->remove_from_list( $model->ip, 'blocklist' );
134 - $bl->add_to_list( $model->ip, 'allowlist' );
135 - $ips[ $model->ip ] = $model->ip;
136 - $logs[] = $model;
137 - break;
138 - case 'delete':
139 - $ips[ $model->ip ] = $model->ip;
140 - $model->delete();
141 - break;
142 - default:
143 - break;
144 - }
145 - }
146 - }
147 - }
148 124
149 - if ( count( $logs ) > 0 ) {
150 - $logs = Lockout_Log::format_logs( $logs );
151 - }
125 + $ips = implode( PHP_EOL, array_filter( array_map( 'sanitize_text_field', (array) $data['ips'] ), 'boolval' ) );
126 + $bl = wd_di()->get( Blacklist_Lockout::class );
152 127
153 128 switch ( $data['action'] ) {
154 - case 'allowlist':
155 - $messages = sprintf(
156 - /* translators: 1: IP Address(es). 2: URL for Defender > Firewall > IP Banning. */
157 - esc_html__(
158 - 'IP %1$s has been added to your allowlist. You can control your allowlist in %2$s.',
159 - 'defender-security'
160 - ),
161 - implode( ', ', $ips ),
162 - '<a href="' . network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blocklist#tab-ip-allowlist' ) . '">' . esc_html__( 'IP Lockouts', 'defender-security' ) . '</a>'
163 - );
164 - break;
165 129 case 'ban':
166 - $messages = sprintf(
167 - /* translators: 1: IP Address(es). 2: URL for Defender > Firewall > IP Banning. */
168 - esc_html__(
169 - 'IP %1$s has been added to your blocklist. You can control your blocklist in %2$s.',
170 - 'defender-security'
171 - ),
172 - implode( ', ', $ips ),
173 - '<a href="' . network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blocklist' ) . '">' . esc_html__( 'IP Lockouts', 'defender-security' ) . '</a>'
174 - );
130 + $bl->ip_blacklist = $ips;
175 131 break;
176 - case 'delete':
177 - $messages = sprintf(
178 - /* translators: %s: IP Address(es) */
179 - esc_html__( 'IP %s has been deleted', 'defender-security' ),
180 - implode( ', ', $ips )
181 - );
132 + case 'allowlist':
133 + $bl->ip_whitelist = $ips;
182 134 break;
183 135 default:
184 - $messages = '';
185 136 break;
186 137 }
187 138
139 + $bl->save();
140 +
188 141 return new Response(
189 142 true,
190 143 array(
191 - 'message' => $messages,
192 - 'logs' => $logs,
144 + 'banning' => $bl->export(),
193 145 )
194 146 );
195 147 }
196 148
197 149 /**
198 - * Export logs to CSV
150 + * Export all logs matching the current filter to CSV.
199 151 *
200 152 * @return void
201 153 * @defender_route
202 - * @throws Exception On failure.
203 154 */
204 155 public function export_as_csv(): void {
205 - $date_from = HTTP::get( 'date_from', strtotime( '-7 days midnight' ) );
206 - $date_to = HTTP::get( 'date_to', strtotime( 'tomorrow' ) );
207 - // Convert date using timezone.
208 - $timezone = wp_timezone();
209 - $date_from = ( new DateTime( $date_from, $timezone ) )->setTime( 0, 0, 0 )->getTimestamp();
210 - $date_to = ( new DateTime( $date_to, $timezone ) )->setTime( 23, 59, 59 )->getTimestamp();
211 - $filters = array(
156 + $timezone = wp_timezone();
157 + $date_from_str = sanitize_text_field( (string) HTTP::get( 'date_from', '' ) );
158 + $date_to_str = sanitize_text_field( (string) HTTP::get( 'date_to', '' ) );
159 + $date_from = $date_from_str
160 + ? $this->date_string_to_timestamp( $date_from_str )
161 + : ( new DateTime( '-30 days', $timezone ) )->setTime( 0, 0, 0 )->getTimestamp();
162 + $date_to = $date_to_str
163 + ? $this->date_string_to_timestamp( $date_to_str, true )
164 + : ( new DateTime( 'now', $timezone ) )->setTime( 23, 59, 59 )->getTimestamp();
165 + $ip = sanitize_text_field( (string) HTTP::get( 'ip', '' ) );
166 + $user_agent = sanitize_text_field( (string) HTTP::get( 'user_agent', '' ) );
167 + $type = sanitize_text_field( (string) HTTP::get( 'type', '' ) );
168 + $ban_status = sanitize_text_field( (string) HTTP::get( 'ban_status', '' ) );
169 + $sort = sanitize_text_field( (string) HTTP::get( 'sort', Table_Lockout::SORT_DESC ) );
170 + $sort_params = wd_di()->get( Table_Lockout::class )->resolve_sort( $sort );
171 +
172 + $filters = array(
212 173 'from' => $date_from,
213 174 'to' => $date_to,
214 - 'type' => HTTP::get( 'term', '' ),
215 - 'ip' => HTTP::get( 'ip', '' ),
216 - 'ban_status' => HTTP::get( 'ban_status', '' ),
175 + 'ip' => $ip,
176 + 'user_agent' => $user_agent,
177 + 'type' => 'all' === $type ? '' : $type,
178 + 'ban_status' => 'all' === $ban_status ? '' : $ban_status,
217 179 );
218 180
219 - if ( 'all' === $filters['type'] ) {
220 - $filters['type'] = '';
221 - }
222 -
223 - if ( 'all' === $filters['ban_status'] ) {
224 - $filters['ban_status'] = '';
225 - }
226 - // User can export the number of logs that are set.
227 - $per_page = (int) defender_get_data_from_request( 'per_page', 'g' );
228 - if ( 0 === $per_page ) {
229 - $per_page = 20;
230 - }
231 - if ( - 1 === $per_page ) {
232 - $per_page = false;
233 - }
234 -
235 - $paged = (int) defender_get_data_from_request( 'paged', 'g' );
236 - if ( 0 === $paged ) {
237 - $paged = 1;
238 - }
239 - $logs = Lockout_Log::query_logs( $filters, $paged, 'id', 'desc', $per_page );
240 -
241 - $tl_component = new Table_Lockout();
242 -
243 - $ua_component = wd_di()->get( User_Agent::class );
244 -
245 - $filename = 'wdf-lockout-logs-export-' . wp_date( 'ymdHis' ) . '.csv';
246 -
247 - header( 'Expires: 0' );
248 - header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
249 - header( 'Cache-Control: private', false );
250 - header( 'Content-Type: application/octet-stream' );
251 - header( 'Content-Disposition: attachment; filename="' . $filename . '";' );
252 - header( 'Content-Transfer-Encoding: binary' );
253 -
254 - extension_loaded( 'zlib' ) ? ob_start( 'ob_gzhandler' ) : ob_start();
255 -
256 - $fp = fopen( 'php://output', 'w' );
257 181 $headers = array(
258 182 esc_html__( 'Log', 'defender-security' ),
259 183 esc_html__( 'Date / Time', 'defender-security' ),
260 184 esc_html__( 'Type', 'defender-security' ),
@@ -262,31 +186,54 @@
262 186 esc_html__( 'IP Status', 'defender-security' ),
263 187 esc_html__( 'User Agent Name', 'defender-security' ),
264 188 esc_html__( 'User Agent Status', 'defender-security' ),
265 189 );
266 - fputcsv( $fp, $headers, ',', '"', '\\' );
267 190
268 - $flush_limit = Lockout_Log::INFINITE_SCROLL_SIZE;
269 - foreach ( $logs as $key => $log ) {
270 - $item = array(
271 - $log->log,
272 - $this->format_date_time( $log->date ),
273 - $tl_component->get_type( $log->type ),
274 - $log->ip,
275 - $tl_component->get_ip_status_text( $log->ip ),
276 - $log->user_agent,
277 - $ua_component->get_status_text( $log->type, $log->tried ),
191 + try {
192 + Csv::to_browser( 'lockout-logs', $this->get_export_rows( $filters, $sort_params ), $headers );
193 + } catch ( \RuntimeException $e ) {
194 + $this->log( $e->getMessage(), 'firewall-logs' );
195 + }
196 + }
197 +
198 + /**
199 + * Generates firewall log export rows in database-backed pages.
200 + *
201 + * @param array $filters Log filters.
202 + * @param array $sort_params Sort parameters.
203 + *
204 + * @return Generator
205 + */
206 + private function get_export_rows( array $filters, array $sort_params ): Generator {
207 + $page = 1;
208 + $per_page = 1000;
209 + $tl_component = new Table_Lockout();
210 + $ua_component = wd_di()->get( User_Agent::class );
211 +
212 + do {
213 + $logs = Lockout_Log::query_logs(
214 + $filters,
215 + $page,
216 + $sort_params['order_by'],
217 + $sort_params['order'],
218 + $per_page
278 219 );
279 - fputcsv( $fp, $item, ',', '"', '\\' );
280 220
281 - if ( 0 === $key % $flush_limit ) {
282 - ob_flush();
283 - flush();
221 + foreach ( $logs as $log ) {
222 + yield array(
223 + $log->log,
224 + $this->format_date_time( $log->date ),
225 + $tl_component->get_type( $log->type ),
226 + $log->ip,
227 + $tl_component->get_ip_status_text( $log->ip ),
228 + $log->user_agent,
229 + $ua_component->get_status_text( $log->type, $log->tried ),
230 + );
284 231 }
285 - }
286 - // WP_Filesystem is not suitable here because it abstracts to reading/writing files on disk, not to output streams.
287 - fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
288 - exit();
232 +
233 + $has_more = count( $logs ) === $per_page;
234 + ++$page;
235 + } while ( $has_more );
289 236 }
290 237
291 238 /**
292 239 * Toggles an IP address to or from a specified list.
@@ -298,104 +245,34 @@
298 245 */
299 246 public function toggle_ip_to_list( Request $request ): Response {
300 247 $data = $request->get_data(
301 248 array(
302 - 'ip' => array(
249 + 'list' => array(
303 250 'type' => 'string',
304 - 'sanitize' => 'sanitize_text_field',
251 + 'sanitize' => 'sanitize_textarea_field',
305 252 ),
306 - 'list' => array(
253 + 'type' => array(
307 254 'type' => 'string',
308 255 'sanitize' => 'sanitize_text_field',
309 256 ),
310 - 'ban_status' => array(
311 - 'type' => 'string',
312 - 'sanitize' => 'sanitize_text_field',
313 - ),
314 257 )
315 258 );
316 259
317 - $ip = $data['ip'];
318 260 $collection = $data['list'];
261 + $list_type = $data['type'];
319 262
320 263 $model = wd_di()->get( Blacklist_Lockout::class );
321 - if ( $model->is_ip_in_list( $ip, $collection ) ) {
322 - $model->remove_from_list( $ip, $collection );
323 - /* translators: 1: IP address, 2: IP address list, 3: IP address list, 4: URL for Defender > Firewall > IP Banning. */
324 - $message = esc_html__(
325 - 'IP %1$s has been removed from your %2$s. You can control your %3$s in %4$s.',
326 - 'defender-security'
327 - );
328 - } else {
329 - $model->add_to_list( $ip, $collection );
330 -
331 - $global_ip_service = wd_di()->get( Global_IP::class );
332 - if ( $global_ip_service->can_blocklist_autosync() ) {
333 - $global_ip_data = array(
334 - 'block_list' => array( $ip ),
335 - );
336 - $global_ip_service->add_to_global_ip_list( $global_ip_data );
337 - }
338 -
339 - /* translators: 1: IP address. 2: IP address list. 3: IP address list. 4: URL for Defender > Firewall > IP Banning. */
340 - $message = esc_html__(
341 - 'IP %1$s has been added to your %2$s. You can control your %3$s in %4$s.',
342 - 'defender-security'
343 - );
264 + if ( 'blacklist' === $list_type ) {
265 + $model->ip_blacklist = $collection;
266 + } elseif ( 'whitelist' === $list_type ) {
267 + $model->ip_whitelist = $collection;
344 268 }
345 - $filter_data = $request->get_data(
346 - array(
347 - 'date_from' => array(
348 - 'type' => 'string',
349 - 'sanitize' => 'sanitize_text_field',
350 - ),
351 - 'date_to' => array(
352 - 'type' => 'string',
353 - 'sanitize' => 'sanitize_text_field',
354 - ),
355 - 'ip_filter' => array(
356 - 'type' => 'string',
357 - 'sanitize' => 'sanitize_text_field',
358 - ),
359 - 'type' => array(
360 - 'type' => 'string',
361 - 'sanitize' => 'sanitize_text_field',
362 - ),
363 - 'paged' => array(
364 - 'type' => 'int',
365 - 'sanitize' => 'sanitize_text_field',
366 - ),
367 - 'per_page' => array(
368 - 'type' => 'int',
369 - 'sanitize' => 'sanitize_text_field',
370 - ),
371 - )
372 - );
373 - $logs = Lockout_Log::get_logs_and_format(
374 - array(
375 - 'from' => strtotime( $filter_data['date_from'] . ' 00:00:00' ),
376 - 'to' => strtotime( $filter_data['date_to'] . ' 23:59:59' ),
377 - 'ip' => $filter_data['ip_filter'],
378 - // If this is all, then we set to null to exclude it from the filter.
379 - 'type' => 'all' === $filter_data['type'] ? '' : $filter_data['type'],
380 - ),
381 - $filter_data['paged'],
382 - 'id',
383 - 'desc',
384 - $filter_data['per_page']
385 - );
269 + $model->save();
386 270
387 271 return new Response(
388 272 true,
389 273 array(
390 - 'message' => sprintf(
391 - $message,
392 - $data['ip'] ?? '-',
393 - $data['list'],
394 - $data['list'],
395 - '<a href="' . network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blocklist' ) . '">' . esc_html__( 'IP Lockouts', 'defender-security' ) . '</a>'
396 - ),
397 - 'logs' => $logs,
274 + 'banning' => $model->export(),
398 275 )
399 276 );
400 277 }
401 278
@@ -409,115 +286,34 @@
409 286 */
410 287 public function toggle_ua_to_list( Request $request ): Response {
411 288 $data = $request->get_data(
412 289 array(
413 - 'ua' => array(
290 + 'list' => array(
414 291 'type' => 'string',
415 - 'sanitize' => 'sanitize_text_field',
292 + 'sanitize' => 'sanitize_textarea_field',
416 293 ),
417 - 'list' => array(
294 + 'type' => array(
418 295 'type' => 'string',
419 296 'sanitize' => 'sanitize_text_field',
420 297 ),
421 - 'scenario' => array(
422 - 'type' => 'string',
423 - 'sanitize' => 'sanitize_text_field',
424 - ),
425 298 )
426 299 );
427 300
428 - $ua = $data['ua'];
429 301 $collection = $data['list'];
430 - $action = $data['scenario'];
302 + $list_type = $data['type'];
431 303
432 304 $model = wd_di()->get( User_Agent_Lockout::class );
433 -
434 - if ( 'remove' === $action && $model->is_ua_in_list( $ua, $collection ) ) {
435 - $model->remove_from_list( $ua, $collection );
436 - /* translators: 1: User agent. 2: User agent list. 3: User agent list. 4: URL for Defender > Firewall > User Agent Banning. */
437 - $message = esc_html__(
438 - 'User agent %1$s has been removed from your %2$s. You can control your %3$s in %4$s.',
439 - 'defender-security'
440 - );
441 - } elseif ( 'add' === $action ) {
442 -
443 - /**
444 - * Possible scenario on regex blocklist. For e.g. UA term `run` present in allowlist & `r.n` regex in blocklist then remove `run` to block `run` user agent using regex `r.n`.
445 - */
446 - if ( 'blocklist' === $collection && $model->is_ua_in_list( $ua, 'allowlist' ) ) {
447 - $model->remove_from_list( $ua, 'allowlist' );
448 - }
449 -
450 - if ( ! $model->is_ua_in_list( $ua, $collection ) ) {
451 - $model->add_to_list( $ua, $collection );
452 - }
453 - /* translators: 1: User agent. 2: User agent list. 3: User agent list. 4: URL for Defender > Firewall > User Agent Banning. */
454 - $message = esc_html__(
455 - 'User agent %1$s has been added to your %2$s. You can control your %3$s in %4$s.',
456 - 'defender-security'
457 - );
458 - } else {
459 - return new Response(
460 - false,
461 - array( 'message' => esc_html__( 'Wrong result.', 'defender-security' ) )
462 - );
305 + if ( 'blacklist' === $list_type ) {
306 + $model->blacklist = $collection;
307 + } elseif ( 'whitelist' === $list_type ) {
308 + $model->whitelist = $collection;
463 309 }
310 + $model->save();
464 311
465 - $filter_data = $request->get_data(
466 - array(
467 - 'date_from' => array(
468 - 'type' => 'string',
469 - 'sanitize' => 'sanitize_text_field',
470 - ),
471 - 'date_to' => array(
472 - 'type' => 'string',
473 - 'sanitize' => 'sanitize_text_field',
474 - ),
475 - 'ip_filter' => array(
476 - 'type' => 'string',
477 - 'sanitize' => 'sanitize_text_field',
478 - ),
479 - 'type' => array(
480 - 'type' => 'string',
481 - 'sanitize' => 'sanitize_text_field',
482 - ),
483 - 'paged' => array(
484 - 'type' => 'int',
485 - 'sanitize' => 'sanitize_text_field',
486 - ),
487 - 'per_page' => array(
488 - 'type' => 'int',
489 - 'sanitize' => 'sanitize_text_field',
490 - ),
491 - )
492 - );
493 - $logs = Lockout_Log::get_logs_and_format(
494 - array(
495 - 'from' => strtotime( $filter_data['date_from'] . ' 00:00:00' ),
496 - 'to' => strtotime( $filter_data['date_to'] . ' 23:59:59' ),
497 - 'ip' => $filter_data['ip_filter'],
498 - // If this is all, then we set to null to exclude it from the filter.
499 - 'type' => 'all' === $filter_data['type'] ? '' : $filter_data['type'],
500 - ),
501 - $filter_data['paged'],
502 - 'id',
503 - 'desc',
504 - $filter_data['per_page']
505 - );
506 -
507 312 return new Response(
508 313 true,
509 314 array(
510 - 'message' => sprintf(
511 - $message,
512 - '<strong>' . $data['ua'] . '</strong>',
513 - $data['list'],
514 - $data['list'],
515 - '<a href="' . network_admin_url( 'admin.php?page=wdf-ip-lockout&view=ua-lockout' ) . '">' . esc_html__( 'User Agent Banning', 'defender-security' ) . '</a>'
516 - ),
517 - 'logs' => $logs,
518 - // Include blocklist preset values for the frontend.
519 - 'blocklist_preset_values' => $model->blocklist_preset_values,
315 + 'uaLockout' => $model->export(),
520 316 )
521 317 );
522 318 }
523 319
@@ -544,8 +340,12 @@
544 340 'ip' => array(
545 341 'type' => 'string',
546 342 'sanitize' => 'sanitize_text_field',
547 343 ),
344 + 'user_agent' => array(
345 + 'type' => 'string',
346 + 'sanitize' => 'sanitize_text_field',
347 + ),
548 348 'type' => array(
549 349 'type' => 'string',
550 350 'sanitize' => 'sanitize_text_field',
551 351 ),
@@ -560,57 +360,44 @@
560 360 'ban_status' => array(
561 361 'type' => 'string',
562 362 'sanitize' => 'sanitize_text_field',
563 363 ),
364 + 'per_page' => array(
365 + 'type' => 'int',
366 + 'sanitize' => 'sanitize_text_field',
367 + ),
564 368 )
565 369 );
566 370 // Validate.
567 371 $v = new Validator( $data, array() );
568 372 $v->rule( 'required', array( 'date_from', 'date_to' ) );
569 - $v->rule( 'date', array( 'date_from', 'date_to' ) );
570 373 if ( ! $v->validate() ) {
374 + return new Response( false, array( 'message' => esc_html__( 'Start and end date are required.', 'defender-security' ) ) );
375 + }
376 +
377 + $date_from = $this->date_string_to_timestamp( $data['date_from'] );
378 + $date_to = $this->date_string_to_timestamp( $data['date_to'], true );
379 + if ( $date_from <= 0 || $date_to <= 0 ) {
571 380 return new Response( false, array( 'message' => esc_html__( 'Wrong start and end date.', 'defender-security' ) ) );
572 381 }
573 - $sort = $data['sort'] ?? Table_Lockout::SORT_DESC;
574 - switch ( $sort ) {
575 - case 'ip':
576 - $order = 'desc';
577 - $order_by = 'ip';
578 - break;
579 - case 'oldest':
580 - $order = 'asc';
581 - $order_by = 'id';
582 - break;
583 - case 'user_agent':
584 - $order = 'asc';
585 - $order_by = 'user_agent';
586 - break;
587 - default:
588 - $order = 'desc';
589 - $order_by = 'id';
590 - break;
591 - }
592 - // Convert date using timezone.
593 - $timezone = wp_timezone();
594 - $date_from = ( new DateTime( $data['date_from'], $timezone ) )
595 - ->setTime( 0, 0, 0 )
596 - ->getTimestamp();
597 - $date_to = ( new DateTime( $data['date_to'], $timezone ) )
598 - ->setTime( 23, 59, 59 )
599 - ->getTimestamp();
600 382
383 + $sort = $data['sort'] ?? Table_Lockout::SORT_DESC;
384 + $sort_params = wd_di()->get( Table_Lockout::class )->resolve_sort( $sort );
385 +
601 386 $result = $this->retrieve_logs(
602 387 array(
603 388 'from' => $date_from,
604 389 'to' => $date_to,
605 390 'ip' => $data['ip'],
391 + 'user_agent' => $data['user_agent'] ?? '',
606 392 // If this is all, then we set to null to exclude it from the filter.
607 393 'type' => 'all' === $data['type'] ? '' : $data['type'],
608 394 'ban_status' => 'all' === $data['ban_status'] ? '' : $data['ban_status'],
609 395 ),
610 396 $data['paged'],
611 - $order,
612 - $order_by
397 + $sort_params['order'],
398 + $sort_params['order_by'],
399 + $data['per_page'] ?? 0
613 400 );
614 401
615 402 return new Response( true, $result );
616 403 }
@@ -622,13 +409,8 @@
622 409 public function enqueue_assets() {
623 410 if ( ! $this->is_page_active() ) {
624 411 return;
625 412 }
626 - wp_localize_script(
627 - 'def-iplockout',
628 - 'lockout_logs',
629 - array_merge( $this->data_frontend(), $this->dump_routes_and_nonces() )
630 - );
631 413 }
632 414
633 415 /**
634 416 * Provides data for the frontend.
@@ -635,20 +417,34 @@
635 417 *
636 418 * @return array An array of data for the frontend.
637 419 */
638 420 public function data_frontend(): array {
639 - $type = defender_get_data_from_request( 'type', 'g' );
421 + $type = defender_get_data_from_request( 'type', 'g' );
422 + $ip = defender_get_data_from_request( 'ip', 'g' );
423 + $user_agent = defender_get_data_from_request( 'user_agent', 'g' );
424 + $timezone = wp_timezone();
640 425
641 - $def_filters = array( 'misc' => wd_di()->get( Table_Lockout::class )->get_filters() );
642 - $init_filters = array(
643 - 'from' => strtotime( '-30 days' ),
644 - 'to' => time(),
426 + $init_filters = array(
427 + 'from' => ( new DateTime( '-30 days', $timezone ) )->setTime( 0, 0, 0 )->getTimestamp(),
428 + 'to' => ( new DateTime( 'now', $timezone ) )->setTime( 23, 59, 59 )->getTimestamp(),
645 429 'type' => $type,
646 - 'ip' => '',
430 + 'ip' => $ip,
431 + 'user_agent' => $user_agent,
647 432 'ban_status' => '',
648 433 );
434 + $misc = wd_di()->get( Table_Lockout::class )->get_filters();
435 + $misc['countries'] = $this->countries_list();
436 + $def_filters = array(
437 + 'misc' => $misc,
438 + 'default_filter' => $init_filters,
439 + 'per_page' => self::DEFAULT_PER_PAGE,
440 + );
649 441
650 - return array_merge( $this->retrieve_logs( $init_filters, 1 ), $def_filters );
442 + return array_merge(
443 + $this->retrieve_logs( $init_filters ),
444 + $def_filters,
445 + $this->dump_routes_and_nonces()
446 + );
651 447 }
652 448
653 449 /**
654 450 * Retrieves logs based on the given filters, paging, order, and order by.
@@ -661,8 +457,9 @@
661 457 * - 'ban_status': The ban status of the logs.
662 458 * @param int $paged The page number of the logs to retrieve. Default is 1.
663 459 * @param string $order The order of the logs. Default is 'desc'.
664 460 * @param string $order_by The field to order the logs by. Default is 'id'.
461 + * @param int $per_page Number of logs per page. 0 falls back to the default of 10.
665 462 *
666 463 * @return array An array containing the following keys:
667 464 * - 'count': The total count of logs.
668 465 * - 'logs': The retrieved logs.
@@ -668,13 +465,12 @@
668 465 * - 'logs': The retrieved logs.
669 466 * - 'per_page': The number of logs per page.
670 467 * - 'total_pages': The total number of pages.
671 468 */
672 - private function retrieve_logs( $filters, $paged = 1, $order = 'desc', $order_by = 'id' ): array {
673 - // User can set the number of logs to retrieve per page.
674 - $per_page = (int) defender_get_data_from_request( 'per_page', 'p' );
469 + private function retrieve_logs( $filters, $paged = 1, $order = 'desc', $order_by = 'id', $per_page = 0 ): array {
470 + $per_page = (int) $per_page;
675 471 if ( 0 === $per_page ) {
676 - $per_page = 20;
472 + $per_page = self::DEFAULT_PER_PAGE;
677 473 }
678 474 $conditions = array( 'ban_status' => $filters['ban_status'] );
679 475
680 476 $count = Lockout_Log::count( $filters['from'], $filters['to'], $filters['type'], $filters['ip'], $conditions );
@@ -679,9 +475,8 @@
679 475
680 476 $count = Lockout_Log::count( $filters['from'], $filters['to'], $filters['type'], $filters['ip'], $conditions );
681 477 $logs = Lockout_Log::get_logs_and_format( $filters, $paged, $order_by, $order, $per_page );
682 478 return array(
683 - 'count' => $count,
684 479 'logs' => $logs,
685 480 'per_page' => $per_page,
686 481 'total_pages' => ceil( $count / $per_page ),
687 482 );
@@ -735,9 +530,9 @@
735 530 private function maybe_send_reports( array $logs, bool $is_staging = false ): void {
736 531 $offset = 0;
737 532 $length = 1000;
738 533 $logs_chunk = array_slice( $logs, $offset, $length );
739 - while ( ! empty( $logs_chunk ) ) {
534 + while ( array() !== $logs_chunk ) {
740 535 $data = array(
741 536 'logs' => $logs_chunk,
742 537 );
743 538
@@ -784,9 +579,10 @@
784 579 * @param bool $status Status for sending logs. Send logs to API if true.
785 580 *
786 581 * @since 4.5.0
787 582 */
788 - $send_logs = (bool) apply_filters( 'wpdef_firewall_send_logs_to_api', true );
583 + $send_logs = apply_filters( 'wpdef_firewall_send_logs_to_api', true );
584 + $send_logs = is_bool( $send_logs ) ? $send_logs : (bool) $send_logs;
789 585
790 586 if (
791 587 ! $send_logs ||
792 588 ! $this->wpmudev->is_dash_activated() ||
@@ -803,10 +599,10 @@
803 599 // Log the site ID where the event is triggered.
804 600 $this->log( "{$event_name} is processing from site {$site_id}", Firewall::FIREWALL_LOG );
805 601 $from = time() - ( 7 * DAY_IN_SECONDS );
806 602
807 - $last_run_time = get_site_option( 'wpdef_ip_blocklist_sync_last_run_time' );
808 - if ( $last_run_time ) {
603 + $last_run_time = get_site_option( 'wpdef_ip_blocklist_sync_last_run_time', 0 );
604 + if ( 0 < $last_run_time ) {
809 605 $time_difference = time() - $last_run_time;
810 606
811 607 if ( $time_difference < 7 * DAY_IN_SECONDS ) { // 7 days in seconds
812 608 $from = $last_run_time;
@@ -816,19 +612,19 @@
816 612
817 613 $service = wd_di()->get( Firewall_Logs_Component::class );
818 614
819 615 $logs = $service->get_compact_logs( $from );
820 - if ( ! empty( $logs ) ) {
616 + if ( array() !== $logs ) {
821 617 $this->maybe_send_reports( $logs );
822 618 }
823 619
824 620 $logs = $service->get_akismet_auto_spam_comment_logs();
825 - if ( ! empty( $logs ) ) {
621 + if ( array() !== $logs ) {
826 622 $this->maybe_send_reports( $logs );
827 623 }
828 624
829 625 $logs = $service->get_404_intelligence_logs( $from );
830 - if ( ! empty( $logs ) ) {
626 + if ( array() !== $logs ) {
831 627 $this->maybe_send_reports( $logs, true );
832 628 }
833 629
834 630 // Release lock after execution.
@@ -852,25 +648,31 @@
852 648
853 649 // Retrieve response body safely.
854 650 $body = wp_remote_retrieve_body( $response );
855 651 // If the body is empty or does not equal 'true' (indicating spam), return the response as is.
856 - if ( empty( $body ) || trim( $body ) !== 'true' ) {
652 + if ( ! is_string( $body ) || in_array( trim( $body ), array( '', 'true' ), true ) ) {
857 653 return $response;
858 654 }
859 655
860 656 // Ensure the request body contains data; otherwise, return the response.
861 - if ( empty( $parsed_args['body'] ) ) {
657 + $body_arg = $parsed_args['body'] ?? '';
658 + if (
659 + ( is_string( $body_arg ) && '' === trim( $body_arg ) )
660 + || ( is_array( $body_arg ) && array() === $body_arg )
661 + || ( is_object( $body_arg ) && 0 === count( get_object_vars( $body_arg ) ) )
662 + ) {
862 663 return $response;
863 664 }
864 665
865 - $request_data = wp_parse_args( $parsed_args['body'] );
666 + $request_data = wp_parse_args( $body_arg );
866 667 // If the comment author's IP is not present in the request data, return the response.
867 - if ( empty( $request_data['comment_author_IP'] ) ) {
668 + $author_ip = $request_data['comment_author_IP'] ?? '';
669 + if ( '' === $author_ip ) {
868 670 return $response;
869 671 }
870 672
871 673 // Validate the user IP address from the request data.
872 - $user_ip = filter_var( $request_data['comment_author_IP'], FILTER_VALIDATE_IP );
674 + $user_ip = filter_var( $author_ip, FILTER_VALIDATE_IP );
873 675 if ( false === $user_ip ) {
874 676 return $response;
875 677 }
876 678