iThemes Security (formerly Better WP Security) - Version 7.6.0

Version Description

  • New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
  • Enhancement: Run Security Check Pro IP Detection automatically once a day.
  • Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
Download this release

Release Info

Developer TimothyBlynJacobs
Plugin Icon 128x128 iThemes Security (formerly Better WP Security)
Version 7.6.0
Comparing to
See all releases

Code changes from version 7.5.0 to 7.6.0

Files changed (37) hide show
  1. better-wp-security.php +1 -1
  2. core/admin-pages/js/settings.js +13 -0
  3. core/admin-pages/module-settings.php +12 -0
  4. core/admin-pages/page-settings.php +4 -0
  5. core/core.php +3 -1
  6. core/history.txt +6 -0
  7. core/lib.php +22 -64
  8. core/lib/class-itsec-ip-detector.php +119 -0
  9. core/lib/class-itsec-lib-ip-detector.php +98 -0
  10. core/modules.php +2 -1
  11. core/modules/global/css/index.php +1 -0
  12. core/modules/global/css/settings-page.css +15 -0
  13. core/modules/global/js/settings-page.js +106 -43
  14. core/modules/global/settings-page.php +260 -235
  15. core/modules/global/setup.php +12 -7
  16. core/modules/global/validator.php +33 -9
  17. core/modules/security-check-pro/activate.php +3 -0
  18. core/modules/security-check-pro/active.php +5 -0
  19. core/modules/security-check-pro/class-itsec-security-check-pro.php +82 -0
  20. core/modules/security-check-pro/deactivate.php +5 -0
  21. core/modules/security-check-pro/debug.php +60 -0
  22. core/modules/security-check-pro/index.php +1 -0
  23. core/modules/security-check-pro/js/debug.js +18 -0
  24. core/modules/security-check-pro/js/index.php +1 -0
  25. core/modules/security-check-pro/js/settings-page.js +106 -0
  26. core/modules/security-check-pro/privacy.php +15 -0
  27. core/modules/security-check-pro/settings-page.php +34 -0
  28. core/modules/security-check-pro/settings.php +20 -0
  29. core/modules/security-check-pro/utility.php +328 -0
  30. core/modules/security-check-pro/validator.php +43 -0
  31. core/modules/security-check/js/settings-page.js +2 -1
  32. core/modules/security-check/settings-page.php +40 -23
  33. core/package.json +3 -2
  34. core/response.php +9 -0
  35. history.txt +4 -0
  36. package.json +3 -2
  37. readme.txt +8 -3
better-wp-security.php CHANGED
@@ -6,7 +6,7 @@
6
  * Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
7
  * Author: iThemes
8
  * Author URI: https://ithemes.com
9
- * Version: 7.5.0
10
  * Text Domain: better-wp-security
11
  * Network: True
12
  * License: GPLv2
6
  * Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
7
  * Author: iThemes
8
  * Author URI: https://ithemes.com
9
+ * Version: 7.6.0
10
  * Text Domain: better-wp-security
11
  * Network: True
12
  * License: GPLv2
core/admin-pages/js/settings.js CHANGED
@@ -216,6 +216,19 @@ var itsecSettingsPage = {
216
  itsecUtil.sendAJAXRequest( module, 'save', data, itsecSettingsPage.saveSettingsCallback );
217
  },
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  saveSettingsCallback: function( results ) {
220
  if ( '' === results.module ) {
221
  jQuery( '#itsec-save' ).prop( 'disabled', false );
216
  itsecUtil.sendAJAXRequest( module, 'save', data, itsecSettingsPage.saveSettingsCallback );
217
  },
218
 
219
+ scrollTop: function() {
220
+ var $container = jQuery( '.itsec-module-cards-container' );
221
+ var view = $container.hasClass( 'grid' ) ? 'grid' : 'list';
222
+
223
+ if ( 'grid' === view ) {
224
+ $container.find( '.itsec-module-settings-content-container:visible' ).animate( { 'scrollTop': 0 }, 'fast' );
225
+ }
226
+
227
+ if ( 'list' === view ) {
228
+ jQuery( document ).scrollTop( 0 );
229
+ }
230
+ },
231
+
232
  saveSettingsCallback: function( results ) {
233
  if ( '' === results.module ) {
234
  jQuery( '#itsec-save' ).prop( 'disabled', false );
core/admin-pages/module-settings.php CHANGED
@@ -2,6 +2,18 @@
2
 
3
  /**
4
  * The iThemes Security Module Settings Page API parent class.
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
  class ITSEC_Module_Settings_Page {
7
  /**
2
 
3
  /**
4
  * The iThemes Security Module Settings Page API parent class.
5
+ *
6
+ * @property-read string $id
7
+ * @property-read string $title
8
+ * @property-read string $description
9
+ * @property-read string $type
10
+ * @property-read string $pro
11
+ * @property-read bool $can_save
12
+ * @property-read bool $redraw_on_save
13
+ * @property-read bool $upsell
14
+ * @property-read string $upsell_url
15
+ * @property-read bool $information_only
16
+ * @property-read string $status
17
  */
18
  class ITSEC_Module_Settings_Page {
19
  /**
core/admin-pages/page-settings.php CHANGED
@@ -5,7 +5,11 @@ final class ITSEC_Settings_Page {
5
  private static $instance;
6
 
7
  private $self_url = '';
 
 
8
  private $modules = array();
 
 
9
  private $widgets = array();
10
  private $translations = array();
11
 
5
  private static $instance;
6
 
7
  private $self_url = '';
8
+
9
+ /** @var ITSEC_Module_Settings_Page[] */
10
  private $modules = array();
11
+
12
+ /** @var ITSEC_Settings_Page_Sidebar_Widget[] */
13
  private $widgets = array();
14
  private $translations = array();
15
 
core/core.php CHANGED
@@ -24,7 +24,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
24
  *
25
  * @access private
26
  */
27
- private $plugin_build = 4115;
28
 
29
  /**
30
  * Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
@@ -297,6 +297,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
297
  */
298
  public function register_events( $scheduler ) {
299
  $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'clear-locks' );
 
300
  }
301
 
302
  /**
@@ -342,6 +343,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
342
  ITSEC_Modules::register_module( 'file-writing', "$path/modules/file-writing", 'always-active' );
343
  ITSEC_Modules::register_module( 'malware', "$path/modules/malware", 'always-active' );
344
  ITSEC_Modules::register_module( 'feature-flags', "$path/modules/feature-flags", 'always-active' );
 
345
 
346
  if ( ! ITSEC_Core::is_pro() ) {
347
  ITSEC_Modules::register_module( 'pro-module-upsells', "$path/modules/pro", 'always-active' );
24
  *
25
  * @access private
26
  */
27
+ private $plugin_build = 4116;
28
 
29
  /**
30
  * Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
297
  */
298
  public function register_events( $scheduler ) {
299
  $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'clear-locks' );
300
+ $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'health-check' );
301
  }
302
 
303
  /**
343
  ITSEC_Modules::register_module( 'file-writing', "$path/modules/file-writing", 'always-active' );
344
  ITSEC_Modules::register_module( 'malware', "$path/modules/malware", 'always-active' );
345
  ITSEC_Modules::register_module( 'feature-flags', "$path/modules/feature-flags", 'always-active' );
346
+ ITSEC_Modules::register_module( 'security-check-pro', "$path/modules/security-check-pro", self::is_pro() ? 'always-active' : 'default-inactive' );
347
 
348
  if ( ! ITSEC_Core::is_pro() ) {
349
  ITSEC_Modules::register_module( 'pro-module-upsells', "$path/modules/pro", 'always-active' );
core/history.txt CHANGED
@@ -833,3 +833,9 @@
833
  5.4.2 - 2019-11-14 - Timothy Jacobs
834
  Tweak: Add stub Passwordless Login settings page.
835
  Bug Fix: PHP warning if lockout_active field is missing.
 
 
 
 
 
 
833
  5.4.2 - 2019-11-14 - Timothy Jacobs
834
  Tweak: Add stub Passwordless Login settings page.
835
  Bug Fix: PHP warning if lockout_active field is missing.
836
+ 5.4.3 - 2019-11-18 - Timothy Jacobs
837
+ Tweak: Introduce ITSEC_Lib method to retrieve WP branch version.
838
+ 5.5.0 - 2019-12-09 - Timothy Jacobs
839
+ New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
840
+ Enhancement: Run Security Check Pro IP Detection automatically once a day.
841
+ Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
core/lib.php CHANGED
@@ -160,6 +160,8 @@ final class ITSEC_Lib {
160
  *
161
  * @since 4.0.0
162
  *
 
 
163
  * @return String The IP address of the user
164
  */
165
  public static function get_ip( $use_cache = true ) {
@@ -172,82 +174,23 @@ final class ITSEC_Lib {
172
  if ( false !== $ip ) {
173
  $ip = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE );
174
 
175
- if ( ! empty( $ip ) ) {
176
  $GLOBALS['__itsec_remote_ip'] = $ip;
177
 
178
  return $ip;
179
  }
180
  }
181
 
182
- unset( $ip );
183
-
184
- $headers = array(
185
- 'HTTP_CF_CONNECTING_IP', // CloudFlare
186
- 'HTTP_X_FORWARDED_FOR', // Squid and most other forward and reverse proxies
187
- 'REMOTE_ADDR', // Default source of remote IP
188
- );
189
-
190
- $headers = (array) apply_filters( 'itsec_filter_remote_addr_headers', $headers );
191
- $proxy = ITSEC_Modules::get_setting( 'global', 'proxy' );
192
-
193
- switch ( $proxy ) {
194
- case 'disabled':
195
- return $GLOBALS['__itsec_remote_ip'] = $_SERVER['REMOTE_ADDR'];
196
- case 'manual':
197
- $header = ITSEC_Modules::get_setting( 'global', 'proxy_header' );
198
-
199
- if ( in_array( $header, $headers, true ) ) {
200
- $headers = array( $header );
201
- }
202
- break;
203
- }
204
-
205
- if ( ! in_array( 'REMOTE_ADDR', $headers, true ) ) {
206
- $headers[] = 'REMOTE_ADDR';
207
- }
208
-
209
- // Loop through twice. The first run won't accept a reserved or private range IP. If an acceptable IP is not
210
- // found, try again while accepting reserved or private range IPs.
211
- for ( $x = 0; $x < 2; $x ++ ) {
212
- foreach ( $headers as $header ) {
213
- if ( ! isset( $_SERVER[ $header ] ) ) {
214
- continue;
215
- }
216
-
217
- $ip = trim( $_SERVER[ $header ] );
218
-
219
- if ( empty( $ip ) ) {
220
- continue;
221
- }
222
-
223
- if ( false !== ( $comma_index = strpos( $_SERVER[ $header ], ',' ) ) ) {
224
- $ip = substr( $ip, 0, $comma_index );
225
- }
226
-
227
- if ( 0 === $x ) {
228
- // First run through. Only accept an IP not in the reserved or private range.
229
- $ip = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE );
230
- } else {
231
- $ip = filter_var( $ip, FILTER_VALIDATE_IP );
232
- }
233
-
234
- if ( ! empty( $ip ) ) {
235
- break;
236
- }
237
- }
238
-
239
- if ( ! empty( $ip ) ) {
240
- break;
241
- }
242
- }
243
 
244
- if ( empty( $ip ) ) {
245
  // If an IP is not found, force it to a localhost IP that would not be blacklisted as this typically
246
  // indicates a local request that does not provide the localhost IP.
247
  $ip = '127.0.0.1';
248
  }
249
 
250
- $GLOBALS['__itsec_remote_ip'] = (string) $ip;
251
 
252
  return $GLOBALS['__itsec_remote_ip'];
253
  }
@@ -1882,4 +1825,19 @@ final class ITSEC_Lib {
1882
  define( 'DONOTCACHEPAGE', true );
1883
  }
1884
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1885
  }
160
  *
161
  * @since 4.0.0
162
  *
163
+ * @param bool $use_cache Whether to check the cache, or force the retrieval of a new value.
164
+ *
165
  * @return String The IP address of the user
166
  */
167
  public static function get_ip( $use_cache = true ) {
174
  if ( false !== $ip ) {
175
  $ip = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE );
176
 
177
+ if ( $ip ) {
178
  $GLOBALS['__itsec_remote_ip'] = $ip;
179
 
180
  return $ip;
181
  }
182
  }
183
 
184
+ self::load( 'ip-detector' );
185
+ $ip = ITSEC_Lib_IP_Detector::build()->get();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
+ if ( ! $ip ) {
188
  // If an IP is not found, force it to a localhost IP that would not be blacklisted as this typically
189
  // indicates a local request that does not provide the localhost IP.
190
  $ip = '127.0.0.1';
191
  }
192
 
193
+ $GLOBALS['__itsec_remote_ip'] = $ip;
194
 
195
  return $GLOBALS['__itsec_remote_ip'];
196
  }
1825
  define( 'DONOTCACHEPAGE', true );
1826
  }
1827
  }
1828
+
1829
+ /**
1830
+ * Get the WordPress branch version.
1831
+ *
1832
+ * @example 5.2.4 => 5.2
1833
+ *
1834
+ * @return string
1835
+ */
1836
+ public static function get_wp_branch() {
1837
+ $version = get_bloginfo( 'version' );
1838
+
1839
+ list( $major, $minor ) = explode( '.', $version );
1840
+
1841
+ return $major . '.' . $minor;
1842
+ }
1843
  }
core/lib/class-itsec-ip-detector.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class ITSEC_IP_Detector
5
+ *
6
+ * @internal Use {@see ITSEC_Lib::get_ip()} instead of this class directly.
7
+ */
8
+ class ITSEC_IP_Detector {
9
+
10
+ /** @var array */
11
+ private $server;
12
+
13
+ /** @var array */
14
+ private $headers = [];
15
+
16
+ /** @var bool */
17
+ private $allow_private = false;
18
+
19
+ /**
20
+ * ITSEC_IP_Detector constructor.
21
+ *
22
+ * A new detector instance should be created whenever you look for a new IP.
23
+ *
24
+ * @param array $server A copy of $_SERVER.
25
+ */
26
+ public function __construct( array $server ) { $this->server = $server; }
27
+
28
+ /**
29
+ * Add a header to check for an IP.
30
+ *
31
+ * @param string $header The header name.
32
+ * @param int $position If multiple IPs are included in this header,
33
+ * the 0-based position of the IP to return.
34
+ *
35
+ * @return $this
36
+ */
37
+ public function add_header( $header, $position = - 1 ) {
38
+ $this->headers[] = [ $header, $position ];
39
+
40
+ return $this;
41
+ }
42
+
43
+ /**
44
+ * Get the IP address for this request.
45
+ *
46
+ * @return string
47
+ */
48
+ public function get() {
49
+ if ( $ip = $this->get_ip() ) {
50
+ return $ip;
51
+ }
52
+
53
+ $this->allow_private = true;
54
+
55
+ return $this->get_ip();
56
+ }
57
+
58
+ /**
59
+ * Get the IP given the current configuration.
60
+ *
61
+ * @return string
62
+ */
63
+ private function get_ip() {
64
+ foreach ( $this->headers as list( $header, $position ) ) {
65
+ $ip = $this->get_for_header( $header, $position );
66
+
67
+ if ( ! $ip ) {
68
+ continue;
69
+ }
70
+
71
+ if ( $this->allow_private ) {
72
+ $ip = filter_var( $ip, FILTER_VALIDATE_IP );
73
+ } else {
74
+ $ip = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE );
75
+ }
76
+
77
+ if ( $ip ) {
78
+ return (string) $ip;
79
+ }
80
+ }
81
+
82
+ return '';
83
+ }
84
+
85
+ /**
86
+ * Get the IP address for a header.
87
+ *
88
+ * @param string $header
89
+ * @param int $position
90
+ *
91
+ * @return string
92
+ */
93
+ private function get_for_header( $header, $position ) {
94
+ if ( empty( $this->server[ $header ] ) ) {
95
+ return '';
96
+ }
97
+
98
+ $value = trim( $this->server[ $header ] );
99
+
100
+ if ( - 1 === $position ) {
101
+ return explode( ',', $value )[0];
102
+ }
103
+
104
+ // Handle Forwarded: header syntax https://tools.ietf.org/html/rfc7239#section-4
105
+ if ( preg_match_all( '{(?:for)=(?:"?\[?)([a-z0-9\.:_\-/]*)}i', $value, $matches, PREG_SET_ORDER ) ) {
106
+ if ( ! empty( $matches[ $position ][1] ) ) {
107
+ return $matches[ $position ][1];
108
+ }
109
+ }
110
+
111
+ $parts = preg_split( '/[, ]/', $value );
112
+
113
+ if ( ! empty( $parts[ $position ] ) ) {
114
+ return $parts[ $position ];
115
+ }
116
+
117
+ return '';
118
+ }
119
+ }
core/lib/class-itsec-lib-ip-detector.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once( __DIR__ . '/class-itsec-ip-detector.php' );
4
+
5
+ /**
6
+ * Class ITSEC_Lib_IP_Detector
7
+ *
8
+ * @internal Use {@see ITSEC_Lib::get_ip()} instead of this class directly.
9
+ */
10
+ class ITSEC_Lib_IP_Detector {
11
+
12
+ public static function get_proxy_types() {
13
+ $types = array(
14
+ 'automatic' => esc_html__( 'Automatic', 'better-wp-security' ),
15
+ 'manual' => esc_html__( 'Manual', 'better-wp-security' ),
16
+ 'disabled' => esc_html__( 'Disabled', 'better-wp-security' ),
17
+ );
18
+
19
+ /**
20
+ * Filters the list of available proxy types.
21
+ *
22
+ * @param string[] $types List of available proxy types.
23
+ */
24
+ return apply_filters( 'itsec_proxy_types', $types );
25
+ }
26
+
27
+ /**
28
+ * Get a list of the available proxy headers.
29
+ *
30
+ * @return string[]
31
+ */
32
+ public static function get_proxy_headers() {
33
+ return apply_filters( 'itsec_filter_remote_addr_headers', array(
34
+ 'HTTP_CF_CONNECTING_IP', // CloudFlare
35
+ 'HTTP_X_FORWARDED_FOR', // Squid and most other forward and reverse proxies
36
+ ) );
37
+ }
38
+
39
+ /**
40
+ * Build an IP detector instance from the configured settings.
41
+ *
42
+ * @return ITSEC_IP_Detector
43
+ */
44
+ public static function build() {
45
+ $proxy = ITSEC_Modules::get_setting( 'global', 'proxy' );
46
+
47
+ return self::build_for_type( $proxy );
48
+ }
49
+
50
+ /**
51
+ * Build a detector for a given proxy type and args.
52
+ *
53
+ * @param string $proxy
54
+ * @param array $args
55
+ *
56
+ * @return ITSEC_IP_Detector
57
+ */
58
+ public static function build_for_type( $proxy, array $args = [] ) {
59
+ $detector = new ITSEC_IP_Detector( $_SERVER );
60
+
61
+ $headers = self::get_proxy_headers();
62
+
63
+ /**
64
+ * Fires when a new IP detector is used.
65
+ *
66
+ * The dynamic portion of the hook name, `$proxy`, refers to the proxy type.
67
+ *
68
+ * @param bool $configured Was the detector configured.
69
+ * @param ITSEC_IP_Detector $detector The IP detector.
70
+ * @param array $args Additional args to customize the behavior.
71
+ */
72
+ $configured = apply_filters( "itsec_build_ip_detector_for_{$proxy}", false, $detector, $args );
73
+
74
+ if ( ! $configured ) {
75
+ switch ( $proxy ) {
76
+ case 'disabled':
77
+ break;
78
+ case 'manual':
79
+ $header = empty( $args['header'] ) ? ITSEC_Modules::get_setting( 'global', 'proxy_header' ) : $args['header'];
80
+
81
+ if ( in_array( $header, $headers, true ) ) {
82
+ $detector->add_header( $header );
83
+ }
84
+ break;
85
+ case 'automatic':
86
+ default:
87
+ foreach ( $headers as $header ) {
88
+ $detector->add_header( $header );
89
+ }
90
+ break;
91
+ }
92
+ }
93
+
94
+ $detector->add_header( 'REMOTE_ADDR' );
95
+
96
+ return $detector;
97
+ }
98
+ }
core/modules.php CHANGED
@@ -251,7 +251,8 @@ final class ITSEC_Modules {
251
  /**
252
  * Update a single setting in a module.
253
  *
254
- * The new value will be validated, updated- in-memory, and persisted.
 
255
  *
256
  * @param string $slug The module slug.
257
  * @param string $name The setting name to updated.
251
  /**
252
  * Update a single setting in a module.
253
  *
254
+ * The new value will be validated and updated in memory. The change isn't persisted until
255
+ * the end of the request or a manual call to {@see ITSEC_Storage::save()}.
256
  *
257
  * @param string $slug The module slug.
258
  * @param string $name The setting name to updated.
core/modules/global/css/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/global/css/settings-page.css ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-global-proxy-wrapper {
2
+ margin: 1em 0;
3
+ }
4
+
5
+ .itsec-global-proxy-wrapper #itsec-global-proxy {
6
+ margin: 0 !important;
7
+ }
8
+
9
+ .itsec-global-detected-ip {
10
+ background: #edeff0;
11
+ color: #23282d;
12
+ padding: 10px 20px;
13
+ border-radius: 14px;
14
+ max-width: max-content;
15
+ }
core/modules/global/js/settings-page.js CHANGED
@@ -1,5 +1,5 @@
1
  function itsec_change_show_error_codes( args ) {
2
- var show = args[0];
3
 
4
  if ( show ) {
5
  jQuery( 'body' ).addClass( 'itsec-show-error-codes' );
@@ -9,7 +9,7 @@ function itsec_change_show_error_codes( args ) {
9
  }
10
 
11
  function itsec_change_write_files( args ) {
12
- var enabled = args[0];
13
 
14
  if ( enabled ) {
15
  jQuery( 'body' ).removeClass( 'itsec-write-files-disabled' ).addClass( 'itsec-write-files-enabled' );
@@ -18,55 +18,118 @@ function itsec_change_write_files( args ) {
18
  }
19
  }
20
 
21
- var itsec_log_type_changed = function() {
22
- var type = jQuery( '#itsec-global-log_type' ).val();
23
-
24
- if ( 'both' === type ) {
25
- jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).show();
26
- jQuery( '#itsec-global-file_log_rotation' ).parents( 'tr' ).show();
27
- jQuery( '#itsec-global-log_location' ).parents( 'tr' ).show();
28
- } else if ( 'file' === type ) {
29
- jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).hide();
30
- jQuery( '#itsec-global-file_log_rotation' ).parents( 'tr' ).show();
31
- jQuery( '#itsec-global-log_location' ).parents( 'tr' ).show();
32
- } else {
33
- jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).show();
34
- jQuery( '#itsec-global-file_log_rotation' ).parents( 'tr' ).hide();
35
- jQuery( '#itsec-global-log_location' ).parents( 'tr' ).hide();
 
 
36
  }
37
- };
38
 
39
- jQuery( document ).ready(function($) {
40
- var $container = jQuery( '#wpcontent' );
41
 
42
- $container.on( 'click', '#itsec-global-add-to-whitelist', function( e ) {
43
- e.preventDefault();
 
 
 
44
 
45
- var whitelist = jQuery( '#itsec-global-lockout_white_list' ).val();
46
- whitelist = whitelist.trim();
47
- whitelist += "\n" + itsec_global_settings_page.ip;
48
- jQuery( '#itsec-global-lockout_white_list' ).val( whitelist );
49
- } );
50
 
51
- $container.on( 'click', '#itsec-global-reset-log-location', function( e ) {
52
- e.preventDefault();
53
 
54
- jQuery( '#itsec-global-log_location' ).val( itsec_global_settings_page.log_location );
55
- } );
 
 
56
 
57
- $container.on( 'change', '#itsec-global-log_type', itsec_log_type_changed );
 
 
 
 
58
 
59
- itsec_log_type_changed();
 
 
 
 
60
 
61
- function proxyHeaderChanged() {
62
- if ( 'manual' === $( "#itsec-global-proxy" ).val() ) {
63
- $( '.itsec-global-proxy_header-container' ).show();
64
- } else {
65
- $( '.itsec-global-proxy_header-container' ).hide();
66
- }
 
 
 
 
67
  }
68
 
69
- proxyHeaderChanged();
70
- $( document ).on( 'change', '#itsec-global-proxy', proxyHeaderChanged );
71
- itsecSettingsPage.events.on( 'modulesReloaded', proxyHeaderChanged );
72
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  function itsec_change_show_error_codes( args ) {
2
+ var show = args[ 0 ];
3
 
4
  if ( show ) {
5
  jQuery( 'body' ).addClass( 'itsec-show-error-codes' );
9
  }
10
 
11
  function itsec_change_write_files( args ) {
12
+ var enabled = args[ 0 ];
13
 
14
  if ( enabled ) {
15
  jQuery( 'body' ).removeClass( 'itsec-write-files-disabled' ).addClass( 'itsec-write-files-enabled' );
18
  }
19
  }
20
 
21
+ ( function( $ ) {
22
+ function logTypeChanged() {
23
+ var type = jQuery( '#itsec-global-log_type' ).val();
24
+
25
+ if ( 'both' === type ) {
26
+ jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).show();
27
+ jQuery( '#itsec-global-file_log_rotation' ).parents( 'tr' ).show();
28
+ jQuery( '#itsec-global-log_location' ).parents( 'tr' ).show();
29
+ } else if ( 'file' === type ) {
30
+ jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).hide();
31
+ jQuery( '#itsec-global-file_log_rotation' ).parents( 'tr' ).show();
32
+ jQuery( '#itsec-global-log_location' ).parents( 'tr' ).show();
33
+ } else {
34
+ jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).show();
35
+ jQuery( '#itsec-global-file_log_rotation' ).parents( 'tr' ).hide();
36
+ jQuery( '#itsec-global-log_location' ).parents( 'tr' ).hide();
37
+ }
38
  }
 
39
 
40
+ function proxyHeaderChanged() {
41
+ var proxy = $( '#itsec-global-proxy' ).val();
42
 
43
+ if ( 'security-check' === proxy ) {
44
+ $( '#itsec-global-ip-scan' ).show();
45
+ } else {
46
+ $( '#itsec-global-ip-scan' ).hide();
47
+ }
48
 
49
+ if ( 'manual' === proxy ) {
50
+ $( '.itsec-global-proxy_header-container' ).show();
51
+ } else {
52
+ $( '.itsec-global-proxy_header-container' ).hide();
53
+ }
54
 
55
+ updateIp();
56
+ }
57
 
58
+ function updateIp() {
59
+ $( '.itsec-global-detected-ip' ).text( itsec_global_settings_page.l10n.loading );
60
+ var proxyType = $( '#itsec-global-proxy' ).val();
61
+ var args = {};
62
 
63
+ switch ( proxyType ) {
64
+ case 'manual':
65
+ args.header = $( '#itsec-global-proxy_header' ).val();
66
+ break;
67
+ }
68
 
69
+ var data = {
70
+ method: 'get-ip',
71
+ proxy : proxyType,
72
+ args : args,
73
+ };
74
 
75
+ itsecUtil.sendModuleAJAXRequest( 'global', data, function( r ) {
76
+ if ( r.errors.length > 0 ) {
77
+ itsecSettingsPage.showErrors( r.errors, 'global', 'open', 'error' );
78
+ itsecSettingsPage.scrollTop();
79
+ }
80
+
81
+ if ( r.response ) {
82
+ $( '.itsec-global-detected-ip' ).text( r.response.ip_l10n );
83
+ }
84
+ } );
85
  }
86
 
87
+ $( function() {
88
+ $( document ).on( 'click', '#itsec-global-add-to-whitelist', function( e ) {
89
+ e.preventDefault();
90
+
91
+ var $whitelist = $( '#itsec-global-lockout_white_list' ),
92
+ whitelist = $whitelist.val().trim();
93
+ whitelist += '\n' + itsec_global_settings_page.ip;
94
+ $whitelist.val( whitelist );
95
+ } );
96
+
97
+ $( document ).on( 'click', '#itsec-global-reset-log-location', function( e ) {
98
+ e.preventDefault();
99
+
100
+ $( '#itsec-global-log_location' ).val( itsec_global_settings_page.log_location );
101
+ } );
102
+
103
+ $( document ).on( 'click', '#itsec-global-ip-scan', function( e ) {
104
+ e.preventDefault();
105
+
106
+ var $btn = $( this ).attr( 'disabled', true );
107
+
108
+ itsecUtil.sendModuleAJAXRequest( 'global', { method: 'scan-ip' }, function( r ) {
109
+ if ( r.errors.length > 0 ) {
110
+ itsecSettingsPage.showErrors( r.errors, 'global', 'open', 'error' );
111
+ itsecSettingsPage.scrollTop();
112
+ }
113
+
114
+ if ( r.response ) {
115
+ $( '.itsec-global-detected-ip' ).text( r.response.ip_l10n );
116
+ }
117
+
118
+ $btn.attr( 'disabled', false );
119
+ } );
120
+ } );
121
+
122
+ $( document ).on( 'change', '#itsec-global-log_type', logTypeChanged );
123
+ $( document ).on( 'change', '#itsec-global-proxy', proxyHeaderChanged );
124
+ $( document ).on( 'change', '#itsec-global-proxy_header', updateIp );
125
+ itsecSettingsPage.events.on( 'modulesReloaded', proxyHeaderChanged );
126
+ itsecSettingsPage.events.on( 'moduleReloaded', function( e, module ) {
127
+ if ( 'global' === module ) {
128
+ proxyHeaderChanged();
129
+ }
130
+ } );
131
+
132
+ logTypeChanged();
133
+ proxyHeaderChanged();
134
+ } );
135
+ } )( jQuery );
core/modules/global/settings-page.php CHANGED
@@ -1,14 +1,13 @@
1
  <?php
2
 
3
  final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
4
- private $version = 3;
5
-
6
 
7
  public function __construct() {
8
- $this->id = 'global';
9
- $this->title = __( 'Global Settings', 'better-wp-security' );
10
  $this->description = __( 'Configure basic settings that control how iThemes Security functions.', 'better-wp-security' );
11
- $this->type = 'recommended';
12
 
13
  parent::__construct();
14
 
@@ -35,10 +34,14 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
35
  $vars = array(
36
  'ip' => ITSEC_Lib::get_ip(),
37
  'log_location' => ITSEC_Modules::get_default( $this->id, 'log_location' ),
 
 
 
38
  );
39
 
40
- wp_enqueue_script( 'itsec-global-settings-page-script', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery', 'itsec-settings-page-script' ), $this->version, true );
41
  wp_localize_script( 'itsec-global-settings-page-script', 'itsec_global_settings_page', $vars );
 
42
  }
43
 
44
  public function handle_form_post( $data ) {
@@ -55,15 +58,64 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
55
  }
56
  }
57
 
58
- protected function render_description( $form ) {
59
-
60
- ?>
61
- <p><?php _e( 'The following settings modify the behavior of many of the features offered by iThemes Security.', 'better-wp-security' ); ?></p>
62
- <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
 
 
 
 
64
  }
65
 
66
  protected function render_settings( $form ) {
 
67
  $validator = ITSEC_Modules::get_validator( $this->id );
68
 
69
  $log_types = $validator->get_valid_log_types();
@@ -80,247 +132,220 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
80
 
81
  $proxy = array( 'value' => $validator->get_proxy_types() );
82
 
83
- if ( $proxy_header = ITSEC_Modules::get_setting( 'security-check-pro', 'remote_ip_index' ) ) {
84
- $proxy['disabled'] = true;
85
- }
86
-
87
- $possible_headers = apply_filters( 'itsec_filter_remote_addr_headers', array(
88
- 'HTTP_CF_CONNECTING_IP', // CloudFlare
89
- 'HTTP_X_FORWARDED_FOR', // Squid and most other forward and reverse proxies
90
- 'REMOTE_ADDR', // Default source of remote IP
91
- ) );
92
-
93
- $ucwords = version_compare( phpversion(), '5.5.16', '>=' ) || ( version_compare( phpversion(), '5.4.32', '>=' ) && version_compare( phpversion(), '5.5.0', '<' ) );
94
- $proxy_header_opt = array();
95
-
96
- foreach ( $possible_headers as $header ) {
97
- $label = $header;
98
 
99
- if ( 0 === strpos( $header, 'HTTP_' ) ) {
100
- $label = substr( $label, 5 );
101
- }
102
-
103
- $label = str_replace( '_', '-', $label );
104
- $label = strtolower( $label );
105
- $label = $ucwords ? ucwords( $label, '-' ) : implode( '-', array_map( 'ucfirst', explode( '-', $label ) ) );
106
-
107
- if ( isset( $_SERVER[ $header ] ) ) {
108
- $label .= ' (' . esc_attr( $_SERVER[ $header ] ) . ')';
109
- }
110
-
111
- $label = str_replace('Ip', 'IP', $label );
112
-
113
- $proxy_header_opt[ $header ] = $label;
114
- }
115
-
116
- ?>
117
- <table class="form-table itsec-settings-section">
118
- <tr>
119
- <th scope="row"><label for="itsec-global-write_files"><?php _e( 'Write to Files', 'better-wp-security' ); ?></label></th>
120
- <td>
121
- <?php $form->add_checkbox( 'write_files' ); ?>
122
- <label for="itsec-global-write_files"><?php _e( 'Allow iThemes Security to write to wp-config.php and .htaccess.', 'better-wp-security' ); ?></label>
123
- <p class="description"><?php _e( 'Whether or not iThemes Security should be allowed to write to wp-config.php and .htaccess automatically. If disabled you will need to manually place configuration options in those files.', 'better-wp-security' ); ?></p>
124
- </td>
125
- </tr>
126
- <tr>
127
- <th scope="row"><label for="itsec-global-lockout_message"><?php _e( 'Host Lockout Message', 'better-wp-security' ); ?></label></th>
128
- <td>
129
- <?php $form->add_textarea( 'lockout_message', array( 'class' => 'widefat' ) ); ?>
130
- <p class="description"><?php _e( 'The message to display when a computer (host) has been locked out.', 'better-wp-security' ); ?></p>
131
- <p class="description"><?php _e( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div.', 'better-wp-security' ); ?></p>
132
- </td>
133
- </tr>
134
- <tr>
135
- <th scope="row"><label for="itsec-global-user_lockout_message"><?php _e( 'User Lockout Message', 'better-wp-security' ); ?></label></th>
136
- <td>
137
- <?php $form->add_textarea( 'user_lockout_message', array( 'class' => 'widefat' ) ); ?>
138
- <p class="description"><?php _e( 'The message to display to a user when their account has been locked out.', 'better-wp-security' ); ?></p>
139
- <p class="description"><?php _e( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div.', 'better-wp-security' ); ?></p>
140
- </td>
141
- </tr>
142
- <tr>
143
- <th scope="row"><label for="itsec-global-community_lockout_message"><?php _e( 'Community Lockout Message', 'better-wp-security' ); ?></label></th>
144
- <td>
145
- <?php $form->add_textarea( 'community_lockout_message', array( 'class' => 'widefat' ) ); ?>
146
- <p class="description"><?php _e( 'The message to display to a user when their IP has been flagged as bad by the iThemes network.', 'better-wp-security' ); ?></p>
147
- <p class="description"><?php _e( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div.', 'better-wp-security' ); ?></p>
148
- </td>
149
- </tr>
150
- <tr>
151
- <th scope="row"><label for="itsec-global-blacklist"><?php _e( 'Blacklist Repeat Offender', 'better-wp-security' ); ?></label></th>
152
- <td>
153
- <?php $form->add_checkbox( 'blacklist' ); ?>
154
- <label for="itsec-global-blacklist"><?php _e( 'Enable Blacklist Repeat Offender', 'better-wp-security' ); ?></label>
155
- <p class="description"><?php _e( 'If this box is checked the IP address of the offending computer will be added to the "Ban Users" blacklist after reaching the number of lockouts listed below.', 'better-wp-security' ); ?></p>
156
- </td>
157
- </tr>
158
- <tr>
159
- <th scope="row"><label for="itsec-global-blacklist_count"><?php _e( 'Blacklist Threshold', 'better-wp-security' ); ?></label></th>
160
- <td>
161
- <?php $form->add_text( 'blacklist_count', array( 'class' => 'small-text' ) ); ?>
162
- <label for="itsec-global-blacklist_count"><?php _e( 'Lockouts', 'better-wp-security' ); ?></label>
163
- <p class="description"><?php _e( 'The number of lockouts per IP before the host is banned permanently from this site.', 'better-wp-security' ); ?></p>
164
- </td>
165
- </tr>
166
- <tr>
167
- <th scope="row"><label for="itsec-global-blacklist_period"><?php _e( 'Blacklist Lookback Period', 'better-wp-security' ); ?></label></th>
168
- <td>
169
- <?php $form->add_text( 'blacklist_period', array( 'class' => 'small-text' ) ); ?>
170
- <label for="itsec-global-blacklist_period"><?php _e( 'Days', 'better-wp-security' ); ?></label>
171
- <p class="description"><?php _e( 'How many days should a lockout be remembered to meet the blacklist count above.', 'better-wp-security' ); ?></p>
172
- </td>
173
- </tr>
174
- <tr>
175
- <th scope="row"><label for="itsec-global-lockout_period"><?php _e( 'Lockout Period', 'better-wp-security' ); ?></label></th>
176
- <td>
177
- <?php $form->add_text( 'lockout_period', array( 'class' => 'small-text' ) ); ?>
178
- <label for="itsec-global-lockout_period"><?php _e( 'Minutes', 'better-wp-security' ); ?></label>
179
- <p class="description"><?php _e( 'The length of time a host or user will be banned from this site after hitting the limit of bad logins. The default setting of 15 minutes is recommended as increasing it could prevent attacking IP addresses from being added to the blacklist.', 'better-wp-security' ); ?></p>
180
- </td>
181
- </tr>
182
- <tr>
183
- <th scope="row"><label for="itsec-global-lockout_white_list"><?php _e( 'Lockout White List', 'better-wp-security' ); ?></label></th>
184
- <td>
185
- <?php $form->add_textarea( 'lockout_white_list' ); ?>
186
- <p><?php $form->add_button( 'add-to-whitelist', array( 'value' => __( 'Add my current IP to the White List', 'better-wp-security' ), 'class' => 'button-primary' ) ); ?></p>
187
- <p class="description"><?php _e( 'Use the guidelines below to enter hosts that will not be locked out from your site. This will keep you from locking yourself out of any features if you should trigger a lockout. Please note this does not override away mode and will only prevent a temporary ban. Should a permanent ban be triggered you will still be added to the "Ban Users" list unless the IP address is also white listed in that section.', 'better-wp-security' ); ?></p>
188
- <ul>
189
- <li>
190
- <?php _e( 'You may white list users by individual IP address or IP address range using wildcards or CIDR notation.', 'better-wp-security' ); ?>
191
- <ul>
192
- <li><?php _e( 'Individual IP addresses must be in IPv4 or IPv6 standard format (###.###.###.### or ####:####:####:####:####:####:####:####).', 'better-wp-security' ); ?></li>
193
- <li><?php _e( 'CIDR notation is allowed to specify a range of IP addresses (###.###.###.###/## or ####:####:####:####:####:####:####:####/###).', 'better-wp-security' ); ?></li>
194
- <li><?php _e( 'Wildcards are also supported with some limitations. If using wildcards (*), you must start with the right-most chunk in the IP address. For example ###.###.###.* and ###.###.*.* are permitted but ###.###.*.### is not. Wildcards are only for convenient entering of IP addresses, and will be automatically converted to their appropriate CIDR notation format on save.', 'better-wp-security' ); ?></li>
195
- </ul>
196
- </li>
197
- <li><?php _e( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ); ?></li>
198
- </ul>
199
- <p><a href="<?php echo esc_url( ITSEC_Lib::get_trace_ip_link() ); ?>" target="_blank" rel="noopener noreferrer"><?php _e( 'Lookup IP Address.', 'better-wp-security' ); ?></a></p>
200
- <p class="description"><strong><?php _e( 'This white list will prevent any IP listed from triggering an automatic lockout. You can still block the IP address manually in the banned users settings.', 'better-wp-security' ); ?></strong></p>
201
- </td>
202
- </tr>
203
- <tr>
204
- <th scope="row"><label for="itsec-global-log_type"><?php _e( 'Log Type', 'better-wp-security' ); ?></label></th>
205
- <td>
206
- <?php $form->add_select( 'log_type', $log_types ); ?>
207
- <label for="itsec-global-log_type"><?php _e( 'How should event logs be kept', 'better-wp-security' ); ?></label>
208
- <p class="description"><?php _e( 'iThemes Security can log events in multiple ways, each with advantages and disadvantages. Database Only puts all events in the database with your posts and other WordPress data. This makes it easy to retrieve and process but can be slower if the database table gets very large. File Only is very fast but the plugin does not process the logs itself as that would take far more resources. For most users or smaller sites Database Only should be fine. If you have a very large site or a log processing software then File Only might be a better option.', 'better-wp-security' ); ?></p>
209
- </td>
210
- </tr>
211
- <tr>
212
- <th scope="row"><label for="itsec-global-log_rotation"><?php _e( 'Days to Keep Database Logs', 'better-wp-security' ); ?></label></th>
213
- <td>
214
- <?php $form->add_text( 'log_rotation', array( 'class' => 'small-text' ) ); ?>
215
- <label for="itsec-global-log_rotation"><?php _e( 'Days', 'better-wp-security' ); ?></label>
216
- <p class="description"><?php _e( 'The number of days database logs should be kept.', 'better-wp-security' ); ?></p>
217
- </td>
218
- </tr>
219
- <tr>
220
- <th scope="row"><label for="itsec-global-file_log_rotation"><?php _e( 'Days to Keep File Logs', 'better-wp-security' ); ?></label></th>
221
- <td>
222
- <?php $form->add_text( 'file_log_rotation', array( 'class' => 'small-text' ) ); ?>
223
- <label for="itsec-global-log_rotation"><?php _e( 'Days', 'better-wp-security' ); ?></label>
224
- <p class="description"><?php _e( 'The number of days file logs should be kept. File logs will additionally be rotated once the file hits 10MB. Set to 0 to only use log rotation.', 'better-wp-security' ); ?></p>
225
- </td>
226
- </tr>
227
- <tr>
228
- <th scope="row"><label for="itsec-global-log_location"><?php _e( 'Path to Log Files', 'better-wp-security' ); ?></label></th>
229
- <td>
230
- <?php $form->add_text( 'log_location', array( 'class' => 'large-text code' ) ); ?>
231
- <p><label for="itsec-global-log_location"><?php _e( 'The path on your server where log files should be stored.', 'better-wp-security' ); ?></label></p>
232
- <p class="description"><?php _e( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ); ?></p>
233
- <p><?php $form->add_button( 'reset-log-location', array( 'value' => __( 'Restore Default Log File Path', 'better-wp-security' ), 'class' => 'button-secondary' ) ); ?></p>
234
- </td>
235
- </tr>
236
- <?php if ( is_dir( WP_PLUGIN_DIR . '/iwp-client' ) ) : ?>
237
  <tr>
238
- <th scope="row"><label for="itsec-global-infinitewp_compatibility"><?php _e( 'Add InfiniteWP Compatibility', 'better-wp-security' ); ?></label></th>
239
  <td>
240
- <?php $form->add_checkbox( 'infinitewp_compatibility' ); ?>
241
- <label for="itsec-global-infinitewp_compatibility"><?php _e( 'Enable InfiniteWP Compatibility', 'better-wp-security' ); ?></label>
242
- <p class="description"><?php printf( __( 'Turning this feature on will enable compatibility with <a href="%s" target="_blank" rel="noopener noreferrer">InfiniteWP</a>. Do not turn it on unless you use the InfiniteWP service.', 'better-wp-security' ), 'http://infinitewp.com' ); ?></p>
243
  </td>
244
  </tr>
245
- <?php endif; ?>
246
- <tr>
247
- <th scope="row"><label for="itsec-global-allow_tracking"><?php _e( 'Allow Data Tracking', 'better-wp-security' ); ?></label></th>
248
- <td>
249
- <?php $form->add_checkbox( 'allow_tracking' ); ?>
250
- <label for="itsec-global-allow_tracking"><?php _e( 'Allow iThemes to track plugin usage via anonymous data.', 'better-wp-security' ); ?></label>
251
- </td>
252
- </tr>
253
- <?php if ( 'nginx' === ITSEC_Lib::get_server() ) : ?>
254
  <tr>
255
- <th scope="row"><label for="itsec-global-nginx_file"><?php _e( 'NGINX Conf File', 'better-wp-security' ); ?></label></th>
256
  <td>
257
- <?php $form->add_text( 'nginx_file', array( 'class' => 'large-text code' ) ); ?>
258
- <p><label for="itsec-global-nginx_file"><?php _e( 'The path on your server where the nginx config file is located.', 'better-wp-security' ); ?></label></p>
259
- <p class="description"><?php _e( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ); ?></p>
260
  </td>
261
  </tr>
262
- <?php endif; ?>
263
- <tr>
264
- <th scope="row"><label for="itsec-global-proxy"><?php esc_html_e( 'Proxy Detection', 'better-wp-security' ); ?></label></th>
265
- <td>
266
- <?php if ( $proxy_header ) : ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  <p class="description">
268
- <?php printf( esc_html__( 'Security Check Pro has automatically determined the correct header, %s.', 'better-wp-security' ), '<code>' . esc_html( is_array( $proxy_header ) ? implode( ', ', $proxy_header ) : $proxy_header ) . '</code>' ); ?>
269
  </p>
270
- <?php else: ?>
271
- <?php $form->add_select( 'proxy', $proxy ); ?>
272
- <?php if ( ITSEC_Core::is_pro() ): ?>
273
- <p class="">
274
- <?php printf(
275
- esc_html__( 'Configure this automatically by running a %1$sSecurity Check%2$s scan.', 'better-wp-security' ),
276
- '<a href="#itsec-security-check-secure_site" data-module-link="security-check">', '</a>'
277
- ); ?>
278
- </p>
279
- <?php endif; ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  <p class="description">
281
- <?php esc_html_e( 'By default, iThemes Security will try to find the correct proxy header to use automatically. However, we highly recommend manually selecting the header your proxy service uses or disabling it completely if your website is not behind a proxy. Otherwise, IP detection might not be accurate, allowing attackers to bypass lockouts.', 'better-wp-security' ) ?>
 
 
 
282
  </p>
283
- <?php endif; ?>
284
- </td>
285
- </tr>
286
- <tr class="itsec-global-proxy_header-container">
287
- <th scope="row"><label for="itsec-global-proxy_header"><?php esc_html_e( 'Proxy Header', 'better-wp-security' ); ?></label></th>
288
- <td>
289
- <?php $form->add_select( 'proxy_header', $proxy_header_opt ); ?>
290
- <p class="description">
291
- <?php printf(
292
- esc_html__( 'Select the header your Proxy Server uses to forward the client IP address. If you don\'t know the header, you can contact your hosting provider or select the header that has your %1$sIP Address%2$s.', 'better-wp-security' ),
293
- '<a href="https://whatismyipaddress.com">', '</a>'
294
- ); ?>
295
- </p>
296
- </td>
297
- </tr>
298
- <tr>
299
- <th scope="row"><label for="itsec-global-hide_admin_bar"><?php _e( 'Hide Security Menu in Admin Bar', 'better-wp-security' ); ?></label></th>
300
- <td>
301
- <?php $form->add_checkbox( 'hide_admin_bar' ); ?>
302
- <label for="itsec-global-hide_admin_bar"><?php _e( 'Hide security menu in admin bar.', 'better-wp-security' ); ?></label>
303
- <p class="description"><?php esc_html_e( 'Remove the Security Messages Menu from the admin bar and receive the messages as traditional WordPress Admin Notices.', 'better-wp-security' ); ?></p>
304
- </td>
305
- </tr>
306
- <tr>
307
- <th scope="row"><label for="itsec-global-show_error_codes"><?php _e( 'Show Error Codes', 'better-wp-security' ); ?></label></th>
308
- <td>
309
- <?php $form->add_select( 'show_error_codes', $show_error_codes_options ); ?>
310
- <p class="description"><?php _e( 'Each error message in iThemes Security has an associated error code that can help diagnose an issue. Changing this setting to "Yes" causes these codes to display. This setting should be left set to "No" unless iThemes Security support requests that you change it.', 'better-wp-security' ); ?></p>
311
- </td>
312
- </tr>
313
- <?php if ( ITSEC_Core::is_pro() ) : ?>
314
  <tr>
315
- <th scope="row"><label for="itsec-global-enable_grade_report"><?php _e( 'Enable Grade Report', 'better-wp-security' ); ?></label></th>
316
  <td>
317
- <?php $form->add_select( 'enable_grade_report', $enable_grade_report_options ); ?>
318
- <p class="description"><?php _e( 'The Grade Report feature can help you identify vulnerabilities on the site. Visit the Notification Center to select which users receive emails from this feature.', 'better-wp-security' ); ?></p>
 
319
  </td>
320
  </tr>
321
- <?php endif; ?>
322
- </table>
323
- <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
  }
326
  }
1
  <?php
2
 
3
  final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
4
+ private $version = 4;
 
5
 
6
  public function __construct() {
7
+ $this->id = 'global';
8
+ $this->title = __( 'Global Settings', 'better-wp-security' );
9
  $this->description = __( 'Configure basic settings that control how iThemes Security functions.', 'better-wp-security' );
10
+ $this->type = 'recommended';
11
 
12
  parent::__construct();
13
 
34
  $vars = array(
35
  'ip' => ITSEC_Lib::get_ip(),
36
  'log_location' => ITSEC_Modules::get_default( $this->id, 'log_location' ),
37
+ 'l10n' => array(
38
+ 'loading' => esc_html__( 'Loading...', 'better-wp-security' ),
39
+ )
40
  );
41
 
42
+ wp_enqueue_script( 'itsec-global-settings-page-script', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery', 'itsec-settings-page-script', 'itsec-util' ), $this->version, true );
43
  wp_localize_script( 'itsec-global-settings-page-script', 'itsec_global_settings_page', $vars );
44
+ wp_enqueue_style( 'itsec-global-settings', plugins_url( 'css/settings-page.css', __FILE__ ), array(), $this->version );
45
  }
46
 
47
  public function handle_form_post( $data ) {
58
  }
59
  }
60
 
61
+ public function handle_ajax_request( $data ) {
62
+ $method = empty( $data['method'] ) ? '' : $data['method'];
63
+
64
+ switch ( $method ) {
65
+ case 'get-ip':
66
+ ITSEC_Lib::load( 'ip-detector' );
67
+ $detector = ITSEC_Lib_IP_Detector::build_for_type( $data['proxy'], isset( $data['args'] ) ? $data['args'] : array() );
68
+ $ip = $detector->get();
69
+
70
+ return array(
71
+ 'ip' => $ip,
72
+ 'ip_l10n' => sprintf( __( 'Detected IP: %s', 'better-wp-security' ), $ip ),
73
+ );
74
+ case 'scan-ip':
75
+ if ( ! ITSEC_Modules::is_active( 'security-check-pro' ) ) {
76
+ ITSEC_Modules::activate( 'security-check-pro' );
77
+ ITSEC_Modules::load_module_file( 'active.php', 'security-check-pro' );
78
+ }
79
+
80
+ ITSEC_Modules::load_module_file( 'feedback.php', 'security-check' );
81
+ ITSEC_Modules::load_module_file( 'utility.php', 'security-check-pro' );
82
+ $scan = ITSEC_Security_Check_Pro_Utility::get_server_response();
83
+
84
+ if ( is_wp_error( $scan ) ) {
85
+ ITSEC_Response::add_error( $scan );
86
+ } elseif ( empty( $scan['remote_ip'] ) ) {
87
+ ITSEC_Response::add_error( __( 'Could not detect IP header.', 'better-wp-security' ) );
88
+ } else {
89
+ ITSEC_Lib::load( 'ip-detector' );
90
+ $detector = ITSEC_Lib_IP_Detector::build_for_type( 'security-check' );
91
+ $ip = $detector->get();
92
+
93
+ if ( ! $ip ) {
94
+ ITSEC_Response::add_error( __( 'Identified IP was invalid.', 'better-wp-security' ) );
95
+
96
+ return null;
97
+ }
98
+
99
+ return array(
100
+ 'ip' => $ip,
101
+ 'ip_l10n' => sprintf( __( 'Detected IP: %s', 'better-wp-security' ), $ip ),
102
+ );
103
+ }
104
+
105
+ return null;
106
+ default:
107
+ return null;
108
+ }
109
+ }
110
 
111
+ protected function render_description( $form ) {
112
+ ?>
113
+ <p><?php _e( 'The following settings modify the behavior of many of the features offered by iThemes Security.', 'better-wp-security' ); ?></p>
114
+ <?php
115
  }
116
 
117
  protected function render_settings( $form ) {
118
+ /** @var ITSEC_Global_Validator $validator */
119
  $validator = ITSEC_Modules::get_validator( $this->id );
120
 
121
  $log_types = $validator->get_valid_log_types();
132
 
133
  $proxy = array( 'value' => $validator->get_proxy_types() );
134
 
135
+ $proxy_header_opt = $validator->get_proxy_header_options();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ ?>
138
+ <table class="form-table itsec-settings-section">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  <tr>
140
+ <th scope="row"><label for="itsec-global-write_files"><?php _e( 'Write to Files', 'better-wp-security' ); ?></label></th>
141
  <td>
142
+ <?php $form->add_checkbox( 'write_files' ); ?>
143
+ <label for="itsec-global-write_files"><?php _e( 'Allow iThemes Security to write to wp-config.php and .htaccess.', 'better-wp-security' ); ?></label>
144
+ <p class="description"><?php _e( 'Whether or not iThemes Security should be allowed to write to wp-config.php and .htaccess automatically. If disabled you will need to manually place configuration options in those files.', 'better-wp-security' ); ?></p>
145
  </td>
146
  </tr>
 
 
 
 
 
 
 
 
 
147
  <tr>
148
+ <th scope="row"><label for="itsec-global-lockout_message"><?php _e( 'Host Lockout Message', 'better-wp-security' ); ?></label></th>
149
  <td>
150
+ <?php $form->add_textarea( 'lockout_message', array( 'class' => 'widefat' ) ); ?>
151
+ <p class="description"><?php _e( 'The message to display when a computer (host) has been locked out.', 'better-wp-security' ); ?></p>
152
+ <p class="description"><?php _e( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div.', 'better-wp-security' ); ?></p>
153
  </td>
154
  </tr>
155
+ <tr>
156
+ <th scope="row"><label for="itsec-global-user_lockout_message"><?php _e( 'User Lockout Message', 'better-wp-security' ); ?></label></th>
157
+ <td>
158
+ <?php $form->add_textarea( 'user_lockout_message', array( 'class' => 'widefat' ) ); ?>
159
+ <p class="description"><?php _e( 'The message to display to a user when their account has been locked out.', 'better-wp-security' ); ?></p>
160
+ <p class="description"><?php _e( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div.', 'better-wp-security' ); ?></p>
161
+ </td>
162
+ </tr>
163
+ <tr>
164
+ <th scope="row"><label for="itsec-global-community_lockout_message"><?php _e( 'Community Lockout Message', 'better-wp-security' ); ?></label></th>
165
+ <td>
166
+ <?php $form->add_textarea( 'community_lockout_message', array( 'class' => 'widefat' ) ); ?>
167
+ <p class="description"><?php _e( 'The message to display to a user when their IP has been flagged as bad by the iThemes network.', 'better-wp-security' ); ?></p>
168
+ <p class="description"><?php _e( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div.', 'better-wp-security' ); ?></p>
169
+ </td>
170
+ </tr>
171
+ <tr>
172
+ <th scope="row"><label for="itsec-global-blacklist"><?php _e( 'Blacklist Repeat Offender', 'better-wp-security' ); ?></label></th>
173
+ <td>
174
+ <?php $form->add_checkbox( 'blacklist' ); ?>
175
+ <label for="itsec-global-blacklist"><?php _e( 'Enable Blacklist Repeat Offender', 'better-wp-security' ); ?></label>
176
+ <p class="description"><?php _e( 'If this box is checked the IP address of the offending computer will be added to the "Ban Users" blacklist after reaching the number of lockouts listed below.', 'better-wp-security' ); ?></p>
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <th scope="row"><label for="itsec-global-blacklist_count"><?php _e( 'Blacklist Threshold', 'better-wp-security' ); ?></label></th>
181
+ <td>
182
+ <?php $form->add_text( 'blacklist_count', array( 'class' => 'small-text' ) ); ?>
183
+ <label for="itsec-global-blacklist_count"><?php _e( 'Lockouts', 'better-wp-security' ); ?></label>
184
+ <p class="description"><?php _e( 'The number of lockouts per IP before the host is banned permanently from this site.', 'better-wp-security' ); ?></p>
185
+ </td>
186
+ </tr>
187
+ <tr>
188
+ <th scope="row"><label for="itsec-global-blacklist_period"><?php _e( 'Blacklist Lookback Period', 'better-wp-security' ); ?></label></th>
189
+ <td>
190
+ <?php $form->add_text( 'blacklist_period', array( 'class' => 'small-text' ) ); ?>
191
+ <label for="itsec-global-blacklist_period"><?php _e( 'Days', 'better-wp-security' ); ?></label>
192
+ <p class="description"><?php _e( 'How many days should a lockout be remembered to meet the blacklist count above.', 'better-wp-security' ); ?></p>
193
+ </td>
194
+ </tr>
195
+ <tr>
196
+ <th scope="row"><label for="itsec-global-lockout_period"><?php _e( 'Lockout Period', 'better-wp-security' ); ?></label></th>
197
+ <td>
198
+ <?php $form->add_text( 'lockout_period', array( 'class' => 'small-text' ) ); ?>
199
+ <label for="itsec-global-lockout_period"><?php _e( 'Minutes', 'better-wp-security' ); ?></label>
200
+ <p class="description"><?php _e( 'The length of time a host or user will be banned from this site after hitting the limit of bad logins. The default setting of 15 minutes is recommended as increasing it could prevent attacking IP addresses from being added to the blacklist.', 'better-wp-security' ); ?></p>
201
+ </td>
202
+ </tr>
203
+ <tr>
204
+ <th scope="row"><label for="itsec-global-lockout_white_list"><?php _e( 'Lockout White List', 'better-wp-security' ); ?></label></th>
205
+ <td>
206
+ <?php $form->add_textarea( 'lockout_white_list' ); ?>
207
+ <p><?php $form->add_button( 'add-to-whitelist', array( 'value' => __( 'Add my current IP to the White List', 'better-wp-security' ), 'class' => 'button-primary' ) ); ?></p>
208
+ <p class="description"><?php _e( 'Use the guidelines below to enter hosts that will not be locked out from your site. This will keep you from locking yourself out of any features if you should trigger a lockout. Please note this does not override away mode and will only prevent a temporary ban. Should a permanent ban be triggered you will still be added to the "Ban Users" list unless the IP address is also white listed in that section.', 'better-wp-security' ); ?></p>
209
+ <ul>
210
+ <li>
211
+ <?php _e( 'You may white list users by individual IP address or IP address range using wildcards or CIDR notation.', 'better-wp-security' ); ?>
212
+ <ul>
213
+ <li><?php _e( 'Individual IP addresses must be in IPv4 or IPv6 standard format (###.###.###.### or ####:####:####:####:####:####:####:####).', 'better-wp-security' ); ?></li>
214
+ <li><?php _e( 'CIDR notation is allowed to specify a range of IP addresses (###.###.###.###/## or ####:####:####:####:####:####:####:####/###).', 'better-wp-security' ); ?></li>
215
+ <li><?php _e( 'Wildcards are also supported with some limitations. If using wildcards (*), you must start with the right-most chunk in the IP address. For example ###.###.###.* and ###.###.*.* are permitted but ###.###.*.### is not. Wildcards are only for convenient entering of IP addresses, and will be automatically converted to their appropriate CIDR notation format on save.', 'better-wp-security' ); ?></li>
216
+ </ul>
217
+ </li>
218
+ <li><?php _e( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ); ?></li>
219
+ </ul>
220
+ <p><a href="<?php echo esc_url( ITSEC_Lib::get_trace_ip_link() ); ?>" target="_blank" rel="noopener noreferrer"><?php _e( 'Lookup IP Address.', 'better-wp-security' ); ?></a></p>
221
  <p class="description">
222
+ <strong><?php _e( 'This white list will prevent any IP listed from triggering an automatic lockout. You can still block the IP address manually in the banned users settings.', 'better-wp-security' ); ?></strong>
223
  </p>
224
+ </td>
225
+ </tr>
226
+ <tr>
227
+ <th scope="row"><label for="itsec-global-log_type"><?php _e( 'Log Type', 'better-wp-security' ); ?></label></th>
228
+ <td>
229
+ <?php $form->add_select( 'log_type', $log_types ); ?>
230
+ <label for="itsec-global-log_type"><?php _e( 'How should event logs be kept', 'better-wp-security' ); ?></label>
231
+ <p class="description"><?php _e( 'iThemes Security can log events in multiple ways, each with advantages and disadvantages. Database Only puts all events in the database with your posts and other WordPress data. This makes it easy to retrieve and process but can be slower if the database table gets very large. File Only is very fast but the plugin does not process the logs itself as that would take far more resources. For most users or smaller sites Database Only should be fine. If you have a very large site or a log processing software then File Only might be a better option.', 'better-wp-security' ); ?></p>
232
+ </td>
233
+ </tr>
234
+ <tr>
235
+ <th scope="row"><label for="itsec-global-log_rotation"><?php _e( 'Days to Keep Database Logs', 'better-wp-security' ); ?></label></th>
236
+ <td>
237
+ <?php $form->add_text( 'log_rotation', array( 'class' => 'small-text' ) ); ?>
238
+ <label for="itsec-global-log_rotation"><?php _e( 'Days', 'better-wp-security' ); ?></label>
239
+ <p class="description"><?php _e( 'The number of days database logs should be kept.', 'better-wp-security' ); ?></p>
240
+ </td>
241
+ </tr>
242
+ <tr>
243
+ <th scope="row"><label for="itsec-global-file_log_rotation"><?php _e( 'Days to Keep File Logs', 'better-wp-security' ); ?></label></th>
244
+ <td>
245
+ <?php $form->add_text( 'file_log_rotation', array( 'class' => 'small-text' ) ); ?>
246
+ <label for="itsec-global-log_rotation"><?php _e( 'Days', 'better-wp-security' ); ?></label>
247
+ <p class="description"><?php _e( 'The number of days file logs should be kept. File logs will additionally be rotated once the file hits 10MB. Set to 0 to only use log rotation.', 'better-wp-security' ); ?></p>
248
+ </td>
249
+ </tr>
250
+ <tr>
251
+ <th scope="row"><label for="itsec-global-log_location"><?php _e( 'Path to Log Files', 'better-wp-security' ); ?></label></th>
252
+ <td>
253
+ <?php $form->add_text( 'log_location', array( 'class' => 'large-text code' ) ); ?>
254
+ <p><label for="itsec-global-log_location"><?php _e( 'The path on your server where log files should be stored.', 'better-wp-security' ); ?></label></p>
255
+ <p class="description"><?php _e( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ); ?></p>
256
+ <p><?php $form->add_button( 'reset-log-location', array( 'value' => __( 'Restore Default Log File Path', 'better-wp-security' ), 'class' => 'button-secondary' ) ); ?></p>
257
+ </td>
258
+ </tr>
259
+ <?php if ( is_dir( WP_PLUGIN_DIR . '/iwp-client' ) ) : ?>
260
+ <tr>
261
+ <th scope="row"><label for="itsec-global-infinitewp_compatibility"><?php _e( 'Add InfiniteWP Compatibility', 'better-wp-security' ); ?></label></th>
262
+ <td>
263
+ <?php $form->add_checkbox( 'infinitewp_compatibility' ); ?>
264
+ <label for="itsec-global-infinitewp_compatibility"><?php _e( 'Enable InfiniteWP Compatibility', 'better-wp-security' ); ?></label>
265
+ <p class="description"><?php printf( __( 'Turning this feature on will enable compatibility with <a href="%s" target="_blank" rel="noopener noreferrer">InfiniteWP</a>. Do not turn it on unless you use the InfiniteWP service.', 'better-wp-security' ), 'http://infinitewp.com' ); ?></p>
266
+ </td>
267
+ </tr>
268
+ <?php endif; ?>
269
+ <tr>
270
+ <th scope="row"><label for="itsec-global-allow_tracking"><?php _e( 'Allow Data Tracking', 'better-wp-security' ); ?></label></th>
271
+ <td>
272
+ <?php $form->add_checkbox( 'allow_tracking' ); ?>
273
+ <label for="itsec-global-allow_tracking"><?php _e( 'Allow iThemes to track plugin usage via anonymous data.', 'better-wp-security' ); ?></label>
274
+ </td>
275
+ </tr>
276
+ <?php if ( 'nginx' === ITSEC_Lib::get_server() ) : ?>
277
+ <tr>
278
+ <th scope="row"><label for="itsec-global-nginx_file"><?php _e( 'NGINX Conf File', 'better-wp-security' ); ?></label></th>
279
+ <td>
280
+ <?php $form->add_text( 'nginx_file', array( 'class' => 'large-text code' ) ); ?>
281
+ <p><label for="itsec-global-nginx_file"><?php _e( 'The path on your server where the nginx config file is located.', 'better-wp-security' ); ?></label></p>
282
+ <p class="description"><?php _e( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ); ?></p>
283
+ </td>
284
+ </tr>
285
+ <?php endif; ?>
286
+ <tr>
287
+ <th scope="row"><label for="itsec-global-proxy"><?php esc_html_e( 'Proxy Detection', 'better-wp-security' ); ?></label></th>
288
+ <td>
289
+ <div class="itsec-global-proxy-wrapper">
290
+ <?php $form->add_select( 'proxy', $proxy ); ?>
291
+ <?php $form->add_button( 'ip-scan', array( 'value' => esc_html__( 'Run Security Check Scan Now', 'better-wp-security' ), 'class' => 'button' ) ); ?>
292
+ </div>
293
+ <p class="itsec-global-detected-ip"><?php printf( __( 'Detected IP: %s', 'better-wp-security' ), ITSEC_Lib::get_ip() ) ?></p>
294
+ <p class="description"><?php esc_html_e( 'Choose how iThemes Security determines your visitor\'s IP addresses. Incorrectly configuring this setting may lead to attackers bypassing lockouts or bans.', 'better-wp-security' ); ?></p>
295
+ <ul>
296
+ <?php if ( isset( $proxy['value']['security-check'] ) ): ?>
297
+ <li>
298
+ <?php printf(
299
+ esc_html__( 'Security Check Scan – (Recommended) Security Check will connect to the iThemes.com servers to accurately identify your server configuration. %1$sRead our Privacy Policy%2$s. Security Check will correctly identify remote IP addresses and ensure your site is using the recommended features.', 'better-wp-security' ),
300
+ '<a href="https://ithemes.com/privacy-policy/" target="_blank">',
301
+ '</a>'
302
+ ); ?>
303
+ </li>
304
+ <?php endif; ?>
305
+ <li><?php esc_html_e( 'Automatic – (Not Recommended) iThemes Security will try to find the correct proxy header to use automatically.', 'better-wp-security' ); ?></li>
306
+ <li><?php esc_html_e( 'Manual – Manually select the header your proxy uses.', 'better-wp-security' ); ?></li>
307
+ <li><?php esc_html_e( 'Disabled – Do not use Proxy Detection if your website isn\'t behind a proxy.', 'better-wp-security' ); ?></li>
308
+ </ul>
309
+ </td>
310
+ </tr>
311
+ <tr class="itsec-global-proxy_header-container">
312
+ <th scope="row"><label for="itsec-global-proxy_header"><?php esc_html_e( 'Proxy Header', 'better-wp-security' ); ?></label></th>
313
+ <td>
314
+ <?php $form->add_select( 'proxy_header', $proxy_header_opt ); ?>
315
  <p class="description">
316
+ <?php printf(
317
+ esc_html__( 'Select the header your Proxy Server uses to forward the client IP address. If you don\'t know the header, you can contact your hosting provider or select the header that has your %1$sIP Address%2$s.', 'better-wp-security' ),
318
+ '<a href="https://whatismyipaddress.com">', '</a>'
319
+ ); ?>
320
  </p>
321
+ </td>
322
+ </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  <tr>
324
+ <th scope="row"><label for="itsec-global-hide_admin_bar"><?php _e( 'Hide Security Menu in Admin Bar', 'better-wp-security' ); ?></label></th>
325
  <td>
326
+ <?php $form->add_checkbox( 'hide_admin_bar' ); ?>
327
+ <label for="itsec-global-hide_admin_bar"><?php _e( 'Hide security menu in admin bar.', 'better-wp-security' ); ?></label>
328
+ <p class="description"><?php esc_html_e( 'Remove the Security Messages Menu from the admin bar and receive the messages as traditional WordPress Admin Notices.', 'better-wp-security' ); ?></p>
329
  </td>
330
  </tr>
331
+ <tr>
332
+ <th scope="row"><label for="itsec-global-show_error_codes"><?php _e( 'Show Error Codes', 'better-wp-security' ); ?></label></th>
333
+ <td>
334
+ <?php $form->add_select( 'show_error_codes', $show_error_codes_options ); ?>
335
+ <p class="description"><?php _e( 'Each error message in iThemes Security has an associated error code that can help diagnose an issue. Changing this setting to "Yes" causes these codes to display. This setting should be left set to "No" unless iThemes Security support requests that you change it.', 'better-wp-security' ); ?></p>
336
+ </td>
337
+ </tr>
338
+ <?php if ( ITSEC_Core::is_pro() ) : ?>
339
+ <tr>
340
+ <th scope="row"><label for="itsec-global-enable_grade_report"><?php _e( 'Enable Grade Report', 'better-wp-security' ); ?></label></th>
341
+ <td>
342
+ <?php $form->add_select( 'enable_grade_report', $enable_grade_report_options ); ?>
343
+ <p class="description"><?php _e( 'The Grade Report feature can help you identify vulnerabilities on the site. Visit the Notification Center to select which users receive emails from this feature.', 'better-wp-security' ); ?></p>
344
+ </td>
345
+ </tr>
346
+ <?php endif; ?>
347
+ </table>
348
+ <?php
349
 
350
  }
351
  }
core/modules/global/setup.php CHANGED
@@ -6,10 +6,10 @@ if ( ! class_exists( 'ITSEC_Global_Setup' ) ) {
6
 
7
  public function __construct() {
8
 
9
- add_action( 'itsec_modules_do_plugin_activation', array( $this, 'execute_activate' ) );
10
- add_action( 'itsec_modules_do_plugin_deactivation', array( $this, 'execute_deactivate' ) );
11
- add_action( 'itsec_modules_do_plugin_uninstall', array( $this, 'execute_uninstall' ) );
12
- add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ), null, 2 );
13
 
14
  }
15
 
@@ -50,8 +50,8 @@ if ( ! class_exists( 'ITSEC_Global_Setup' ) ) {
50
 
51
  if ( $options['log_info'] ) {
52
  $new_log_info = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
53
- $old_file = path_join( $options['log_location'], 'event-log-' . $options['log_info'] . '.log' );
54
- $new_file = path_join( $options['log_location'], 'event-log-' . $new_log_info . '.log' );
55
 
56
  // If the file exists already, don't update the location unless we successfully move it.
57
  if ( file_exists( $old_file ) && rename( $old_file, $new_file ) ) {
@@ -128,8 +128,13 @@ if ( ! class_exists( 'ITSEC_Global_Setup' ) ) {
128
  ITSEC_Modules::set_setting( 'global', 'proxy', 'disabled' );
129
  }
130
  }
131
- }
132
 
 
 
 
 
 
 
133
  }
134
 
135
  }
6
 
7
  public function __construct() {
8
 
9
+ add_action( 'itsec_modules_do_plugin_activation', array( $this, 'execute_activate' ) );
10
+ add_action( 'itsec_modules_do_plugin_deactivation', array( $this, 'execute_deactivate' ) );
11
+ add_action( 'itsec_modules_do_plugin_uninstall', array( $this, 'execute_uninstall' ) );
12
+ add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ), null, 2 );
13
 
14
  }
15
 
50
 
51
  if ( $options['log_info'] ) {
52
  $new_log_info = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
53
+ $old_file = path_join( $options['log_location'], 'event-log-' . $options['log_info'] . '.log' );
54
+ $new_file = path_join( $options['log_location'], 'event-log-' . $new_log_info . '.log' );
55
 
56
  // If the file exists already, don't update the location unless we successfully move it.
57
  if ( file_exists( $old_file ) && rename( $old_file, $new_file ) ) {
128
  ITSEC_Modules::set_setting( 'global', 'proxy', 'disabled' );
129
  }
130
  }
 
131
 
132
+ if ( $itsec_old_version < 4116 ) {
133
+ if ( ITSEC_Core::is_pro() && ITSEC_Modules::get_setting( 'security-check-pro', 'remote_ip_index' ) ) {
134
+ ITSEC_Modules::set_setting( 'global', 'proxy', 'security-check' );
135
+ }
136
+ }
137
+ }
138
  }
139
 
140
  }
core/modules/global/validator.php CHANGED
@@ -22,8 +22,7 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
22
  $this->vars_to_skip_validate_matching_fields = array( 'digest_last_sent', 'digest_messages', 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'show_new_dashboard_notice', 'proxy_override', 'proxy', 'proxy_header', 'server_ips', 'initial_build', 'feature_flags' );
23
  $this->set_previous_if_empty( array( 'did_upgrade', 'log_info', 'show_security_check', 'build', 'activation_timestamp', 'lock_file', 'cron_status', 'use_cron', 'cron_test_time', 'proxy', 'proxy_header', 'server_ips', 'initial_build', 'feature_flags' ) );
24
  $this->set_default_if_empty( array( 'log_location', 'nginx_file', 'enable_grade_report' ) );
25
- $this->preserve_setting_if_exists( array( 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'proxy_override' ) );
26
-
27
 
28
  $this->sanitize_setting( 'bool', 'write_files', __( 'Write to Files', 'better-wp-security' ) );
29
  $this->sanitize_setting( 'bool', 'blacklist', __( 'Blacklist Repeat Offender', 'better-wp-security' ) );
@@ -55,8 +54,8 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
55
 
56
  $allowed_tags = $this->get_allowed_tags();
57
 
58
- $this->settings['lockout_message'] = trim( wp_kses( $this->settings['lockout_message'], $allowed_tags ) );
59
- $this->settings['user_lockout_message'] = trim( wp_kses( $this->settings['user_lockout_message'], $allowed_tags ) );
60
  $this->settings['community_lockout_message'] = trim( wp_kses( $this->settings['community_lockout_message'], $allowed_tags ) );
61
 
62
  $this->sanitize_setting( 'newline-separated-ips', 'server_ips', __( 'Server IPs', 'better-wp-security' ) );
@@ -64,11 +63,36 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
64
  }
65
 
66
  public function get_proxy_types() {
67
- return array(
68
- 'automatic' => esc_html__( 'Automatic', 'better-wp-security' ),
69
- 'manual' => esc_html__( 'Manual', 'better-wp-security' ),
70
- 'disabled' => esc_html__( 'Disabled', 'better-wp-security' ),
71
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
 
74
  public function get_valid_log_types() {
22
  $this->vars_to_skip_validate_matching_fields = array( 'digest_last_sent', 'digest_messages', 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'show_new_dashboard_notice', 'proxy_override', 'proxy', 'proxy_header', 'server_ips', 'initial_build', 'feature_flags' );
23
  $this->set_previous_if_empty( array( 'did_upgrade', 'log_info', 'show_security_check', 'build', 'activation_timestamp', 'lock_file', 'cron_status', 'use_cron', 'cron_test_time', 'proxy', 'proxy_header', 'server_ips', 'initial_build', 'feature_flags' ) );
24
  $this->set_default_if_empty( array( 'log_location', 'nginx_file', 'enable_grade_report' ) );
25
+ $this->preserve_setting_if_exists( array( 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'proxy_override' ) );
 
26
 
27
  $this->sanitize_setting( 'bool', 'write_files', __( 'Write to Files', 'better-wp-security' ) );
28
  $this->sanitize_setting( 'bool', 'blacklist', __( 'Blacklist Repeat Offender', 'better-wp-security' ) );
54
 
55
  $allowed_tags = $this->get_allowed_tags();
56
 
57
+ $this->settings['lockout_message'] = trim( wp_kses( $this->settings['lockout_message'], $allowed_tags ) );
58
+ $this->settings['user_lockout_message'] = trim( wp_kses( $this->settings['user_lockout_message'], $allowed_tags ) );
59
  $this->settings['community_lockout_message'] = trim( wp_kses( $this->settings['community_lockout_message'], $allowed_tags ) );
60
 
61
  $this->sanitize_setting( 'newline-separated-ips', 'server_ips', __( 'Server IPs', 'better-wp-security' ) );
63
  }
64
 
65
  public function get_proxy_types() {
66
+ ITSEC_Lib::load( 'ip-detector' );
67
+
68
+ return ITSEC_Lib_IP_Detector::get_proxy_types();
69
+ }
70
+
71
+ public function get_proxy_header_options() {
72
+ ITSEC_Lib::load( 'ip-detector' );
73
+
74
+ $possible_headers = ITSEC_Lib_IP_Detector::get_proxy_headers();
75
+ $possible_headers[] = 'REMOTE_ADDR';
76
+
77
+ $ucwords = version_compare( phpversion(), '5.5.16', '>=' ) || ( version_compare( phpversion(), '5.4.32', '>=' ) && version_compare( phpversion(), '5.5.0', '<' ) );
78
+ $options = array();
79
+
80
+ foreach ( $possible_headers as $header ) {
81
+ $label = $header;
82
+
83
+ if ( 0 === strpos( $header, 'HTTP_' ) ) {
84
+ $label = substr( $label, 5 );
85
+ }
86
+
87
+ $label = str_replace( '_', '-', $label );
88
+ $label = strtolower( $label );
89
+ $label = $ucwords ? ucwords( $label, '-' ) : implode( '-', array_map( 'ucfirst', explode( '-', $label ) ) );
90
+ $label = str_replace('Ip', 'IP', $label );
91
+
92
+ $options[ $header ] = $label;
93
+ }
94
+
95
+ return $options;
96
  }
97
 
98
  public function get_valid_log_types() {
core/modules/security-check-pro/activate.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ ITSEC_Response::reload_module( 'security-check' );
3
+ ITSEC_Response::reload_module( 'global' );
core/modules/security-check-pro/active.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once( __DIR__ . '/class-itsec-security-check-pro.php' );
4
+ $itsec_security_check_pro = new ITSEC_Security_Check_Pro();
5
+ $itsec_security_check_pro->run();
core/modules/security-check-pro/class-itsec-security-check-pro.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Security_Check_Pro {
4
+ public function __construct() { }
5
+
6
+ public function run() {
7
+ if ( defined( 'ITSEC_DISABLE_SECURITY_CHECK_PRO' ) && ITSEC_DISABLE_SECURITY_CHECK_PRO ) {
8
+ return;
9
+ }
10
+
11
+ if ( isset( $_POST['itsec-security-check'] ) ) {
12
+ require_once( dirname( __FILE__ ) . '/utility.php' );
13
+
14
+ ITSEC_Security_Check_Pro_Utility::handle_scan_request();
15
+ }
16
+
17
+ add_action( 'itsec-security-check-before-default-checks', array( $this, 'run_scan' ) );
18
+ add_action( 'itsec-security-check-enable-ssl', array( $this, 'handle_enable_ssl' ) );
19
+
20
+ add_filter( 'itsec-ssl-support-probability', array( $this, 'filter_ssl_support_probability' ) );
21
+
22
+ if ( ! defined( 'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION' ) || ! ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION ) {
23
+ add_filter( 'itsec_proxy_types', array( $this, 'add_security_check_proxy_type' ) );
24
+ add_filter( 'itsec_build_ip_detector_for_security-check', array( $this, 'build_detector' ), 10, 2 );
25
+ add_action( 'itsec_scheduled_health-check', array( $this, 'health_check' ) );
26
+ }
27
+ }
28
+
29
+ public function run_scan( $feedback ) {
30
+ require_once( dirname( __FILE__ ) . '/utility.php' );
31
+
32
+ ITSEC_Security_Check_Pro_Utility::run_scan( $feedback );
33
+ }
34
+
35
+ public function handle_enable_ssl( $data ) {
36
+ require_once( dirname( __FILE__ ) . '/utility.php' );
37
+
38
+ ITSEC_Security_Check_Pro_Utility::handle_enable_ssl( $data );
39
+ }
40
+
41
+ public function filter_ssl_support_probability( $probability ) {
42
+ if ( ITSEC_Modules::get_setting( 'security-check-pro', 'ssl_supported' ) ) {
43
+ $probability = 100;
44
+ }
45
+
46
+ return $probability;
47
+ }
48
+
49
+ public function add_security_check_proxy_type( $proxy_types ) {
50
+ return ITSEC_Lib::array_insert_before( 'automatic', $proxy_types, 'security-check', esc_html__( 'Security Check Scan', 'better-wp-security' ) );
51
+ }
52
+
53
+ /**
54
+ * Add the remote IP index to the detector.
55
+ *
56
+ * @param bool $configured
57
+ * @param ITSEC_IP_Detector $detector
58
+ *
59
+ * @return bool
60
+ */
61
+ public function build_detector( $configured, ITSEC_IP_Detector $detector ) {
62
+ $index = ITSEC_Modules::get_setting( 'security-check-pro', 'remote_ip_index' );
63
+
64
+ if ( ! $index ) {
65
+ return $configured;
66
+ }
67
+
68
+ if ( is_string( $index ) ) {
69
+ $detector->add_header( $index );
70
+ } elseif ( is_array( $index ) && 2 === count( $index ) ) {
71
+ $detector->add_header( $index[0], (int) $index[1] );
72
+ }
73
+
74
+ return true;
75
+ }
76
+
77
+ public function health_check() {
78
+ ITSEC_Modules::load_module_file( 'feedback.php', 'security-check' );
79
+ require_once( __DIR__ . '/utility.php' );
80
+ ITSEC_Security_Check_Pro_Utility::get_server_response();
81
+ }
82
+ }
core/modules/security-check-pro/deactivate.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ ITSEC_Modules::set_setting( 'global', 'proxy', 'automatic' );
4
+ ITSEC_Response::reload_module( 'security-check' );
5
+ ITSEC_Response::reload_module( 'global' );
core/modules/security-check-pro/debug.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class ITSEC_Security_Check_Pro_Debug
5
+ */
6
+ class ITSEC_Security_Check_Pro_Debug {
7
+
8
+ public function __construct() {
9
+ add_action( 'itsec_debug_page', array( $this, 'render' ) );
10
+ add_action( 'itsec_debug_page_enqueue', array( $this, 'enqueue_scripts_and_styles' ) );
11
+ add_action( 'itsec_debug_module_request_security-check-pro', array( $this, 'handle_ajax_request' ) );
12
+ }
13
+
14
+ public function enqueue_scripts_and_styles() {
15
+ wp_enqueue_script( 'itsec-security-check-pro-debug', plugins_url( 'js/debug.js', __FILE__ ), array( 'itsec-util' ), ITSEC_Core::get_plugin_build() );
16
+ }
17
+
18
+ public function handle_ajax_request( $data ) {
19
+
20
+ ITSEC_Modules::load_module_file( 'feedback.php', 'security-check' );
21
+ ITSEC_Modules::load_module_file( 'scanner.php', 'security-check' );
22
+
23
+ require_once( dirname( __FILE__ ) . '/utility.php' );
24
+
25
+ $feedback = new ITSEC_Security_Check_Feedback();
26
+ $response = ITSEC_Security_Check_Pro_Utility::run_scan( $feedback );
27
+
28
+ $raw_data = $feedback->get_raw_data();
29
+
30
+ if ( is_wp_error( $response ) ) {
31
+ ITSEC_Response::add_error( $response );
32
+ } else {
33
+ ITSEC_Response::add_message( esc_html__( 'Scan Complete', 'better-wp-security' ) );
34
+ }
35
+
36
+ foreach ( $raw_data['sections'] as $id => $section ) {
37
+ foreach ( $section['entries'] as $entry ) {
38
+ if ( 'text' === $entry['type'] ) {
39
+ ITSEC_Response::add_info( $entry['value'] );
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Render our data to the Debug Page.
47
+ */
48
+ public function render() {
49
+ ?>
50
+
51
+ <div>
52
+ <h2><?php esc_html_e( 'Security Check Pro', 'better-wp-security' ); ?></h2>
53
+ <button class="button" id="itsec-debug-run-security-check-pro"><?php esc_html_e( 'Run', 'better-wp-security' ); ?></button>
54
+ </div>
55
+
56
+ <?php
57
+ }
58
+ }
59
+
60
+ new ITSEC_Security_Check_Pro_Debug();
core/modules/security-check-pro/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/security-check-pro/js/debug.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ( $, itsecUtil ) {
2
+ "use strict";
3
+
4
+ $( function () {
5
+ $( '#itsec-debug-run-security-check-pro' ).on( 'click', function () {
6
+
7
+ var $btn = $( this );
8
+ $btn.prop( 'disabled', true );
9
+
10
+ itsecUtil.sendModuleAJAXRequest( 'security-check-pro', { run: true }, function ( response ) {
11
+
12
+ $btn.prop( 'disabled', false );
13
+
14
+ itsecUtil.displayNotices( response, $( '#itsec-messages' ) );
15
+ } );
16
+ } );
17
+ } );
18
+ })( jQuery, window.itsecUtil );
core/modules/security-check-pro/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/security-check-pro/js/settings-page.js ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function ( $ ) {
2
+
3
+ $( document ).on( 'click', '#itsec-security-check-secure_site', function( e ) {
4
+ e.preventDefault();
5
+
6
+ $( '#itsec-security-check-secure_site' )
7
+ .removeClass( 'button-primary' )
8
+ .addClass( 'button-secondary' )
9
+ .attr( 'value', itsec_security_check_settings.securing_site )
10
+ .prop( 'disabled', true );
11
+
12
+ $( '#itsec-security-check-details-container' ).html( '' );
13
+
14
+ var data = {
15
+ 'method': 'secure-site'
16
+ };
17
+
18
+ itsecUtil.sendModuleAJAXRequest( 'security-check', data, function( results ) {
19
+ $( '#itsec-security-check-secure_site' )
20
+ .addClass( 'button-primary' )
21
+ .removeClass( 'button-secondary' )
22
+ .attr( 'value', itsec_security_check_settings.rerun_secure_site )
23
+ .prop( 'disabled', false );
24
+
25
+ $( '#itsec-security-check-details-container' ).html( results.response );
26
+ } );
27
+ } );
28
+
29
+ $( document ).on( 'click', '#itsec-module-card-security-check .itsec-security-check-container-is-interactive :submit', function( e ) {
30
+ e.preventDefault();
31
+
32
+ var $button = $( this );
33
+ var $container = $( this ).parents( '.itsec-security-check-container-is-interactive' );
34
+ var inputs = $container.find( ':input' ).serializeArray();
35
+ var data = {};
36
+
37
+ for ( var i = 0; i < inputs.length; i++ ) {
38
+ var input = inputs[i];
39
+
40
+ if ( '[]' === input.name.substr( -2 ) ) {
41
+ var name = input.name.substr( 0, input.name.length - 2 );
42
+
43
+ if ( data[name] ) {
44
+ data[name].push( input.value );
45
+ } else {
46
+ data[name] = [input.value];
47
+ }
48
+ } else {
49
+ data[input.name] = input.value;
50
+ }
51
+ };
52
+
53
+
54
+ $button
55
+ .removeClass( 'button-primary' )
56
+ .addClass( 'button-secondary' )
57
+ .prop( 'disabled', true );
58
+
59
+ if ( $button.data( 'clicked-value' ) ) {
60
+ $button
61
+ .data( 'original-value', $( this ).val() )
62
+ .attr( 'value', $( this ).data( 'clicked-value' ) )
63
+ }
64
+
65
+ var ajaxFunction = itsecUtil.sendModuleAJAXRequest;
66
+
67
+ if ( 'undefined' !== typeof itsecSecurityCheckAJAXRequest ) {
68
+ ajaxFunction = itsecSecurityCheckAJAXRequest;
69
+ }
70
+
71
+ ajaxFunction( 'security-check', data, function( results ) {
72
+ $button
73
+ .removeClass( 'button-secondary' )
74
+ .addClass( 'button-primary' )
75
+ .prop( 'disabled', false );
76
+
77
+ if ( $button.data( 'original-value' ) ) {
78
+ $button
79
+ .attr( 'value', $( this ).data( 'original-value' ) )
80
+ }
81
+
82
+
83
+ var $feedback = $container.find( '.itsec-security-check-feedback' );
84
+ $feedback.html( '' );
85
+
86
+ if ( results.errors && results.errors.length > 0 ) {
87
+ $container
88
+ .removeClass( 'itsec-security-check-container-call-to-action' )
89
+ .removeClass( 'itsec-security-check-container-confirmation' )
90
+ .addClass( 'itsec-security-check-container-error' );
91
+
92
+ $.each( results.errors, function( index, error ) {
93
+ $feedback.append( '<div class="error inline"><p><strong>' + error + '</strong></p></div>' );
94
+ } );
95
+ } else {
96
+ $container
97
+ .removeClass( 'itsec-security-check-container-call-to-action' )
98
+ .removeClass( 'itsec-security-check-container-error' )
99
+ .addClass( 'itsec-security-check-container-confirmation' );
100
+
101
+ $container.html( results.response );
102
+ $( '#itsec-notice-network-brute-force' ).hide();
103
+ }
104
+ } );
105
+ } );
106
+ } );
core/modules/security-check-pro/privacy.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Security_Check_Pro_Privacy {
4
+ public function __construct() {
5
+ add_filter( 'itsec_get_privacy_policy_for_sharing', array( $this, 'get_privacy_policy_for_sharing' ) );
6
+ }
7
+
8
+ public function get_privacy_policy_for_sharing( $policy ) {
9
+ /* Translators: 1: Link to iThemes' privacy policy */
10
+ $policy .= "<p class=\"privacy-policy-tutorial\">" . sprintf( wp_kses( __( 'When running Security Check, ithemes.com will be contacted as part of a process to determine if the site supports TLS/SSL requests. No personal data is sent to ithemes.com as part of this process. Requests to ithemes.com include the site\'s URL. For ithemes.com privacy policy details, please see the <a href="%1$s">iThemes Privacy Policy</a>.', 'better-wp-security' ), array( 'a' => array( 'href' => array() ) ) ), 'https://ithemes.com/privacy-policy/' ) . "</p>\n";
11
+
12
+ return $policy;
13
+ }
14
+ }
15
+ new ITSEC_Security_Check_Pro_Privacy();
core/modules/security-check-pro/settings-page.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Security_Check_Pro_Settings_Page extends ITSEC_Module_Settings_Page {
4
+
5
+ public function __construct() {
6
+ $this->id = 'security-check-pro';
7
+ $this->title = __( 'Security Check Pro', 'better-wp-security' );
8
+ $this->description = __( 'Adds secure automatic IP detection and SSL server setup checks.', 'better-wp-security' );
9
+ $this->type = 'advanced';
10
+ $this->can_save = false;
11
+
12
+ parent::__construct();
13
+ }
14
+
15
+ protected function render_description( $form ) {
16
+ echo '<p>';
17
+ echo __( 'Adds secure automatic IP detection and SSL server setup checks.', 'better-wp-security' );
18
+ printf(
19
+ __( 'This feature requires contacting an iThemes.com server. See our %1$sPrivacy Policy%2$s.', 'better-wp-security' ),
20
+ '<a href="https://ithemes.com/privacy-policy/">',
21
+ '</a>'
22
+ );
23
+ echo '</p>';
24
+ }
25
+
26
+ protected function render_settings( $form ) {
27
+
28
+ }
29
+ }
30
+
31
+
32
+ if ( ! ITSEC_Modules::is_always_active( 'security-check-pro' ) ) {
33
+ new ITSEC_Security_Check_Pro_Settings_Page();
34
+ }
core/modules/security-check-pro/settings.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Security_Check_Pro_Settings extends ITSEC_Settings {
4
+ public function get_id() {
5
+ return 'security-check-pro';
6
+ }
7
+
8
+ public function get_defaults() {
9
+ return array(
10
+ 'last_scan_timestamp' => null,
11
+ 'remote_ip_index' => null,
12
+ 'ssl_supported' => null,
13
+ 'remote_ips_timestamp' => null,
14
+ 'remote_ips' => array(),
15
+ 'key_salt' => '',
16
+ );
17
+ }
18
+ }
19
+
20
+ ITSEC_Modules::register_settings( new ITSEC_Security_Check_Pro_Settings() );
core/modules/security-check-pro/utility.php ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Security_Check_Pro_Utility {
4
+ private static $api_url = 'https://itsec-ssl-proxy-detect.ithemes.com/';
5
+ private static $config_url = 'https://itsec-ssl-proxy-detect.ithemes.com/config.json';
6
+
7
+ /**
8
+ * Run the security check pro scan.
9
+ *
10
+ * @param ITSEC_Security_Check_Feedback $feedback
11
+ *
12
+ * @return array|WP_Error
13
+ */
14
+ public static function run_scan( $feedback ) {
15
+ $response = self::get_server_response();
16
+
17
+ if ( ! is_array( $response ) ) {
18
+ $settings = ITSEC_Modules::get_settings( 'security-check-pro' );
19
+
20
+ if ( ! is_int( $settings['last_scan_timestamp'] ) || time() > $settings['last_scan_timestamp'] + HOUR_IN_SECONDS ) {
21
+ return $response;
22
+ }
23
+
24
+ $response = array(
25
+ 'remote_ip' => ! empty( $settings['remote_ip_index'] ),
26
+ 'ssl_supported' => $settings['ssl_supported'],
27
+ );
28
+ }
29
+
30
+ if ( ! defined( 'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION' ) || ! ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION ) {
31
+ if ( isset( $response['remote_ip'] ) && $response['remote_ip'] ) {
32
+ ITSEC_Modules::set_setting( 'global', 'proxy', 'security-check' );
33
+ $feedback->add_section( 'security-check-pro-remote-ip', array( 'status' => 'action-taken' ) );
34
+ $feedback->add_text( __( 'Identified remote IP entry to protect against IP spoofing.', 'better-wp-security' ) );
35
+ }
36
+ }
37
+
38
+ if ( isset( $response['ssl_supported'] ) && $response['ssl_supported'] ) {
39
+ ITSEC_Response::reload_module( 'ssl' );
40
+
41
+ $ssl_settings = ITSEC_Modules::get_settings( 'ssl' );
42
+
43
+ if ( 'enabled' === $ssl_settings['require_ssl'] || ( 'advanced' === $ssl_settings['require_ssl'] && $ssl_settings['admin'] ) ) {
44
+ $feedback->add_section( 'security-check-pro-ssl' );
45
+ $feedback->add_text( __( 'Requests for http pages are redirected to https as recommended.', 'better-wp-security' ) );
46
+ } else {
47
+ $feedback->add_section( 'security-check-pro-ssl', array( 'interactive' => true, 'status' => 'call-to-action' ) );
48
+ $feedback->add_text( __( 'Your site supports SSL. Redirecting all http page requests to https is highly recommended as it protects login details from being stolen when using public WiFi or insecure networks.', 'better-wp-security' ) );
49
+
50
+ if ( ! is_ssl() ) {
51
+ $feedback->add_text( __( 'Please note that you will have to log back in after enabling this.', 'better-wp-security' ) );
52
+ }
53
+
54
+ $feedback->add_input( 'submit', 'enable_ssl', array(
55
+ 'value' => __( 'Redirect HTTP Requests to HTTPS', 'better-wp-security' ),
56
+ 'style_class' => 'button-primary',
57
+ 'data' => array(
58
+ 'clicked-value' => __( 'Updating Site Configuration...', 'better-wp-security' ),
59
+ ),
60
+ ) );
61
+ $feedback->add_input( 'hidden', 'method', array(
62
+ 'value' => 'enable-ssl',
63
+ ) );
64
+ }
65
+ }
66
+
67
+ return $response;
68
+ }
69
+
70
+ public static function handle_enable_ssl( $data ) {
71
+ $settings = ITSEC_Modules::get_settings( 'ssl' );
72
+
73
+ $settings['require_ssl'] = 'enabled';
74
+
75
+ $results = ITSEC_Modules::set_settings( 'ssl', $settings );
76
+
77
+ if ( is_wp_error( $results ) ) {
78
+ ITSEC_Response::add_error( $results );
79
+ } elseif ( $results['saved'] ) {
80
+ ITSEC_Modules::activate( 'ssl' );
81
+ ITSEC_Response::add_js_function_call( 'setModuleToActive', 'ssl' );
82
+ ITSEC_Response::set_response( '<p>' . __( 'Your site now redirects http page requests to https.', 'better-wp-security' ) . '</p>' );
83
+ ITSEC_Response::reload_module( 'ssl' );
84
+ }
85
+ }
86
+
87
+ public static function handle_scan_request() {
88
+ if ( ! isset( $_POST['itsec-security-check'] ) || 'scan' !== $_POST['itsec-security-check'] ) {
89
+ return;
90
+ }
91
+
92
+ if ( ! isset( $_POST['site'], $_POST['key'], $_POST['expect'], $_POST['scheme'] ) ) {
93
+ return;
94
+ }
95
+
96
+ if ( ! self::validate_key( $_POST['key'] ) ) {
97
+ return;
98
+ }
99
+
100
+ if ( defined( 'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION' ) && ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION ) {
101
+ $remote_ip_index = '';
102
+ } else {
103
+ $remote_ip_index = self::get_remote_ip_index();
104
+
105
+ if ( false === $remote_ip_index ) {
106
+ $remote_ip_index = '';
107
+ }
108
+ }
109
+
110
+ if ( 'https' === $_POST['scheme'] && is_ssl() ) {
111
+ $ssl_supported = true;
112
+ } else {
113
+ $ssl_supported = false;
114
+ }
115
+
116
+ $settings = array(
117
+ 'last_scan_timestamp' => time(),
118
+ 'remote_ip_index' => $remote_ip_index,
119
+ 'ssl_supported' => $ssl_supported,
120
+ );
121
+
122
+ ITSEC_Modules::set_settings( 'security-check-pro', $settings );
123
+
124
+ header( 'Content-Type: text/plain' );
125
+ echo "<response>{$_POST['expect']}:" . ( empty( $remote_ip_index ) ? 'false' : 'true' ) . ':' . ( $ssl_supported ? 'true' : 'false' ) . '</response>';
126
+ exit();
127
+ }
128
+
129
+ public static function get_remote_ip_index() {
130
+ $remote_ips = self::get_remote_ips();
131
+
132
+
133
+ $standard_indexes = array(
134
+ 'REMOTE_ADDR',
135
+ 'HTTP_X_REAL_IP',
136
+ 'HTTP_X_FORWARDED_FOR',
137
+ 'HTTP_CF_CONNECTING_IP',
138
+ 'HTTP_CLIENT_IP',
139
+ );
140
+
141
+ foreach ( $remote_ips as $ip ) {
142
+ foreach ( $standard_indexes as $standard_index ) {
143
+ $index = self::get_index( $ip, $standard_index );
144
+
145
+ if ( false !== $index ) {
146
+ return $index;
147
+ }
148
+ }
149
+ }
150
+
151
+
152
+ foreach ( $remote_ips as $ip ) {
153
+ foreach ( array_keys( $_SERVER ) as $var ) {
154
+ $index = self::get_index( $ip, $var );
155
+
156
+ if ( false !== $index ) {
157
+ return $index;
158
+ }
159
+ }
160
+ }
161
+
162
+
163
+ return false;
164
+ }
165
+
166
+ public static function get_index( $ip, $var ) {
167
+ if ( ! isset( $_SERVER[ $var ] ) ) {
168
+ return false;
169
+ }
170
+
171
+ if ( $_SERVER[ $var ] === $ip ) {
172
+ return $var;
173
+ }
174
+
175
+ $value = trim( $_SERVER[ $var ] );
176
+ $ip_regex_pattern = '/' . preg_quote( $ip, '/' ) . '/';
177
+
178
+ if ( preg_match( $ip_regex_pattern, $value ) ) {
179
+ $potential_ips = preg_split( '/[, ]+/', $value );
180
+
181
+ foreach ( $potential_ips as $index => $potential_ip ) {
182
+ if ( $ip === $potential_ip ) {
183
+ return array( $var, $index );
184
+ }
185
+ }
186
+
187
+ if ( preg_match_all( '{(?:for)=(?:"?\[?)([a-z0-9\.:_\-/]*)}i', $value, $matches, PREG_SET_ORDER ) ) {
188
+ foreach ( $matches as $index => $match ) {
189
+ if ( $ip === $match[1] ) {
190
+ return array( $var, $index );
191
+ }
192
+ }
193
+ }
194
+ }
195
+
196
+ return false;
197
+ }
198
+
199
+ public static function get_server_response() {
200
+ $data = array(
201
+ 'site' => get_home_url(),
202
+ 'key' => self::get_key(),
203
+ );
204
+
205
+ $remote_post_args = array(
206
+ 'timeout' => 60,
207
+ 'body' => $data,
208
+ );
209
+
210
+ $response = wp_remote_post( self::$api_url, $remote_post_args );
211
+
212
+ if ( is_wp_error( $response ) && ( 'connect() timed out!' !== $response->get_error_message() ) ) {
213
+ $url = preg_replace( '|^https://|', 'http://', self::$api_url );
214
+ $response = wp_remote_post( $url, $remote_post_args );
215
+ }
216
+
217
+ if ( is_wp_error( $response ) ) {
218
+ if ( 'connect() timed out!' === $response->get_error_message() ) {
219
+ return new WP_Error( 'http_request_failed', __( 'The server was unable to be contacted.', 'better-wp-security' ) );
220
+ }
221
+
222
+ return $response;
223
+ }
224
+
225
+ if ( '' === trim( $response['body'] ) ) {
226
+ return new WP_Error( 'itsec-security-check-pro-empty-response', __( 'An error occurred when communicating with the iThemes Security Check server: The server returned a blank response.', 'better-wp-security' ) );
227
+ }
228
+
229
+ $body = json_decode( $response['body'], true );
230
+
231
+ if ( is_null( $body ) ) {
232
+ return new WP_Error( 'itsec-security-check-pro-non-json-response', __( 'An error occurred when communicating with the iThemes Security Check server: The server did not return JSON data when JSON data was expected.', 'better-wp-security' ) );
233
+ }
234
+
235
+ if ( isset( $body['error'], $body['error']['code'], $body['error']['message'] ) ) {
236
+ return new WP_Error( 'itsec-security-check-pro-' . $body['error']['code'], sprintf( __( 'An error occurred when communicating with the iThemes Security Check server: %s (%s)', 'better-wp-security' ), $body['error']['message'], $body['error']['code'] ) );
237
+ }
238
+
239
+ if ( empty( $body['complete'] ) ) {
240
+ return new WP_Error( 'itsec-security-check-pro-scan-incomplete', __( 'The iThemes Security Check server could not contact your site. Please wait a few minutes and try again.', 'better-wp-security' ) );
241
+ }
242
+
243
+ return $body;
244
+ }
245
+
246
+ public static function validate_key( $key, $expires = false ) {
247
+ $salt = ITSEC_Modules::get_setting( 'security-check-pro', 'key_salt' );
248
+ $key = trim( $key );
249
+
250
+ if ( empty( $salt ) ) {
251
+ return false; // Only validate if a salt has been stored.
252
+ }
253
+
254
+ if ( ! preg_match( '/^(\d+):([a-f0-9]+)$/', $key, $matches ) ) {
255
+ return false;
256
+ }
257
+
258
+ if ( false === $expires ) {
259
+ $expires = 2 * MINUTE_IN_SECONDS; // keys expire every 2 minutes by default.
260
+ }
261
+
262
+ $time = $matches[1];
263
+ $hash = $matches[2];
264
+
265
+ if ( time() > $time + $expires ) {
266
+ return false;
267
+ }
268
+
269
+ $calculated_hash = hash_hmac( 'md5', $time, $salt );
270
+
271
+ return hash_equals( $calculated_hash, $hash );
272
+ }
273
+
274
+ public static function get_key() {
275
+ $salt = ITSEC_Modules::get_setting( 'security-check-pro', 'key_salt' );
276
+
277
+ if ( empty( $salt ) ) {
278
+ $salt = wp_generate_password( 60, true, true );
279
+ ITSEC_Modules::set_setting( 'security-check-pro', 'key_salt', $salt );
280
+ ITSEC_Storage::save();
281
+ }
282
+
283
+ $time = time();
284
+ $hash = hash_hmac( 'md5', $time, $salt );
285
+
286
+ $key = "$time:$hash";
287
+
288
+ return $key;
289
+ }
290
+
291
+ public static function get_remote_ips() {
292
+ $remote_ips = apply_filters( 'itsec-security-check-pro-remote-ips', array() );
293
+
294
+ if ( is_array( $remote_ips ) && ! empty( $remote_ips ) ) {
295
+ return $remote_ips;
296
+ }
297
+
298
+
299
+ $settings = ITSEC_Modules::get_settings( 'security-check-pro' );
300
+
301
+ if ( $settings['remote_ips_timestamp'] + ( 5 * MINUTE_IN_SECONDS ) > time() && ! empty( $settings['remote_ips'] ) ) {
302
+ return $settings['remote_ips'];
303
+ }
304
+
305
+
306
+ $response = wp_remote_get( self::$config_url );
307
+
308
+ if ( is_wp_error( $response ) ) {
309
+ return array();
310
+ }
311
+
312
+
313
+ $body = $response['body'];
314
+ $data = json_decode( $body, true );
315
+
316
+ if ( ! is_array( $data ) || ! isset( $data['ips'] ) || ! is_array( $data['ips'] ) ) {
317
+ return array();
318
+ }
319
+
320
+
321
+ $settings['remote_ips_timestamp'] = time();
322
+ $settings['remote_ips'] = $data['ips'];
323
+
324
+ ITSEC_Modules::set_settings( 'security-check-pro', $settings );
325
+
326
+ return $data['ips'];
327
+ }
328
+ }
core/modules/security-check-pro/validator.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Security_Check_Pro_Validator extends ITSEC_Validator {
4
+ public function get_id() {
5
+ return 'security-check-pro';
6
+ }
7
+
8
+ protected function sanitize_settings() {
9
+ $this->set_previous_if_empty( array_keys( $this->defaults ) );
10
+ $this->vars_to_skip_validate_matching_fields = array_keys( $this->defaults );
11
+ $this->vars_to_skip_validate_matching_types = array_keys( $this->defaults );
12
+
13
+ if ( ! is_null( $this->settings['last_scan_timestamp'] ) && ! is_int( $this->settings['last_scan_timestamp'] ) || $this->settings['last_scan_timestamp'] < 0 ) {
14
+ $this->settings['last_scan_timestamp'] = $this->defaults['last_scan_timestamp'];
15
+ }
16
+
17
+ if ( is_array( $this->settings['remote_ip_index'] ) ) {
18
+ if ( 2 !== count( $this->settings['remote_ip_index'] ) || ! is_string( $this->settings['remote_ip_index'][0] ) || ! is_int( $this->settings['remote_ip_index'][1] ) ) {
19
+ $this->settings['remote_ip_index'] = $this->defaults['remote_ip_index'];
20
+ }
21
+ } else if ( ! is_null( $this->settings['remote_ip_index'] ) && ! is_string( $this->settings['remote_ip_index'] ) ) {
22
+ $this->settings['remote_ip_index'] = $this->defaults['remote_ip_index'];
23
+ }
24
+
25
+ if ( ! is_null( $this->settings['ssl_supported'] ) && ! is_bool( $this->settings['ssl_supported'] ) ) {
26
+ $this->settings['ssl_supported'] = $this->defaults['ssl_supported'];
27
+ }
28
+
29
+ if ( ! is_int( $this->settings['remote_ips_timestamp'] ) ) {
30
+ $this->settings['remote_ips_timestamp'] = $this->defaults['remote_ips_timestamp'];
31
+ }
32
+
33
+ if ( ! is_array( $this->settings['remote_ips'] ) ) {
34
+ $this->settings['remote_ips'] = $this->defaults['remote_ips'];
35
+ }
36
+
37
+ if ( ! is_string( $this->settings['key_salt'] ) ) {
38
+ $this->settings['key_salt'] = $this->defaults['key_salt'];
39
+ }
40
+ }
41
+ }
42
+
43
+ ITSEC_Modules::register_validator( new ITSEC_Security_Check_Pro_Validator() );
core/modules/security-check/js/settings-page.js CHANGED
@@ -12,7 +12,8 @@ jQuery( document ).ready( function ( $ ) {
12
  $( '#itsec-security-check-details-container' ).html( '' );
13
 
14
  var data = {
15
- 'method': 'secure-site'
 
16
  };
17
 
18
  itsecUtil.sendModuleAJAXRequest( 'security-check', data, function( results ) {
12
  $( '#itsec-security-check-details-container' ).html( '' );
13
 
14
  var data = {
15
+ 'method': 'secure-site',
16
+ pro: $( '#itsec-security-check-security_check_pro' ).is( ':checked' ),
17
  };
18
 
19
  itsecUtil.sendModuleAJAXRequest( 'security-check', data, function( results ) {
core/modules/security-check/settings-page.php CHANGED
@@ -1,16 +1,15 @@
1
  <?php
2
 
3
  final class ITSEC_Security_Check_Settings_Page extends ITSEC_Module_Settings_Page {
4
- private $script_version = 3;
5
-
6
 
7
  public function __construct() {
8
- $this->id = 'security-check';
9
- $this->title = __( 'Security Check', 'better-wp-security' );
10
- $this->description = __( 'Ensure that your site is using the recommended features and settings.', 'better-wp-security' );
11
- $this->type = 'recommended';
12
  $this->information_only = true;
13
- $this->can_save = false;
14
 
15
  parent::__construct();
16
  }
@@ -27,6 +26,12 @@ final class ITSEC_Security_Check_Settings_Page extends ITSEC_Module_Settings_Pag
27
 
28
  public function handle_ajax_request( $data ) {
29
  if ( 'secure-site' === $data['method'] ) {
 
 
 
 
 
 
30
  require_once( dirname( __FILE__ ) . '/scanner.php' );
31
  require_once( dirname( __FILE__ ) . '/feedback-renderer.php' );
32
 
@@ -35,7 +40,7 @@ final class ITSEC_Security_Check_Settings_Page extends ITSEC_Module_Settings_Pag
35
  ob_start();
36
  ITSEC_Security_Check_Feedback_Renderer::render( $results );
37
  ITSEC_Response::set_response( ob_get_clean() );
38
- } else if ( 'activate-network-brute-force' === $data['method'] ) {
39
  require_once( dirname( __FILE__ ) . '/scanner.php' );
40
 
41
  ITSEC_Security_Check_Scanner::activate_network_brute_force( $_POST['data'] );
@@ -44,27 +49,39 @@ final class ITSEC_Security_Check_Settings_Page extends ITSEC_Module_Settings_Pag
44
  }
45
  }
46
 
47
- protected function render_description( $form ) {}
48
 
49
  protected function render_settings( $form ) {
50
  require_once( dirname( __FILE__ ) . '/scanner.php' );
51
 
52
  $modules_to_activate = ITSEC_Security_Check_Scanner::get_supported_modules();
53
-
54
- ?>
55
- <div id="itsec-security-check-details-container">
56
- <p><?php _e( 'Some features and settings are recommended for every site to run. This tool will ensure that your site is using these recommendations.', 'better-wp-security' ); ?></p>
57
- <p><?php _e( 'When the button below is clicked the following modules will be enabled and configured:', 'better-wp-security' ); ?></p>
58
- <ul class="itsec-security-check-list">
59
- <?php foreach ( $modules_to_activate as $name ) : ?>
60
- <li><p><?php echo $name; ?></p></li>
61
- <?php endforeach; ?>
62
- </ul>
63
- </div>
64
-
65
- <p><?php $form->add_button( 'secure_site', array( 'value' => 'Secure Site', 'class' => 'button-primary' ) ); ?></p>
66
- <?php
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  }
69
  }
 
70
  new ITSEC_Security_Check_Settings_Page();
1
  <?php
2
 
3
  final class ITSEC_Security_Check_Settings_Page extends ITSEC_Module_Settings_Page {
4
+ private $script_version = 4;
 
5
 
6
  public function __construct() {
7
+ $this->id = 'security-check';
8
+ $this->title = __( 'Security Check', 'better-wp-security' );
9
+ $this->description = __( 'Ensure that your site is using the recommended features and settings.', 'better-wp-security' );
10
+ $this->type = 'recommended';
11
  $this->information_only = true;
12
+ $this->can_save = false;
13
 
14
  parent::__construct();
15
  }
26
 
27
  public function handle_ajax_request( $data ) {
28
  if ( 'secure-site' === $data['method'] ) {
29
+ if ( ! empty( $data['pro'] ) ) {
30
+ ITSEC_Modules::activate( 'security-check-pro' );
31
+ ITSEC_Modules::load_module_file( 'active.php', 'security-check-pro' );
32
+ ITSEC_Response::remove_js_function_call( 'reloadModule', 'security-check' );
33
+ }
34
+
35
  require_once( dirname( __FILE__ ) . '/scanner.php' );
36
  require_once( dirname( __FILE__ ) . '/feedback-renderer.php' );
37
 
40
  ob_start();
41
  ITSEC_Security_Check_Feedback_Renderer::render( $results );
42
  ITSEC_Response::set_response( ob_get_clean() );
43
+ } elseif ( 'activate-network-brute-force' === $data['method'] ) {
44
  require_once( dirname( __FILE__ ) . '/scanner.php' );
45
 
46
  ITSEC_Security_Check_Scanner::activate_network_brute_force( $_POST['data'] );
49
  }
50
  }
51
 
52
+ protected function render_description( $form ) { }
53
 
54
  protected function render_settings( $form ) {
55
  require_once( dirname( __FILE__ ) . '/scanner.php' );
56
 
57
  $modules_to_activate = ITSEC_Security_Check_Scanner::get_supported_modules();
58
+ ?>
59
+ <div id="itsec-security-check-details-container">
60
+ <p><?php _e( 'Some features and settings are recommended for every site to run. This tool will ensure that your site is using these recommendations.', 'better-wp-security' ); ?></p>
61
+ <p><?php _e( 'When the button below is clicked the following modules will be enabled and configured:', 'better-wp-security' ); ?></p>
62
+ <ul class="itsec-security-check-list">
63
+ <?php foreach ( $modules_to_activate as $name ) : ?>
64
+ <li><p><?php echo $name; ?></p></li>
65
+ <?php endforeach; ?>
66
+ </ul>
67
+
68
+ <?php if ( ! ITSEC_Modules::is_active( 'security-check-pro' ) ) : ?>
69
+ <p>
70
+ <label>
71
+ <?php $form->add_checkbox( 'security_check_pro' ); ?>
72
+ <?php printf(
73
+ esc_html__( 'Enable Security Check Pro to identify IP addresses and ensure attackers are locked out correctly. This requires contacting an iThemes.com server. Read our %1$sPrivacy Policy%2$s.', 'better-wp-security' ),
74
+ '<a href="https://ithemes.com/privacy-policy/" target="_blank">',
75
+ '</a>'
76
+ ); ?>
77
+ </label>
78
+ </p>
79
+ <?php endif; ?>
80
+ </div>
81
+ <p><?php $form->add_button( 'secure_site', array( 'value' => 'Secure Site', 'class' => 'button-primary' ) ); ?></p>
82
+ <?php
83
 
84
  }
85
  }
86
+
87
  new ITSEC_Security_Check_Settings_Page();
core/package.json CHANGED
@@ -110,7 +110,8 @@
110
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
111
  "test-unit:watch": "npm run test-unit -- --watch",
112
  "watch": "./node_modules/.bin/webpack --watch",
113
- "test-acceptance": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
114
- "test-acceptance:build": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
 
115
  }
116
  }
110
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
111
  "test-unit:watch": "npm run test-unit -- --watch",
112
  "watch": "./node_modules/.bin/webpack --watch",
113
+ "test-wpunit": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpunit",
114
+ "test-acceptance": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
115
+ "test-acceptance:build": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
116
  }
117
  }
core/response.php CHANGED
@@ -153,6 +153,15 @@ final class ITSEC_Response {
153
  return $self->js_function_calls;
154
  }
155
 
 
 
 
 
 
 
 
 
 
156
  public static function set_show_default_success_message( $show_default_success_message ) {
157
  $self = self::get_instance();
158
 
153
  return $self->js_function_calls;
154
  }
155
 
156
+ public static function remove_js_function_call( $js_function, $args = null ) {
157
+ $self = self::get_instance();
158
+ $call = is_null( $args ) ? array( $js_function ) : array( $js_function, $args );
159
+
160
+ $self->js_function_calls = array_values( array_filter( $self->js_function_calls, static function ( $maybe_call ) use ( $call ) {
161
+ return $maybe_call !== $call;
162
+ } ) );
163
+ }
164
+
165
  public static function set_show_default_success_message( $show_default_success_message ) {
166
  $self = self::get_instance();
167
 
history.txt CHANGED
@@ -859,3 +859,7 @@
859
  Bug Fix: Admin Notices list did not refresh after dismissing a notice.
860
  Bug Fix: Strong Passwords zxcvbn Library was not evaluating penalty strings correctly.
861
  Bug Fix: Fix PHP warning if there are multiple detected proxy headers.
 
 
 
 
859
  Bug Fix: Admin Notices list did not refresh after dismissing a notice.
860
  Bug Fix: Strong Passwords zxcvbn Library was not evaluating penalty strings correctly.
861
  Bug Fix: Fix PHP warning if there are multiple detected proxy headers.
862
+ 7.6.0 - 2019-12-09 - Timothy Jacobs
863
+ New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
864
+ Enhancement: Run Security Check Pro IP Detection automatically once a day.
865
+ Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
package.json CHANGED
@@ -110,7 +110,8 @@
110
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
111
  "test-unit:watch": "npm run test-unit -- --watch",
112
  "watch": "./node_modules/.bin/webpack --watch",
113
- "test-acceptance": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
114
- "test-acceptance:build": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
 
115
  }
116
  }
110
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
111
  "test-unit:watch": "npm run test-unit -- --watch",
112
  "watch": "./node_modules/.bin/webpack --watch",
113
+ "test-wpunit": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpunit",
114
+ "test-acceptance": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
115
+ "test-acceptance:build": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
116
  }
117
  }
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: ithemes, chrisjean, mattdanner, timothyblynjacobs
3
  Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
4
  Requires at least: 4.7
5
  Tested up to: 5.3.0
6
- Stable tag: 7.5.0
7
  Requires PHP: 5.4
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -189,6 +189,11 @@ Free support may be available with the help of the community in the <a href="htt
189
 
190
  == Changelog ==
191
 
 
 
 
 
 
192
  = 7.5.0 =
193
  * Breaking Change: iThemes Security requires PHP 5.4 or later.
194
  * Enhancement: New Lockout Template screen.
@@ -560,5 +565,5 @@ Free support may be available with the help of the community in the <a href="htt
560
 
561
  == Upgrade Notice ==
562
 
563
- = 7.5.0 =
564
- Version 7.5.0 contains new features and bug fixes. It is recommended for all users.
3
  Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
4
  Requires at least: 4.7
5
  Tested up to: 5.3.0
6
+ Stable tag: 7.6.0
7
  Requires PHP: 5.4
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
189
 
190
  == Changelog ==
191
 
192
+ = 7.6.0 =
193
+ * New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
194
+ * Enhancement: Run Security Check Pro IP Detection automatically once a day.
195
+ * Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
196
+
197
  = 7.5.0 =
198
  * Breaking Change: iThemes Security requires PHP 5.4 or later.
199
  * Enhancement: New Lockout Template screen.
565
 
566
  == Upgrade Notice ==
567
 
568
+ = 7.6.0 =
569
+ Version 7.6.0 contains new features and bug fixes. It is recommended for all users.