PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.8
Patchstack – WordPress & Plugins Security v2.2.8
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
patchstack / includes / listener.php

listener.php in Patchstack – WordPress & Plugins Security 2.2.8, at includes/listener.php

751 lines 22.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * This class is used to communicate from the API to the plugin.
10 */
11 class P_Listener extends P_Core {
12
13 /**
14 * Add the actions required to hide the login page.
15 *
16 * @param Patchstack $core
17 * @return void
18 */
19 public function __construct( $core ) {
20 parent::__construct( $core );
21
22 // Only hook into the action if the authentication is set and valid.
23 if ( isset( $_POST['webarx_secret'] ) && $this->verifyToken( $_POST['webarx_secret'] ) ) {
24 add_action( 'init', [ $this, 'handleRequest' ] );
25 }
26
27 // OTT action.
28 if ( isset( $_POST['patchstack_ott_action'] ) ) {
29 $ott = get_option( 'patchstack_ott_action', '' );
30 if ( ! empty( $ott ) && hash_equals( $ott, $_POST['patchstack_ott_action'] ) ) {
31 $this->setIpHeader();
32 }
33 }
34 }
35
36 /**
37 * Handle the incoming request.
38 *
39 * @return void
40 */
41 public function handleRequest() {
42 // Loop through all possible actions.
43 foreach ( [
44 'webarx_remote_users' => 'listUsers',
45 'webarx_firewall_switch' => 'switchFirewallStatus',
46 'webarx_wordpress_upgrade' => 'wordpressCoreUpgrade',
47 'webarx_theme_upgrade' => 'themeUpgrade',
48 'webarx_plugins_upgrade' => 'pluginsUpgrade',
49 'webarx_plugins_toggle' => 'pluginsToggle',
50 'webarx_plugins_delete' => 'pluginsDelete',
51 'webarx_get_options' => 'getAvailableOptions',
52 'webarx_set_options' => 'saveOptions',
53 'webarx_refresh_rules' => 'refreshRules',
54 'webarx_get_firewall_bans' => 'getFirewallBans',
55 'webarx_firewall_unban_ip' => 'unbanFirewallIp',
56 'webarx_firewall_unban_all' => 'unbanFirewallAll',
57 'webarx_upload_software' => 'uploadSoftware',
58 'webarx_upload_logs' => 'uploadLogs',
59 'webarx_send_ping' => 'sendPing',
60 'webarx_login_bans' => 'getLoginBans',
61 'webarx_unban_login' => 'unbanLogin',
62 'webarx_debug_info' => 'debugInfo',
63 'webarx_set_ip_header' => 'setIpHeader',
64 'webarx_refresh_license' => 'refreshLicense'
65 ] as $key => $action ) {
66 // Special case for Patchstack plugin upgrade.
67 if ( isset( $_POST[ $key ] ) ) {
68 $this->$action();
69 }
70 }
71 }
72
73 /**
74 * Determine if the provided secret hash equals the sha1 of the private id and key.
75 *
76 * @param string $secret Hash that is sent from our API.
77 * @return boolean
78 */
79 public function verifyToken( $secret ) {
80 $id = get_option( 'patchstack_clientid' );
81 $key = $this->get_secret_key();
82
83 if ( empty( $id ) || empty ( $key ) || strlen( $secret ) != 40 ) {
84 return false;
85 }
86
87 return hash_equals( sha1( $id . $key ), $secret );
88 }
89
90 /**
91 * Determine if given action succeded or not, then return the appropriate message.
92 *
93 * @param mixed $thing
94 * @param string $success
95 * @param string $fail
96 * @return void
97 */
98 private function returnResults( $thing, $success = '', $fail = '' ) {
99 if ( ! is_wp_error( $thing ) && $thing !== false ) {
100 wp_send_json( [ 'success' => $success ] );
101 }
102
103 wp_send_json( [ 'error' => $fail ] );
104 }
105
106 /**
107 * Send a ping back to the API.
108 *
109 * @return void
110 */
111 private function sendPing() {
112 do_action( 'patchstack_send_ping' );
113 wp_send_json( [ 'firewall' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ] );
114 }
115
116 /**
117 * Get list of all users on WordPress
118 *
119 * @return void
120 */
121 private function listUsers() {
122 // Only fetch data we actually need.
123 $users = get_users( [ 'role__in' => [ 'administrator', 'editor', 'author', 'contributor' ] ] );
124 $roles = wp_roles();
125 $roles = $roles->get_names();
126 $data = [];
127
128 // Loop through all users.
129 foreach ( $users as $user ) {
130
131 // Get text friendly version of the role.
132 $text = '';
133 foreach ( $user->roles as $role ) {
134 if ( isset( $roles[ $role ] ) ) {
135 $text .= $roles[ $role ] . ', ';
136 } else {
137 $text .= $role . ', ';
138 }
139 }
140
141 // Push to array that we will eventually output.
142 array_push(
143 $data,
144 [
145 'id' => $user->data->ID,
146 'username' => $user->data->user_login,
147 'email' => $user->data->user_email,
148 'roles' => substr( $text, 0, -2 ),
149 ]
150 );
151 }
152
153 wp_send_json( [ 'users' => $data ] );
154 }
155
156 /**
157 * Switch the firewall status from on to off or off to on.
158 *
159 * @return string
160 */
161 private function switchFirewallStatus() {
162 $state = $this->get_option( 'patchstack_basic_firewall' ) == 1;
163 update_option( 'patchstack_basic_firewall', $state == 1 ? 0 : 1 );
164 $this->returnResults( null, 'Firewall ' . ( $state == 1 ? 'disabled' : 'enabled' ) . '.', null );
165 }
166
167 /**
168 * Upgrade the core of WordPress.
169 *
170 * @return string|void
171 */
172 private function wordpressCoreUpgrade() {
173 @set_time_limit( 180 );
174
175 // Get the core update info.
176 wp_version_check();
177 $core = get_site_transient( 'update_core' );
178
179 // Any updates available?
180 if ( ! isset( $core->updates ) ) {
181 $this->returnResults( false, null, 'No update available at this time.' );
182 }
183
184 // Are we on the latest version already?
185 if ( $core->updates[0]->response == 'latest' ) {
186 $this->returnResults( false, null, 'Site is already running the latest version available.' );
187 }
188
189 // Require some libraries and attempt the upgrade.
190 @include_once ABSPATH . '/wp-admin/includes/admin.php';
191 @include_once ABSPATH . '/wp-admin/includes/class-wp-upgrader.php';
192 $skin = new Automatic_Upgrader_Skin();
193 $upgrader = new Core_Upgrader( $skin );
194 $result = $upgrader->upgrade(
195 $core->updates[0],
196 [
197 'attempt_rollback' => true,
198 'do_rollback' => true,
199 'allow_relaxed_file_ownership' => true,
200 ]
201 );
202 if ( ! $result ) {
203 $this->returnResults( false, null, 'The WordPress core could not be upgraded, most likely because of invalid filesystem connection information.' );
204 }
205
206 // Synchronize again with the API.
207 do_action( 'patchstack_send_software_data' );
208 $this->returnResults( $results, 'WordPress core has been upgraded.' );
209 }
210
211 /**
212 * Upgrade a WordPress theme.
213 *
214 * @return string|void
215 */
216 private function themeUpgrade() {
217 if ( !isset( $_POST['webarx_theme_upgrade'] ) ) {
218 return;
219 }
220
221 @set_time_limit( 180 );
222
223 // Require some files we need to execute the upgrade.
224 $theme = wp_filter_nohtml_kses( $_POST['webarx_theme_upgrade'] );
225 @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
226 if ( file_exists( ABSPATH . 'wp-admin/includes/class-theme-upgrader.php' ) ) {
227 @include_once ABSPATH . 'wp-admin/includes/class-theme-upgrader.php';
228 }
229 @include_once ABSPATH . 'wp-admin/includes/misc.php';
230 @include_once ABSPATH . 'wp-admin/includes/file.php';
231
232 // Upgrade the theme.
233 $skin = new Automatic_Upgrader_Skin();
234 $upgrader = new Theme_Upgrader( $skin );
235 $result = $upgrader->upgrade( $theme, [ 'allow_relaxed_file_ownership' => true ] );
236 if ( ! $result ) {
237 $this->returnResults( false, null, 'The theme could not be upgraded, most likely because of invalid filesystem connection information.' );
238 }
239
240 // Synchronize again with the API.
241 do_action( 'patchstack_send_software_data' );
242 $this->returnResults( null, 'The theme has been updated successfully.' );
243 }
244
245 /**
246 * Upgrade a batch of plugins at once.
247 *
248 * @return string|void
249 */
250 private function pluginsUpgrade() {
251 if (!isset( $_POST['webarx_plugins_upgrade'] ) ) {
252 return;
253 }
254
255 @set_time_limit( 180 );
256
257 // Must have a valid number of plugins received to upgrade.
258 $plugins = wp_filter_nohtml_kses( $_POST['webarx_plugins_upgrade'] );
259 $plugins = explode( '|', $plugins );
260 if ( count( $plugins ) == 0 ) {
261 $this->returnResults( false, null, 'No valid plugin names have been given.' );
262 }
263
264 // Require some files we need to execute the upgrade.
265 @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
266 if ( file_exists( ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php' ) ) {
267 @include_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
268 }
269 @include_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
270
271 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
272 @include_once ABSPATH . 'wp-admin/includes/misc.php';
273 @include_once ABSPATH . 'wp-admin/includes/file.php';
274 @include_once ABSPATH . 'wp-admin/includes/template.php';
275 @wp_update_plugins();
276 $all_plugins = get_plugins();
277
278 // New array with all available plugins and the ones we want to upgrade.
279 $upgrade = [];
280 foreach ( $all_plugins as $path => $data ) {
281 $t = explode( '/', $path );
282 if ( in_array( $t[0], $plugins ) ) {
283 array_push( $upgrade, $path );
284 }
285 }
286
287 // Don't continue if we have no valid plugins to upgrade.
288 if ( count( $upgrade ) == 0 ) {
289 $this->returnResults( false, null, 'No valid plugin names have been given.' );
290 }
291
292 // Upgrade the plugins.
293 $skin = new Automatic_Upgrader_Skin();
294 $upgrader = new Plugin_Upgrader( $skin );
295 $result = $upgrader->bulk_upgrade( $upgrade, [ 'allow_relaxed_file_ownership' => true ] );
296 if ( ! $result ) {
297 $this->returnResults( false, null, 'The plugins could not be upgraded, most likely because of invalid filesystem connection information.' );
298 }
299
300 // Synchronize again with the API.
301 do_action( 'patchstack_send_software_data' );
302 $this->returnResults( null, 'The plugins have been updated successfully.' );
303 }
304
305 /**
306 * Toggle the state of a batch of plugin to activated or de-activated.
307 *
308 * @return string|void
309 */
310 private function pluginsToggle() {
311 if (!isset( $_POST['webarx_plugins'], $_POST['webarx_plugins_toggle'] ) ) {
312 return;
313 }
314
315 @set_time_limit( 180 );
316
317 // Must have a valid number of plugins received to toggle.
318 $plugins = wp_filter_nohtml_kses( $_POST['webarx_plugins'] );
319 $plugins = explode( '|', $plugins );
320 $state = $_POST['webarx_plugins_toggle'] == 'on' ? 'on' : 'off';
321 if ( count( $plugins ) == 0 ) {
322 $this->returnResults( false, null, 'No valid plugin names have been given.' );
323 }
324
325 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
326 $all_plugins = get_plugins();
327
328 // New array with all available plugins and the ones we want to toggle.
329 $toggle = [];
330 foreach ( $all_plugins as $path => $data ) {
331 $t = explode( '/', $path );
332
333 // Don't continue if the plugin does not exist locally.
334 if ( ! in_array( $t[0], $plugins ) ) {
335 continue;
336 }
337
338 // If plugin should be turned on, check if it's already turned on first.
339 if ( $state == 'on' && ! is_plugin_active( $path ) ) {
340 array_push( $toggle, $path );
341 }
342
343 // If plugin should be turned off, check if it's already turned off first.
344 if ( $state == 'off' && is_plugin_active( $path ) ) {
345 array_push( $toggle, $path );
346 }
347 }
348
349 // Don't continue if we have no valid plugins to toggle..
350 if ( count( $toggle ) == 0 ) {
351 $this->returnResults( false, null, 'The plugins are already turned ' . $state . '.' );
352 }
353
354 // Turn the plugins on or off?
355 if ( $state == 'on' ) {
356 activate_plugins( $toggle );
357 }
358
359 if ( $state == 'off' ) {
360 deactivate_plugins( $toggle );
361 }
362
363 // Synchronize again with the API.
364 do_action( 'patchstack_send_software_data' );
365 $this->returnResults( null, 'The ' . ( count( $toggle ) == 1 ? 'plugin has' : 'plugins have' ) . ' been successfully turned ' . $state . '.' );
366 }
367
368 /**
369 * Delete a batch of plugins.
370 *
371 * @return string|void
372 */
373 private function pluginsDelete() {
374 if (!isset( $_POST['webarx_plugins'] ) ) {
375 return;
376 }
377
378 @set_time_limit( 180 );
379
380 // Must have a valid number of plugins received to toggle.
381 $plugins = wp_filter_nohtml_kses( $_POST['webarx_plugins'] );
382 $plugins = explode( '|', $plugins );
383 if ( count( $plugins ) == 0 ) {
384 $this->returnResults( false, null, 'No valid plugin names have been given.' );
385 }
386
387 @include_once ABSPATH . 'wp-admin/includes/file.php';
388 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
389 $all_plugins = get_plugins();
390
391 // New array with all available plugins and the ones we want to toggle.
392 $delete = [];
393 foreach ( $all_plugins as $path => $data ) {
394 $t = explode( '/', $path );
395
396 // Don't continue if the plugin does not exist locally.
397 if ( ! in_array( $t[0], $plugins ) ) {
398 continue;
399 }
400
401 array_push( $delete, $path );
402 }
403
404 // Don't continue if we have no valid plugins to toggle..
405 if ( count( $delete ) == 0 ) {
406 $this->returnResults( false, null, 'No valid plugins to delete.' );
407 }
408
409 @deactivate_plugins( $delete );
410 @delete_plugins( $delete );
411
412 // Synchronize again with the API.
413 do_action( 'patchstack_send_software_data' );
414 $this->returnResults( null, 'The plugins have been successfully deleted.' );
415 }
416
417 /**
418 * Save received options.
419 *
420 * @return void
421 */
422 private function saveOptions() {
423 if ( ! isset( $_POST['webarx_set_options'], $_POST['webarx_secret'] ) ) {
424 exit;
425 }
426
427 // Get the received options.
428 $options = json_decode( base64_decode( $_POST['webarx_set_options'] ), true );
429 if ( ! $options || count( $options ) == 0 ) {
430 exit;
431 }
432
433 // Loop through the options and update their value.
434 $exclude_filter = ['patchstack_firewall_custom_rules'];
435 foreach ( $options as $key => $value ) {
436 if ( array_key_exists( $key, $this->plugin->admin_options->options ) ) {
437
438 // Some options should not be filtered and could cause unexpected behavior if they are filtered.
439 if ( ! in_array( $key, $exclude_filter ) ) {
440 $value = map_deep( $value, 'wp_filter_nohtml_kses' );
441 }
442
443 update_option( $key, $value, true );
444 }
445 }
446
447 $this->returnResults( null, 'Plugin options has been updated.' );
448 }
449
450 /**
451 * Return list of keys and values of Patchstack options.
452 *
453 * @return array
454 */
455 private function getAvailableOptions() {
456 // Get all options and filter by the Patchstack prefix.
457 global $wpdb;
458 $options = $wpdb->get_results( "SELECT option_name, option_value FROM " . $wpdb->options . " WHERE option_name LIKE 'patchstack_%'" );
459 $settings = [];
460 $found = [];
461 foreach ( $options as $option ) {
462 array_push( $found, $option->option_name );
463 $settings[] = (array) $option;
464 }
465
466 // Check for potential missing options and add them to the output.
467 foreach( [ 'patchstack_firewall_custom_rules' ] as $slug ) {
468 if ( ! isset ( $found[$slug] ) ) {
469 $settings[] = [
470 'option_name' => $slug,
471 'option_value' => $this->get_option( $slug, '' )
472 ];
473 }
474 }
475
476 // Add custom values which aren't directly available from the options table.
477 // User roles available for whitelisting.
478 $roles = wp_roles();
479 $roles = $roles->get_names();
480 $roles_available = [];
481 foreach ( $roles as $key => $role ) {
482 $roles_available[ $key ] = $role;
483 }
484 $settings[] = [
485 'option_name' => 'patchstack_basic_firewall_roles_available',
486 'option_value' => serialize( $roles_available ),
487 ];
488
489 // Whether or not auto-updates are disabled in the code.
490 $settings[] = [
491 'option_name' => 'patchstack_auto_updates_disabled',
492 'option_value' => defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED,
493 ];
494
495 wp_send_json( $settings );
496 }
497
498 /**
499 * Pull firewall rules from the API.
500 *
501 * @return void
502 */
503 private function refreshRules() {
504 do_action( 'patchstack_post_dynamic_firewall_rules' );
505 $this->returnResults( null, 'Firewall rules have been refreshed.' );
506 }
507
508 /**
509 * Get a list of IP addresses that are currently banned by the firewall.
510 *
511 * @return void|array
512 */
513 private function getFirewallBans($return = false) {
514 // Calculate block time.
515 $minutes = (int) $this->get_option( 'patchstack_autoblock_minutes', 30 );
516 $timeout = (int) $this->get_option( 'patchstack_autoblock_blocktime', 60 );
517 if ( empty( $minutes ) || empty( $timeout ) ) {
518 $time = 30 + 60;
519 } else {
520 $time = $minutes + $timeout;
521 }
522
523 global $wpdb;
524 $results = $wpdb->get_results(
525 $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . "patchstack_firewall_log WHERE apply_ban = 1 AND log_date >= ('" . current_time( 'mysql' ) . "' - INTERVAL %d MINUTE) GROUP BY ip", [ $time ] ),
526 OBJECT
527 );
528
529 $out = [];
530 foreach ( $results as $result ) {
531 if ( isset( $result->ip ) ) {
532 array_push( $out, $result->ip );
533 }
534 }
535
536 if ($return) {
537 return $out;
538 }
539
540 wp_send_json( $out );
541 }
542
543 /**
544 * Unban a specific IP address from the firewall.
545 *
546 * @return void
547 */
548 private function unbanFirewallIp() {
549 if ( ! isset( $_POST['webarx_ip'] ) || !filter_var( $_POST['webarx_ip'], FILTER_VALIDATE_IP ) ) {
550 return;
551 }
552
553 global $wpdb;
554 $wpdb->query( $wpdb->prepare( 'UPDATE ' . $wpdb->prefix . 'patchstack_firewall_log SET apply_ban = 0 WHERE ip = %s', [ $_POST['webarx_ip'] ] ) );
555 $this->returnResults( null, 'The IP has been unbanned.' );
556 }
557
558 /**
559 * Unban a specific IP address from the firewall.
560 *
561 * @return void
562 */
563 private function unbanFirewallAll() {
564 global $wpdb;
565
566 // Get all banned IP addresses.
567 $ips = $this->getFirewallBans(true);
568 if (count($ips) == 0) {
569 $this->returnResults( null, 'There are no IP addresses to unban.' );
570 }
571
572 // Unban all IP addresses.
573 foreach ($ips as $ip) {
574 $wpdb->query( $wpdb->prepare( 'UPDATE ' . $wpdb->prefix . 'patchstack_firewall_log SET apply_ban = 0 WHERE ip = %s', [ $ip ] ) );
575 }
576
577 $this->returnResults( null, 'All IP addresses has been unbanned.' );
578 }
579
580 /**
581 * Send all current software on the WordPress site to the API.
582 *
583 * @return void
584 */
585 private function uploadSoftware() {
586 do_action( 'patchstack_send_software_data' );
587 $this->returnResults( null, 'The software data has been sent to the API.' );
588 }
589
590 /**
591 * Upload the firewall and activity logs.
592 *
593 * @return void
594 */
595 private function uploadLogs() {
596 do_action( 'patchstack_send_hacker_logs' );
597 do_action( 'patchstack_send_event_logs' );
598 $this->returnResults( null, 'The logs have been sent to the API.' );
599 }
600
601 /**
602 * Get the currently banned IP addresses from the login page.
603 *
604 * @return void
605 */
606 private function getLoginBans() {
607 // Calculate block time.
608 $minutes = (int) $this->get_option( 'patchstack_anti_bruteforce_minutes', 30 );
609 $timeout = (int) $this->get_option( 'patchstack_anti_bruteforce_blocktime', 60 );
610 if ( empty( $minutes ) || empty( $timeout ) ) {
611 $time = 30 + 60;
612 } else {
613 $time = $minutes + $timeout;
614 }
615
616 // Check if X failed login attempts were made.
617 global $wpdb;
618 $results = $wpdb->get_results(
619 $wpdb->prepare( 'SELECT id, ip, date FROM ' . $wpdb->prefix . "patchstack_event_log WHERE action = 'failed login' AND date >= ('" . current_time( 'mysql' ) . "' - INTERVAL %d MINUTE) GROUP BY ip HAVING COUNT(ip) >= %d ORDER BY date DESC", [ $time, $this->get_option( 'patchstack_anti_bruteforce_attempts', 10 ) ] ),
620 OBJECT
621 );
622
623 // Return the banned IP addresses.
624 wp_send_json( [ 'banned' => $results ] );
625 }
626
627 /**
628 * Unban a banned login IP address.
629 *
630 * @return void
631 */
632 private function unbanLogin() {
633 if ( ! isset( $_POST['id'], $_POST['type'] ) || !ctype_digit( $_POST['id'] ) ) {
634 exit;
635 }
636
637 global $wpdb;
638
639 // Unblock the IP; delete the logs of the IP.
640 if ( $_POST['type'] == 'unblock' ) {
641 // First get the IP address to unblock.
642 $result = $wpdb->get_results(
643 $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id = %d', [ (int) $_POST['id'] ] )
644 );
645
646 // Unblock the IP address.
647 if ( isset( $result[0], $result[0]->ip ) && filter_var( $result[0]->ip, FILTER_VALIDATE_IP ) ) {
648 $wpdb->query(
649 $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE ip = %s', [ $result[0]->ip ] )
650 );
651 }
652 }
653
654 // Unblock and whitelist the IP.
655 if ( $_POST['type'] == 'unblock_whitelist' ) {
656 // First get the IP address to whitelist.
657 $result = $wpdb->get_results(
658 $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id = %d', [ (int) $_POST['id'] ] )
659 );
660
661 // Whitelist and unblock the IP address.
662 if ( isset( $result[0], $result[0]->ip ) && filter_var( $result[0]->ip, FILTER_VALIDATE_IP ) ) {
663 update_option( 'patchstack_login_whitelist', $this->get_option( 'patchstack_login_whitelist', '' ) . "\n" . $result[0]->ip );
664 $wpdb->query(
665 $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE ip = %s', [ $result[0]->ip ] )
666 );
667 }
668 }
669
670 $this->returnResults( null, 'The unban has been processed.' );
671 }
672
673 /**
674 * Get information for debugging purposes.
675 *
676 * @return void
677 */
678 private function debugInfo() {
679 $debug = [
680 'server' => $_SERVER,
681 'php' => phpversion()
682 ];
683
684 wp_send_json( $debug );
685 }
686
687 /**
688 * Try to determine the proper IP address headers.
689 *
690 * @return void
691 */
692 private function setIpHeader() {
693 if ( ! isset( $_POST['ip'] ) ) {
694 return;
695 }
696
697 $ips = ! is_array ( $_POST['ip'] ) ? [ $_POST['ip'] ] : $_POST['ip'];
698
699 // REMOTE_ADDR?
700 foreach ( $ips as $ip ) {
701 if ( isset( $_SERVER['REMOTE_ADDR'] ) && $_SERVER['REMOTE_ADDR'] == $ip ) {
702 update_option( 'patchstack_firewall_ip_header', 'REMOTE_ADDR' );
703 update_option( 'patchstack_ip_header_computed', 1 );
704 update_option( 'patchstack_ott_action', '' );
705 wp_send_json( [ 'success' => true, 'header' => 'REMOTE_ADDR' ] );
706 }
707 }
708
709 // IP address headers in order of priority.
710 $priority = [ 'REMOTE_ADDR', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_SUCURI_CLIENTIP', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'SUCURI_RIP' ];
711 foreach ( $ips as $ip ) {
712 foreach ( $priority as $header ) {
713 if ( isset( $_SERVER[ $header ] ) && $_SERVER[ $header ] == $ip ) {
714 update_option( 'patchstack_firewall_ip_header', $header );
715 update_option( 'patchstack_ip_header_computed', 1 );
716 update_option( 'patchstack_ott_action', '' );
717 wp_send_json( [ 'success' => true, 'header' => $header ] );
718 }
719 }
720 }
721
722 // Still not found? Iterate over all $_SERVER keys.
723 foreach ( $ips as $ip ) {
724 foreach ( $_SERVER as $key => $value ) {
725 if ( $value == $ip ) {
726 update_option( 'patchstack_firewall_ip_header', $key );
727 update_option( 'patchstack_ip_header_computed', 1 );
728 update_option( 'patchstack_ott_action', '' );
729 wp_send_json( [ 'success' => true, 'header' => $key ] );
730 }
731 }
732 }
733
734 update_option( 'patchstack_ott_action', '' );
735 wp_send_json( [ 'success' => false, 'header' => 'unknown' ] );
736 }
737
738 /**
739 * Refresh the license and subscription information.
740 *
741 * @return void
742 */
743 private function refreshLicense (){
744 do_action( 'update_license_status' );
745 do_action( 'patchstack_send_software_data' );
746 do_action( 'patchstack_post_dynamic_firewall_rules' );
747
748 wp_send_json( array( 'success' => true ) );
749 }
750 }
751