iThemes Security (formerly Better WP Security) - Version 7.0.1

Version Description

  • Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
  • Bug Fix: Skip recovery if File Change storage is empty.
Download this release

Release Info

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

Code changes from version 7.1.0 to 7.0.1

Files changed (77) hide show
  1. better-wp-security.php +1 -1
  2. core/admin-pages/css/style.css +0 -19
  3. core/admin-pages/init.php +1 -6
  4. core/admin-pages/js/debug.js +0 -90
  5. core/admin-pages/js/script.js +21 -25
  6. core/admin-pages/js/settings.js +21 -25
  7. core/admin-pages/js/util.js +1 -37
  8. core/admin-pages/logs-list-table.php +23 -88
  9. core/admin-pages/page-debug.php +0 -425
  10. core/admin-pages/page-logs.php +0 -22
  11. core/admin-pages/page-settings.php +0 -2
  12. core/core.php +12 -20
  13. core/history.txt +1 -50
  14. core/lib.php +32 -143
  15. core/lib/class-itsec-job.php +3 -4
  16. core/lib/class-itsec-lib-canonical-roles.php +0 -70
  17. core/lib/class-itsec-lib-password-requirements.php +159 -156
  18. core/lib/class-itsec-mail.php +28 -148
  19. core/lib/class-itsec-scheduler-cron.php +12 -19
  20. core/lib/class-itsec-scheduler-page-load.php +2 -17
  21. core/lib/class-itsec-scheduler.php +0 -14
  22. core/lib/class-itsec-wp-list-table.php +11 -9
  23. core/lib/form.php +0 -39
  24. core/lib/log-util.php +16 -34
  25. core/lib/mail-templates/header.html +0 -1
  26. core/lib/schema.php +0 -1
  27. core/lib/settings.php +22 -24
  28. core/lib/validator.php +1 -75
  29. core/lockout.php +4 -1
  30. core/modules/404-detection/class-itsec-four-oh-four.php +8 -4
  31. core/modules/404-detection/logs.php +0 -5
  32. core/modules/away-mode/class-itsec-away-mode.php +1 -6
  33. core/modules/file-change/class-itsec-file-change.php +1 -1
  34. core/modules/file-change/css/settings.css +0 -14
  35. core/modules/file-change/js/settings-page.js +0 -15
  36. core/modules/file-change/logs.php +1 -11
  37. core/modules/file-change/scanner.php +8 -18
  38. core/modules/file-change/settings-page.php +4 -15
  39. core/modules/global/active.php +1 -20
  40. core/modules/global/settings-page.php +0 -14
  41. core/modules/global/settings.php +0 -12
  42. core/modules/global/validator.php +1 -2
  43. core/modules/notification-center/class-notification-center.php +26 -72
  44. core/modules/notification-center/debug.php +0 -92
  45. core/modules/notification-center/js/debug.js +0 -22
  46. core/modules/notification-center/js/settings-page.js +4 -6
  47. core/modules/notification-center/logs.php +0 -94
  48. core/modules/notification-center/settings-page.php +75 -103
  49. core/modules/notification-center/settings.php +29 -79
  50. core/modules/notification-center/setup.php +2 -19
  51. core/modules/notification-center/validator.php +5 -60
  52. core/modules/password-requirements/active.php +0 -6
  53. core/modules/password-requirements/class-itsec-password-requirements.php +0 -480
  54. core/modules/password-requirements/css/index.php +0 -1
  55. core/modules/password-requirements/css/settings-page.css +0 -3
  56. core/modules/password-requirements/index.php +0 -1
  57. core/modules/password-requirements/js/index.php +0 -1
  58. core/modules/password-requirements/js/settings-page.js +0 -24
  59. core/modules/password-requirements/settings-page.php +0 -115
  60. core/modules/password-requirements/settings.php +0 -51
  61. core/modules/password-requirements/validator.php +0 -62
  62. core/modules/pro/settings-page.php +16 -0
  63. core/modules/security-check/scanner.php +2 -38
  64. core/modules/ssl/class-itsec-ssl.php +2 -2
  65. core/modules/strong-passwords/class-itsec-strong-passwords.php +168 -139
  66. core/modules/strong-passwords/js/script.js +8 -1
  67. core/modules/strong-passwords/settings-page.php +51 -0
  68. core/modules/strong-passwords/setup.php +4 -26
  69. core/modules/wordpress-tweaks/class-itsec-wordpress-tweaks.php +5 -155
  70. core/modules/wordpress-tweaks/settings-page.php +0 -8
  71. core/modules/wordpress-tweaks/settings.php +0 -1
  72. core/modules/wordpress-tweaks/validator.php +0 -1
  73. core/notify.php +5 -10
  74. core/response.php +8 -22
  75. core/setup.php +6 -4
  76. history.txt +0 -38
  77. readme.txt +4 -46
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.1.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.0.1
10
  * Text Domain: better-wp-security
11
  * Network: True
12
  * License: GPLv2
core/admin-pages/css/style.css CHANGED
@@ -851,22 +851,3 @@ body.security_page_itsec-logs #old-logs-migration-status img {
851
  body.security_page_itsec-logs #old-logs-migration-status p {
852
  line-height: 1.4em;
853
  }
854
- #itsec-system-info, #itsec-settings-editor {
855
- font-family: monospace;
856
- width: 100%;
857
- margin: 0;
858
- height: 300px;
859
- padding: 20px;
860
- border-radius: 0;
861
- resize: none;
862
- font-size: 12px;
863
- line-height: 20px;
864
- outline: 0;
865
- }
866
-
867
- #itsec-settings-editor:empty {
868
- display: none;
869
- }
870
- .itsec-module-cards-container .bulkactions:empty {
871
- display: none;
872
- }
851
  body.security_page_itsec-logs #old-logs-migration-status p {
852
  line-height: 1.4em;
853
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/admin-pages/init.php CHANGED
@@ -16,7 +16,6 @@ final class ITSEC_Admin_Page_Loader {
16
  add_action( 'wp_ajax_itsec_settings_page', array( $this, 'handle_ajax_request' ) );
17
  add_action( 'wp_ajax_itsec_logs_page', array( $this, 'handle_ajax_request' ) );
18
  add_action( 'wp_ajax_itsec_help_page', array( $this, 'handle_ajax_request' ) );
19
- add_action( 'wp_ajax_itsec_debug_page', array( $this, 'handle_ajax_request' ) );
20
  add_action( 'wp_ajax_itsec-set-user-setting', array( $this, 'handle_user_setting' ) );
21
 
22
  // Filters for validating user settings
@@ -24,7 +23,7 @@ final class ITSEC_Admin_Page_Loader {
24
  }
25
 
26
  public function add_scripts() {
27
-
28
  }
29
 
30
  public function add_styles() {
@@ -47,10 +46,6 @@ final class ITSEC_Admin_Page_Loader {
47
  $page_refs[] = add_submenu_page( 'itsec', '', '<span style="color:#2EA2CC">' . __( 'Go Pro', 'better-wp-security' ) . '</span>', $capability, 'itsec-go-pro', array( $this, 'show_page' ) );
48
  }
49
 
50
- if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG ) {
51
- $page_refs[] = add_submenu_page( 'itsec', __( 'iThemes Security Debug', 'better-wp-security' ), __( 'Debug' ), $capability, 'itsec-debug', array( $this, 'show_page' ) );
52
- }
53
-
54
  foreach ( $page_refs as $page_ref ) {
55
  add_action( "load-$page_ref", array( $this, 'load' ) );
56
  }
16
  add_action( 'wp_ajax_itsec_settings_page', array( $this, 'handle_ajax_request' ) );
17
  add_action( 'wp_ajax_itsec_logs_page', array( $this, 'handle_ajax_request' ) );
18
  add_action( 'wp_ajax_itsec_help_page', array( $this, 'handle_ajax_request' ) );
 
19
  add_action( 'wp_ajax_itsec-set-user-setting', array( $this, 'handle_user_setting' ) );
20
 
21
  // Filters for validating user settings
23
  }
24
 
25
  public function add_scripts() {
26
+ ITSEC_Lib::enqueue_util();
27
  }
28
 
29
  public function add_styles() {
46
  $page_refs[] = add_submenu_page( 'itsec', '', '<span style="color:#2EA2CC">' . __( 'Go Pro', 'better-wp-security' ) . '</span>', $capability, 'itsec-go-pro', array( $this, 'show_page' ) );
47
  }
48
 
 
 
 
 
49
  foreach ( $page_refs as $page_ref ) {
50
  add_action( "load-$page_ref", array( $this, 'load' ) );
51
  }
core/admin-pages/js/debug.js DELETED
@@ -1,90 +0,0 @@
1
- (function ( $, itsecUtil, codeEditor ) {
2
- $( function () {
3
- var $messages = $( "#itsec-messages" );
4
-
5
- $( '#itsec-scheduler-events' ).on( 'click', '.button', function () {
6
-
7
- var $btn = $( this );
8
- $btn.prop( 'disabled', true );
9
-
10
- // We are purposely using attr() so as not to parse the data string as json.
11
- itsecUtil.sendAJAXRequest( '', 'run_event', { id: $btn.data( 'id' ), data: $btn.attr( 'data-data' ) }, function ( result ) {
12
-
13
- $btn.prop( 'disabled', false );
14
-
15
- if ( result.success ) {
16
- $( 'table', '#itsec-scheduler-events' ).replaceWith( result.response );
17
- }
18
-
19
- itsecUtil.displayNotices( result, $messages );
20
- } );
21
- } );
22
-
23
- $( document ).on( 'click', '#itsec-events-data-toggle', function () {
24
- $( '.itsec-events-data' ).toggleClass( 'hidden' );
25
- } );
26
-
27
- $( '#itsec-scheduler-reset' ).on( 'click', function () {
28
-
29
- var $btn = $( this );
30
- $btn.prop( 'disabled', true );
31
-
32
- itsecUtil.sendAJAXRequest( '', 'reset_scheduler', {}, function ( result ) {
33
-
34
- $btn.prop( 'disabled', false );
35
-
36
- if ( result.success ) {
37
- $( 'table', '#itsec-scheduler-events' ).replaceWith( result.response );
38
- }
39
-
40
- itsecUtil.displayNotices( result, $messages );
41
- } );
42
- } );
43
-
44
- var $saveBtn = $( '#itsec-settings-save' ), $loadBtn = $( "#itsec-settings-load" );
45
-
46
- $loadBtn.on( 'click', function () {
47
- $loadBtn.prop( 'disabled', true );
48
-
49
- itsecUtil.sendAJAXRequest( $( '#itsec-settings-module' ).val(), 'load_settings', {}, function ( result ) {
50
- itsecUtil.displayNotices( result, $messages );
51
-
52
- $loadBtn.prop( 'disabled', false );
53
- $saveBtn.prop( 'disabled', false );
54
- setEditorContent( JSON.stringify( result.response, null, 4 ) );
55
- } );
56
- } );
57
-
58
- $saveBtn.on( 'click', function () {
59
-
60
- $loadBtn.prop( 'disabled', true );
61
- $saveBtn.prop( 'disabled', true );
62
-
63
- itsecUtil.sendAJAXRequest( $( '#itsec-settings-module' ).val(), 'save_settings', getEditorContent(), function ( result ) {
64
- itsecUtil.displayNotices( result, $messages );
65
-
66
- $loadBtn.prop( 'disabled', false );
67
- $saveBtn.prop( 'disabled', false );
68
-
69
- if ( result.success ) {
70
- setEditorContent( JSON.stringify( result.response, null, 4 ) );
71
- }
72
- } );
73
- } );
74
-
75
- var $editor = $( "#itsec-settings-editor" ), editor;
76
-
77
- function setEditorContent( content ) {
78
- if ( codeEditor ) {
79
- if ( !editor ) editor = codeEditor.initialize( $editor );
80
- editor.codemirror.setValue( content );
81
- } else {
82
- $editor.val( content );
83
- }
84
- }
85
-
86
- function getEditorContent() {
87
- return editor ? editor.codemirror.getValue() : $editor.val();
88
- }
89
- } );
90
- })( jQuery, window.itsecUtil, wp.codeEditor );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/admin-pages/js/script.js CHANGED
@@ -980,38 +980,34 @@ var itsecSettingsPage = {
980
  },
981
 
982
  // Make notices dismissible
983
- makeNoticesDismissible: function() {
984
- jQuery( '.notice.itsec-is-dismissible' ).each( function() {
985
- var $el = jQuery( this ),
986
- $button = jQuery( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
987
- btnText = itsec_page.translations.dismiss || '';
988
-
989
- // Don't rebind twice
990
- if ( jQuery( '.notice-dismiss', $el ).length ) {
991
- return;
992
- }
993
 
994
- // Ensure plain text
995
- $button.find( '.screen-reader-text' ).text( btnText );
996
- $button.on( 'click.wp-dismiss-notice', function( event ) {
997
- event.preventDefault();
998
 
999
- $el.trigger( 'itsec-dismiss-notice' );
1000
 
1001
- $el.fadeTo( 100, 0, function() {
1002
- $el.slideUp( 100, function() {
1003
- $el.remove();
1004
- });
1005
  });
1006
  });
1007
-
1008
- $el.append( $button );
1009
  });
1010
- },
1011
 
1012
- refreshPage: function() {
1013
- location.reload( true );
1014
- }
1015
  };
1016
 
1017
  jQuery(document).ready(function( $ ) {
980
  },
981
 
982
  // Make notices dismissible
983
+ makeNoticesDismissible: function(){
984
+ jQuery( '.notice.itsec-is-dismissible' ).each( function() {
985
+ var $el = jQuery( this ),
986
+ $button = jQuery( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
987
+ btnText = itsec_page.translations.dismiss || '';
988
+
989
+ // Don't rebind twice
990
+ if ( jQuery( '.notice-dismiss', $el ).length ) {
991
+ return;
992
+ }
993
 
994
+ // Ensure plain text
995
+ $button.find( '.screen-reader-text' ).text( btnText );
996
+ $button.on( 'click.wp-dismiss-notice', function( event ) {
997
+ event.preventDefault();
998
 
999
+ $el.trigger( 'itsec-dismiss-notice' );
1000
 
1001
+ $el.fadeTo( 100, 0, function() {
1002
+ $el.slideUp( 100, function() {
1003
+ $el.remove();
 
1004
  });
1005
  });
 
 
1006
  });
 
1007
 
1008
+ $el.append( $button );
1009
+ });
1010
+ }
1011
  };
1012
 
1013
  jQuery(document).ready(function( $ ) {
core/admin-pages/js/settings.js CHANGED
@@ -824,38 +824,34 @@ var itsecSettingsPage = {
824
  },
825
 
826
  // Make notices dismissible
827
- makeNoticesDismissible: function() {
828
- jQuery( '.notice.itsec-is-dismissible' ).each( function() {
829
- var $el = jQuery( this ),
830
- $button = jQuery( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
831
- btnText = itsec_page.translations.dismiss || '';
832
-
833
- // Don't rebind twice
834
- if ( jQuery( '.notice-dismiss', $el ).length ) {
835
- return;
836
- }
837
 
838
- // Ensure plain text
839
- $button.find( '.screen-reader-text' ).text( btnText );
840
- $button.on( 'click.wp-dismiss-notice', function( event ) {
841
- event.preventDefault();
842
 
843
- $el.trigger( 'itsec-dismiss-notice' );
844
 
845
- $el.fadeTo( 100, 0, function() {
846
- $el.slideUp( 100, function() {
847
- $el.remove();
848
- });
849
  });
850
  });
851
-
852
- $el.append( $button );
853
  });
854
- },
855
 
856
- refreshPage: function() {
857
- location.reload( true );
858
- }
859
  };
860
 
861
  jQuery(document).ready(function( $ ) {
824
  },
825
 
826
  // Make notices dismissible
827
+ makeNoticesDismissible: function(){
828
+ jQuery( '.notice.itsec-is-dismissible' ).each( function() {
829
+ var $el = jQuery( this ),
830
+ $button = jQuery( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
831
+ btnText = itsec_page.translations.dismiss || '';
832
+
833
+ // Don't rebind twice
834
+ if ( jQuery( '.notice-dismiss', $el ).length ) {
835
+ return;
836
+ }
837
 
838
+ // Ensure plain text
839
+ $button.find( '.screen-reader-text' ).text( btnText );
840
+ $button.on( 'click.wp-dismiss-notice', function( event ) {
841
+ event.preventDefault();
842
 
843
+ $el.trigger( 'itsec-dismiss-notice' );
844
 
845
+ $el.fadeTo( 100, 0, function() {
846
+ $el.slideUp( 100, function() {
847
+ $el.remove();
 
848
  });
849
  });
 
 
850
  });
 
851
 
852
+ $el.append( $button );
853
+ });
854
+ }
855
  };
856
 
857
  jQuery(document).ready(function( $ ) {
core/admin-pages/js/util.js CHANGED
@@ -166,42 +166,6 @@ var itsecUtil = {
166
  }
167
  // If the requested parameter doesn't exist, return false
168
  return false;
169
- },
170
-
171
- buildNotices: function ( response, asAlt ) {
172
- var notices = [],
173
- types = ['error', 'warning', 'message', 'info'];
174
-
175
- for ( var i = 0; i < types.length; i++ ) {
176
- for ( var j = 0; j < response[types[i] + 's'].length; j++ ) {
177
- notices.push( itsecUtil.makeNotice( response[types[i] + 's'][j], types[i], asAlt ) );
178
- }
179
- }
180
-
181
- return notices;
182
- },
183
-
184
- makeNotice: function ( message, type, asAlt ) {
185
- type = type === 'message' ? 'success' : type;
186
 
187
- var className = 'notice notice-' + type;
188
-
189
- if ( asAlt ) {
190
- className += ' notice-alt';
191
- }
192
-
193
- return jQuery( '<div>', { class: className } )
194
- .append( jQuery( '<p>', { html: message } ) );
195
- },
196
-
197
- displayNotices: function ( response, $container, asAlt ) {
198
- var notices = itsecUtil.buildNotices( response, asAlt );
199
-
200
- for ( var i = 0; i < notices.length; i++ ) {
201
- (function ( $notice ) {
202
- $container.append( $notice );
203
- setTimeout( function () {$notice.remove();}, 10000 );
204
- })( notices[i].clone() );
205
- }
206
- },
207
  };
166
  }
167
  // If the requested parameter doesn't exist, return false
168
  return false;
169
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  };
core/admin-pages/logs-list-table.php CHANGED
@@ -110,17 +110,12 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
110
  return;
111
  }
112
 
113
- if ( false === strpos( $item['code'], '::' ) ) {
114
- $code = $item['code'];
115
- $data = array();
116
  } else {
117
- list( $code, $data ) = explode( '::', $item['code'], 2 );
118
- $data = explode( ',', $data );
119
  }
120
 
121
- $vars = apply_filters( "itsec_logs_prepare_{$item['module']}_filter_row_action_for_{$column_name}", array( 'filters' => "{$column_name}|{$item[ $column_name ]}"), $item, $code, $data );
122
- $url = $this->get_self_link( $vars );
123
-
124
  $out = '&nbsp;<a class="dashicons dashicons-filter" href="' . esc_url( $url ) . '" title="' . sprintf( esc_attr__( 'Show only entries for this %s', 'better-wp-security' ), strtolower( $column_header ) ) . '">&nbsp;</a>';
125
 
126
  if ( 'module' === $column_name ) {
@@ -273,18 +268,9 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
273
  $hidden_fields = array( 'id' );
274
  $sortable_columns = $this->get_sortable_columns();
275
 
276
- if ( isset( $_GET['orderby'], $_GET['order'] ) && is_string( $_GET['orderby'] ) && is_string( $_GET['order'] ) ) {
277
- if ( preg_match( '/^[a-z_]+$/', $_GET['orderby'] ) ) {
278
- $sort_by_column = $_GET['orderby'];
279
- } else {
280
- $sort_by_column = 'timestamp';
281
- }
282
-
283
- if ( in_array( strtoupper( $_GET['order'] ), array( 'DESC', 'ASC' ) ) ) {
284
- $sort_direction = strtoupper( $_GET['order'] );
285
- } else {
286
- $sort_direction = 'DESC';
287
- }
288
  } else {
289
  $sort_by_column = 'timestamp';
290
  $sort_direction = 'DESC';
@@ -377,7 +363,7 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
377
  $current = $this->get_current_view();
378
 
379
  foreach ( $views as $type => $description ) {
380
- $url = $this->get_self_link( array( 'filters' => "type|$type" ), array() );
381
 
382
  if ( $current === $type ) {
383
  $description = '<a href="' . esc_url( $url ) . '" class="current" aria-current="page">' . $description . '</a>';
@@ -414,79 +400,28 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
414
  }
415
 
416
  protected function extra_tablenav( $which ) {
 
417
 
418
- $filters = $this->get_raw_filters();
419
- $current = isset( $filters['module'] ) ? $filters['module'] : '';
420
-
421
- ?>
422
- <div class="alignleft actions">
423
- <?php if ( 'top' === $which ): ?>
424
- <label for="itsec-module-filter" class="screen-reader-text"><?php esc_html_e( 'Filter by Module', 'better-wp-security' ) ?></label>
425
- <select name="filters[]" id="itsec-module-filter">
426
- <option value=""><?php esc_html_e( 'All Modules', 'better-wp-security' ); ?></option>
427
- <?php foreach ( $this->get_modules() as $module => $label ): ?>
428
- <option value="module|<?php echo esc_attr( $module ) ?>" <?php selected( $module, $current ); ?>>
429
- <?php echo $label; // Expected to be escaped by modules. ?>
430
- </option>
431
- <?php endforeach; ?>
432
- </select>
433
-
434
- <?php submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'itsec-logs-query-submit' ) ); ?>
435
-
436
- <?php if ( isset( $filters['type'] ) ): ?>
437
- <input type="hidden" name="filters[]" value="type|<?php echo esc_attr( $filters['type'] ); ?>">
438
- <?php endif; ?>
439
- <?php endif; ?>
440
- </div>
441
- <?php
442
- }
443
-
444
- public function no_items() {
445
- esc_html_e( 'No events.', 'better-wp-security' );
446
- }
447
-
448
- private function get_modules() {
449
- $columns = implode(', ', array(
450
- 'id',
451
- 'parent_id',
452
- 'module',
453
- 'type',
454
- 'code',
455
- 'timestamp',
456
- 'init_timestamp',
457
- 'remote_ip',
458
- 'user_id',
459
- 'url',
460
- 'memory_current',
461
- 'memory_peak',
462
- ) );
463
-
464
- global $wpdb;
465
 
466
- $items = $wpdb->get_results( "SELECT {$columns} FROM {$wpdb->prefix}itsec_logs GROUP BY `module`", ARRAY_A );
467
 
468
- if ( ! is_array( $items ) ) {
469
- return array();
 
 
470
  }
471
 
472
- $modules = array();
473
-
474
- foreach ( $items as $item ) {
475
- if ( false === strpos( $item['code'], '::' ) ) {
476
- $code = $item['code'];
477
- $data = array();
478
- } else {
479
- list( $code, $data ) = explode( '::', $item['code'], 2 );
480
- $data = explode( ',', $data );
481
- }
482
 
483
- $item['description'] = $item['code'];
484
- $item['module_display'] = $item['module'];
485
- $item = apply_filters( "itsec_logs_prepare_{$item['module']}_entry_for_list_display", $item, $code, $data );
486
-
487
- $modules[ $item['module'] ] = $item['module_display'];
488
- }
489
 
490
- return $modules;
 
491
  }
492
  }
110
  return;
111
  }
112
 
113
+ if ( 'four_oh_four' === $item['module'] && 'code' === $column_name ) {
114
+ $url = self::get_self_link( array( 'filters[10]' => "url|{$item['url']}", 'filters[11]' => 'module|four_oh_four' ) );
 
115
  } else {
116
+ $url = self::get_self_link( array( 'filters' => "$column_name|{$item[$column_name]}" ) );
 
117
  }
118
 
 
 
 
119
  $out = '&nbsp;<a class="dashicons dashicons-filter" href="' . esc_url( $url ) . '" title="' . sprintf( esc_attr__( 'Show only entries for this %s', 'better-wp-security' ), strtolower( $column_header ) ) . '">&nbsp;</a>';
120
 
121
  if ( 'module' === $column_name ) {
268
  $hidden_fields = array( 'id' );
269
  $sortable_columns = $this->get_sortable_columns();
270
 
271
+ if ( isset( $_GET['orderby'], $_GET['order'] ) ) {
272
+ $sort_by_column = $_GET['orderby'];
273
+ $sort_direction = $_GET['order'];
 
 
 
 
 
 
 
 
 
274
  } else {
275
  $sort_by_column = 'timestamp';
276
  $sort_direction = 'DESC';
363
  $current = $this->get_current_view();
364
 
365
  foreach ( $views as $type => $description ) {
366
+ $url = self::get_self_link( array( 'filters' => "type|$type" ), array() );
367
 
368
  if ( $current === $type ) {
369
  $description = '<a href="' . esc_url( $url ) . '" class="current" aria-current="page">' . $description . '</a>';
400
  }
401
 
402
  protected function extra_tablenav( $which ) {
403
+ echo '<div class="alignleft actions">';
404
 
405
+ if ( 'top' === $which ) {
406
+ /*
407
+ ob_start();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
+ $output = ob_get_clean();
410
 
411
+ if ( ! empty( $output ) ) {
412
+ echo $output;
413
+ submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
414
+ }*/
415
  }
416
 
417
+ /* if ( $this->is_trash && current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_others_posts ) && $this->has_items() ) {
418
+ submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false );
419
+ }*/
 
 
 
 
 
 
 
420
 
421
+ echo '</div>';
422
+ }
 
 
 
 
423
 
424
+ public function no_items() {
425
+ esc_html_e( 'No events.', 'better-wp-security' );
426
  }
427
  }
core/admin-pages/page-debug.php DELETED
@@ -1,425 +0,0 @@
1
- <?php
2
-
3
- final class ITSEC_Debug_Page {
4
-
5
- /** @var string */
6
- private $self_url;
7
-
8
- public function __construct() {
9
- add_action( 'itsec-page-show', array( $this, 'handle_page_load' ) );
10
- add_action( 'itsec-page-ajax', array( $this, 'handle_ajax_request' ) );
11
- add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
12
- add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
13
-
14
- ITSEC_Modules::load_module_file( 'debug.php', ':active' );
15
- }
16
-
17
- public function handle_page_load( $self_url ) {
18
- $this->self_url = $self_url;
19
-
20
- $this->show_settings_page();
21
- }
22
-
23
- public function add_scripts() {
24
-
25
- $deps = array( 'itsec-util' );
26
-
27
- if ( function_exists( 'wp_enqueue_code_editor' ) ) {
28
- $deps[] = 'code-editor';
29
- wp_enqueue_code_editor( array(
30
- 'type' => 'application/json'
31
- ) );
32
- }
33
-
34
- ITSEC_Lib::enqueue_util( array( 'action' => 'itsec_debug_page', 'nonce' => 'itsec-debug-page' ) );
35
- wp_enqueue_script( 'itsec-debug', plugins_url( 'js/debug.js', __FILE__ ), $deps, ITSEC_Core::get_plugin_build() );
36
-
37
- do_action( 'itsec_debug_page_enqueue' );
38
- }
39
-
40
- public function add_styles() {
41
- wp_enqueue_style( 'itsec-debug-page-style', plugins_url( 'css/style.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
42
- }
43
-
44
- public function handle_ajax_request() {
45
- if ( WP_DEBUG ) {
46
- ini_set( 'display_errors', 1 );
47
- }
48
-
49
- ITSEC_Core::set_interactive( true );
50
-
51
- $method = ( isset( $_POST['method'] ) && is_string( $_POST['method'] ) ) ? $_POST['method'] : '';
52
- $module = ( isset( $_POST['module'] ) && is_string( $_POST['module'] ) ) ? $_POST['module'] : '';
53
-
54
- if ( empty( $GLOBALS['hook_suffix'] ) ) {
55
- $GLOBALS['hook_suffix'] = 'security_page_itsec-debug';
56
- }
57
-
58
- if ( false === check_ajax_referer( 'itsec-debug-page', 'nonce', false ) ) {
59
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-failed-nonce', __( 'A nonce security check failed, preventing the request from completing as expected. Please try reloading the page and trying again.', 'better-wp-security' ) ) );
60
- } elseif ( ! ITSEC_Core::current_user_can_manage() ) {
61
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-insufficient-privileges', __( 'A permissions security check failed, preventing the request from completing as expected. The currently logged in user does not have sufficient permissions to make this request. Please try reloading the page and trying again.', 'better-wp-security' ) ) );
62
- } elseif ( empty( $method ) ) {
63
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-missing-method', __( 'The server did not receive a valid request. The required "method" argument is missing. Please try again.', 'better-wp-security' ) ) );
64
- } elseif ( 'handle_module_request' === $method && empty( $module ) ) {
65
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-missing-module', __( 'The server did not receive a valid request. The required "module" argument is missing. Please try again.', 'better-wp-security' ) ) );
66
- } elseif ( 'handle_module_request' === $method ) {
67
- if ( isset( $_POST['data'] ) ) {
68
- ITSEC_Modules::load_module_file( 'debug.php', ':active' );
69
- /**
70
- * Fires when an ajax request is being made to a module.
71
- *
72
- * At some point this will probably be replaced by a more thought-out framework, but this hook will probably power it.
73
- *
74
- * The dynamic portion of this hook, {$module}, refers to the module name. For example, 'notification-center'.
75
- *
76
- * @param array $data
77
- */
78
- do_action( "itsec_debug_module_request_{$module}", $_POST['data'] );
79
- } else {
80
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-module-request-missing-data', __( 'The server did not receive a valid request. The required "data" argument for the module is missing. Please try again.', 'better-wp-security' ) ) );
81
- }
82
- } elseif ( 'reset_scheduler' === $method ) {
83
- ITSEC_Core::get_scheduler()->uninstall();
84
- ITSEC_Core::get_scheduler()->register_events();
85
- ITSEC_Response::set_response( $this->get_events_table() );
86
- ITSEC_Response::set_success( true );
87
- ITSEC_Response::add_message( __( 'Scheduler reset.', 'better-wp-security' ) );
88
- } elseif ( 'run_event' === $method ) {
89
- if ( empty( $_POST['data']['id'] ) ) {
90
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-run-event-missing-id', __( 'The server did not receive a valid request. The required "data.id" argument for the "run_event" method is missing.', 'better-wp-security' ) ) );
91
- } elseif ( ! empty( $_POST['data']['data'] ) ) {
92
- $hash = $_POST['data']['data'];
93
-
94
- if ( ! is_string( $hash ) ) {
95
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-run-event-invalid-data', __( 'The server did not receive a valid request. The "data.data" argument for the "run_event" method is an invalid string.', 'better-wp-security' ) ) );
96
- } else {
97
- ITSEC_Core::get_scheduler()->run_single_event_by_hash( $_POST['data']['id'], $hash );
98
- ITSEC_Response::set_response( $this->get_events_table() );
99
- ITSEC_Response::set_success( true );
100
- ITSEC_Response::add_message( __( 'Event successfully run.', 'better-wp-security' ) );
101
- }
102
- } else {
103
- ITSEC_Core::get_scheduler()->run_recurring_event( $_POST['data']['id'] );
104
- ITSEC_Response::set_response( $this->get_events_table() );
105
- ITSEC_Response::set_success( true );
106
- ITSEC_Response::add_message( __( 'Event successfully run.', 'better-wp-security' ) );
107
- }
108
- } elseif ( 'load_settings' === $method ) {
109
- ITSEC_Response::set_response( ITSEC_Modules::get_settings( $module ) );
110
- } elseif ( 'save_settings' === $method ) {
111
- $data = json_decode( wp_unslash( $_POST['data'] ), true );
112
-
113
- if ( ! is_array( $data ) ) {
114
- ITSEC_Response::set_success( false );
115
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-run-event-invalid-data', __( 'The server did not receive a valid request. The "data" argument for the "save_settings" method is invalid.', 'better-wp-security' ) ) );
116
- } else {
117
- $result = ITSEC_Modules::set_settings( $module, $data );
118
-
119
- if ( is_wp_error( $result ) ) {
120
- ITSEC_Response::set_success( false );
121
- ITSEC_Response::add_error( $result );
122
- } else {
123
- ITSEC_Response::set_response( ITSEC_Modules::get_settings( $module ) );
124
-
125
- if ( $result['saved'] ) {
126
- ITSEC_Response::add_message( esc_html__( 'Module settings updated.', 'better-wp-security' ) );
127
- }
128
- }
129
- }
130
- } else {
131
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-unknown-method', __( 'The server did not receive a valid request. An unknown "method" argument was supplied. Please try again.', 'better-wp-security' ) ) );
132
- }
133
-
134
- ITSEC_Response::send_json();
135
- }
136
-
137
- private function show_settings_page() {
138
-
139
- $sysinfo = $this->get_sysinfo();
140
-
141
- $out = '';
142
-
143
- foreach ( $sysinfo as $category => $info ) {
144
- if ( $out ) {
145
- $out .= "\r\n";
146
- }
147
-
148
- $out .= "### {$category} ###\r\n";
149
-
150
- foreach ( $info as $label => $value ) {
151
- $out .= "{$label}: {$value}\r\n";
152
- }
153
- }
154
-
155
- $out = rtrim( $out );
156
-
157
- $scheduler = ITSEC_Core::get_scheduler();
158
-
159
- $modules = array();
160
-
161
- foreach ( ITSEC_Modules::get_available_modules() as $module ) {
162
- if ( ITSEC_Modules::get_settings_obj( $module ) ) {
163
- $modules[ $module ] = $module;
164
- }
165
- }
166
-
167
- sort( $modules );
168
- ?>
169
- <div class="wrap">
170
- <h1>
171
- <?php _e( 'iThemes Security', 'better-wp-security' ); ?>
172
- <a href="<?php echo esc_url( ITSEC_Core::get_settings_page_url() ); ?>" class="page-title-action"><?php _e( 'Manage Settings', 'better-wp-security' ); ?></a>
173
- <a href="<?php echo esc_url( apply_filters( 'itsec_support_url', 'https://wordpress.org/support/plugin/better-wp-security' ) ); ?>" class="page-title-action">
174
- <?php _e( 'Support', 'better-wp-security' ); ?>
175
- </a>
176
- </h1>
177
-
178
- <div id="itsec-messages"></div>
179
-
180
- <div>
181
- <h2><?php esc_html_e( 'System Info', 'better-wp-security' ); ?></h2>
182
- <label for="itsec-system-info"><?php esc_html__( 'System Info Summary', 'better-wp-security' ); ?></label>
183
- <textarea readonly id="itsec-system-info"><?php echo esc_textarea( $out ); ?></textarea>
184
- </div>
185
-
186
- <div>
187
- <h2><?php esc_html_e( 'Settings', 'better-wp-security' ); ?></h2>
188
- <p>
189
- <label for="itsec-settings-module" class="screen-reader-text"><?php esc_html_e( 'Module', 'better-wp-security' ); ?></label>
190
- <select id="itsec-settings-module">
191
- <?php foreach ( $modules as $module ) : ?>
192
- <option value="<?php echo esc_attr( $module ); ?>"><?php echo esc_html( $module ); ?></option>
193
- <?php endforeach; ?>
194
- </select>
195
- <button class="button" id="itsec-settings-load"><?php esc_html_e( 'Load', 'better-wp-security' ); ?></button>
196
- <button class="button" id="itsec-settings-save" disabled><?php esc_html_e( 'Save', 'better-wp-security' ) ?></button>
197
- </p>
198
- <label for="itsec-settings-editor" class="screen-reader-text"><?php esc_html_e( 'Edit Settings', 'better-wp-security' ); ?></label>
199
- <textarea id="itsec-settings-editor"></textarea>
200
- </div>
201
-
202
- <div id="itsec-scheduler-events">
203
- <h2><?php esc_html_e( 'Scheduler', 'better-wp-security' ); ?></h2>
204
- <?php echo $this->get_events_table(); ?>
205
- <p style="text-align: right;">
206
- <code><?php echo get_class( $scheduler ); ?></code>
207
- <button class="button" id="itsec-scheduler-reset"><?php esc_html_e( 'Reset', 'better-wp-security' ) ?></button>
208
- </p>
209
- </div>
210
-
211
- <?php do_action( 'itsec_debug_page' ); ?>
212
- </div>
213
- <?php
214
- }
215
-
216
- private function get_events_table() {
217
- $scheduler = ITSEC_Core::get_scheduler();
218
- ob_start();
219
-
220
- ?>
221
-
222
- <table class="widefat striped">
223
- <thead>
224
- <tr>
225
- <th><?php esc_html_e( 'ID', 'better-wp-security' ) ?></th>
226
- <th><?php esc_html_e( 'Fire At', 'better-wp-security' ) ?></th>
227
- <th><?php esc_html_e( 'Schedule', 'better-wp-security' ) ?></th>
228
- <th><button class="button-link" id="itsec-events-data-toggle"><?php esc_html_e( 'Data', 'better-wp-security' ) ?></button></th>
229
- <th></th>
230
- </tr>
231
- </thead>
232
- <tbody>
233
- <?php foreach ( array_merge( $scheduler->get_recurring_events(), $scheduler->get_single_events() ) as $event ) : ?>
234
- <tr>
235
- <td><?php echo esc_html( $event['id'] ); ?></td>
236
- <td><?php echo date( 'Y-m-d H:i:s', $event['fire_at'] ); ?> (<?php echo esc_html( human_time_diff( $event['fire_at'] ) ) ?>)</td>
237
- <td><?php echo isset( $event['schedule'] ) ? $event['schedule'] : '–'; ?></td>
238
- <td><div class="hidden itsec-events-data"><?php $event['data'] ? ITSEC_Lib::print_r( $event['data'] ) : print( '–' ); ?></div></td>
239
- <td>
240
- <button class="button" data-id="<?php echo esc_attr( $event['id'] ); ?>"
241
- data-data="<?php echo isset( $event['schedule'] ) ? '' : esc_attr( $event['hash'] ); ?>">
242
- <?php esc_html_e( 'Run', 'better-wp-security' ) ?>
243
- </button>
244
- </td>
245
- </tr>
246
- <?php endforeach; ?>
247
- </tbody>
248
- </table>
249
-
250
- <?php
251
-
252
- return ob_get_clean();
253
- }
254
-
255
- private function get_sysinfo() {
256
-
257
- /** @var $wpdb wpdb */
258
- global $wpdb;
259
-
260
- $info = array();
261
-
262
- $info['Site Info'] = array(
263
- 'Site URL' => site_url(),
264
- 'Home URL' => home_url(),
265
- 'Multisite' => is_multisite() ? 'Yes' : 'No'
266
- );
267
-
268
- $wp_config = array(
269
- 'Version' => get_bloginfo( 'version' ),
270
- 'Language' => defined( 'WPLANG' ) && WPLANG ? WPLANG : 'en_US',
271
- 'Permalink' => get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default',
272
- 'Theme' => wp_get_theme()->Name . ' ' . wp_get_theme()->Version,
273
- 'Show on Front' => get_option( 'show_on_front' )
274
- );
275
-
276
- if ( get_option( 'show_on_front' ) === 'page' ) {
277
- $front_page_id = get_option( 'page_on_front' );
278
- $blog_page_id = get_option( 'page_for_posts' );
279
-
280
- $wp_config['Page On Front'] = $front_page_id ? get_the_title( $front_page_id ) . " (#$front_page_id)" : 'Unset';
281
- $wp_config['Page For Posts'] = $blog_page_id ? get_the_title( $blog_page_id ) . " (#$blog_page_id)" : 'Unset';
282
- }
283
-
284
- $wp_config['ABSPATH'] = ABSPATH;
285
- $wp_config['Table Prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'Too long' : 'Acceptable' );
286
- $wp_config['WP_DEBUG'] = defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
287
- $wp_config['WP_DEBUG_LOG'] = defined( 'WP_DEBUG_LOG' ) ? WP_DEBUG_LOG ? 'Enabled' : 'Disabled' : 'Not set';
288
- $wp_config['SCRIPT_DEBUG'] = defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
289
- $wp_config['Object Cache'] = wp_using_ext_object_cache() ? 'Yes' : 'No';
290
- $wp_config['Memory Limit'] = WP_MEMORY_LIMIT;
291
- $info['WordPress Configuration'] = $wp_config;
292
-
293
- $defines = array(
294
- 'ITSEC_USE_CRON',
295
- 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS',
296
- 'ITSEC_DEVELOPMENT',
297
- 'ITSEC_DISABLE_MODULES',
298
- 'ITSEC_DISABLE_TWO_FACTOR',
299
- 'ITSEC_DISABLE_CRON_TEST',
300
- 'ITSEC_SERVER_OVERRIDE',
301
- 'ITSEC_DOING_FILE_CHECK',
302
- 'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE',
303
- 'ITSEC_TEST_MALWARE_SCAN_SITE_URL',
304
- 'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSL_VERIFY',
305
- 'ITSEC_SUCURI_KEY',
306
- 'ITSEC_NOTIFY_USE_CRON',
307
- 'ITSEC_DISABLE_SECURITY_CHECK_PRO',
308
- 'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION',
309
- 'ITSEC_DISABLE_PASSWORD_STRENGTH',
310
- 'ITSEC_DISABLE_INACTIVE_USER_CHECK',
311
- );
312
-
313
- $info['iThemes Security'] = array(
314
- 'Build' => ITSEC_Core::get_plugin_build(),
315
- 'Pro' => ITSEC_Core::is_pro(),
316
- 'Modules' => wp_sprintf( '%l', ITSEC_Modules::get_active_modules() ),
317
- 'Cron' => ITSEC_Lib::use_cron(),
318
- 'Cron Status' => ITSEC_Lib::is_cron_working(),
319
- 'Scheduler' => get_class( ITSEC_Core::get_scheduler() ),
320
- );
321
-
322
- foreach ( $defines as $define ) {
323
- if ( defined( $define ) ) {
324
- $value = constant( $define );
325
- $info['iThemes Security'][ $define ] = $value === true ? 'Enabled' : $value === false ? 'Disabled' : $value;
326
- }
327
- }
328
-
329
- $plugins = get_plugins();
330
- $active_plugins = get_option( 'active_plugins', array() );
331
-
332
- foreach ( $plugins as $plugin_path => $plugin ) {
333
-
334
- if ( ! in_array( $plugin_path, $active_plugins, true ) ) {
335
- continue;
336
- }
337
-
338
- $info['Active Plugins'][ $plugin['Name'] ] = $plugin['Version'];
339
- }
340
-
341
- foreach ( get_mu_plugins() as $plugin ) {
342
- $info['MU Plugins'][ $plugin['Name'] ] = $plugin['Version'];
343
- }
344
-
345
- if ( is_multisite() ) {
346
- $plugins = wp_get_active_network_plugins();
347
- $active_plugins = get_site_option( 'active_sitewide_plugins', array() );
348
-
349
- foreach ( $plugins as $plugin_path ) {
350
-
351
- $plugin_base = plugin_basename( $plugin_path );
352
-
353
- if ( ! array_key_exists( $plugin_base, $active_plugins ) ) {
354
- continue;
355
- }
356
-
357
- $plugin = get_plugin_data( $plugin_path );
358
-
359
- $info['Network Active Plugins'][ $plugin['Name'] ] = $plugin['Version'];
360
- }
361
- }
362
-
363
- $info['Webserver Configuration'] = array(
364
- 'PHP Version' => PHP_VERSION,
365
- 'MySQL Version' => $wpdb->db_version(),
366
- 'Use MySQLi' => $wpdb->use_mysqli ? 'Yes' : 'No',
367
- 'Webserver Info' => ITSEC_Lib::get_server(),
368
- 'Host' => $this->get_host(),
369
- );
370
-
371
- $info['PHP Configuration'] = array(
372
- 'Safe Mode' => ini_get( 'safe_mode' ) ? 'Enabled' : 'Disabled',
373
- 'Memory Limit' => ini_get( 'memory_limit' ),
374
- 'Upload Max Size' => ini_get( 'upload_max_filesize' ),
375
- 'Post Max Size' => ini_get( 'post_max_size' ),
376
- 'Upload Max Filesize' => ini_get( 'upload_max_filesize' ),
377
- 'Time Limit' => ini_get( 'max_execution_time' ),
378
- 'Max Input Vars' => ini_get( 'max_input_vars' ),
379
- 'Display Errors' => ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A'
380
- );
381
-
382
- $info['PHP Extensions'] = array(
383
- 'cURL' => function_exists( 'curl_init' ) ? 'Supported' : 'Not Supported',
384
- 'fsockopen' => function_exists( 'fsockopen' ) ? 'Supported' : 'Not Supported',
385
- 'SOAP Client' => class_exists( 'SoapClient' ) ? 'Installed' : 'Not Installed',
386
- 'Suhosin' => extension_loaded( 'suhosin' ) ? 'Installed' : 'Not Installed'
387
- );
388
-
389
- return $info;
390
- }
391
-
392
- private function get_host() {
393
-
394
- if ( defined( 'WPE_APIKEY' ) ) {
395
- $host = 'WP Engine';
396
- } elseif ( defined( 'PAGELYBIN' ) ) {
397
- $host = 'Pagely';
398
- } elseif ( DB_HOST === 'localhost:/tmp/mysql5.sock' ) {
399
- $host = 'ICDSoft';
400
- } elseif ( DB_HOST === 'mysqlv5' ) {
401
- $host = 'NetworkSolutions';
402
- } elseif ( strpos( DB_HOST, 'ipagemysql.com' ) !== false ) {
403
- $host = 'iPage';
404
- } elseif ( strpos( DB_HOST, 'ipowermysql.com' ) !== false ) {
405
- $host = 'IPower';
406
- } elseif ( strpos( DB_HOST, '.gridserver.com' ) !== false ) {
407
- $host = 'MediaTemple Grid';
408
- } elseif ( strpos( DB_HOST, '.pair.com' ) !== false ) {
409
- $host = 'pair Networks';
410
- } elseif ( strpos( DB_HOST, '.stabletransit.com' ) !== false ) {
411
- $host = 'Rackspace Cloud';
412
- } elseif ( strpos( DB_HOST, '.sysfix.eu' ) !== false ) {
413
- $host = 'SysFix.eu Power Hosting';
414
- } elseif ( isset( $_SERVER['SERVER_NAME'] ) && strpos( $_SERVER['SERVER_NAME'], 'Flywheel' ) !== false ) {
415
- $host = 'Flywheel';
416
- } else {
417
- // Adding a general fallback for data gathering
418
- $host = 'DBH/' . DB_HOST . ', SRV/' . ( isset( $_SERVER['SERVER_NAME'] ) ? $_SERVER['SERVER_NAME'] : '' );
419
- }
420
-
421
- return $host;
422
- }
423
- }
424
-
425
- new ITSEC_Debug_Page();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/admin-pages/page-logs.php CHANGED
@@ -37,8 +37,6 @@ final class ITSEC_Logs_Page {
37
  }
38
 
39
  public function add_scripts() {
40
- ITSEC_Lib::enqueue_util();
41
-
42
  foreach ( $this->modules as $id => $module ) {
43
  $module->enqueue_scripts_and_styles();
44
  }
@@ -274,42 +272,34 @@ final class ITSEC_Logs_Page {
274
  'module' => array(
275
  'header' => esc_html__( 'Module', 'better-wp-security' ),
276
  'content' => esc_html( $entry['module'] ),
277
- 'order' => 0,
278
  ),
279
  'type' => array(
280
  'header' => esc_html__( 'Type', 'better-wp-security' ),
281
  'content' => $type,
282
- 'order' => 10,
283
  ),
284
  'description' => array(
285
  'header' => esc_html__( 'Description', 'better-wp-security' ),
286
  'content' => esc_html( $code ),
287
- 'order' => 20,
288
  ),
289
  'timestamp' => array(
290
  'header' => esc_html__( 'Timestamp', 'better-wp-security' ),
291
  'content' => esc_html( $datetime ),
292
- 'order' => 30,
293
  ),
294
  'host' => array(
295
  'header' => esc_html__( 'Host', 'better-wp-security' ),
296
  'content' => '<code>' . esc_html( $entry['remote_ip'] ) . '</code>',
297
- 'order' => 40,
298
  ),
299
  'user' => array(
300
  'header' => esc_html__( 'User', 'better-wp-security' ),
301
  'content' => esc_html( $username ),
302
- 'order' => 50,
303
  ),
304
  'url' => array(
305
  'header' => esc_html__( 'URL', 'better-wp-security' ),
306
  'content' => '<code>' . $url . '</code>',
307
- 'order' => 60,
308
  ),
309
  'raw-details' => array(
310
  'header' => esc_html__( 'Raw Details', 'better-wp-security' ),
311
  'content' => true,
312
- 'order' => PHP_INT_MAX,
313
  ),
314
  );
315
 
@@ -337,17 +327,6 @@ final class ITSEC_Logs_Page {
337
 
338
  $details['raw-details']['content'] = '<p><a class="itsec-log-raw-details-toggle" href="#">' . $this->translations['show_raw_details'] . '</a></p><div class="itsec-log-raw-details">' . $details['raw-details']['content'] . '</div>';
339
  }
340
-
341
- $i = 1;
342
-
343
- foreach ( $details as $column => $detail ) {
344
- if ( ! isset( $detail['order'] ) ) {
345
- $details[ $column ]['order'] = PHP_INT_MAX - 10 * $i;
346
- $i ++;
347
- }
348
- }
349
-
350
- $details = wp_list_sort( $details, 'order', 'ASC', true );
351
  }
352
 
353
  ob_start();
@@ -487,7 +466,6 @@ final class ITSEC_Logs_Page {
487
  $list->prepare_items();
488
  $list->views();
489
  $form->start_form( array( 'method' => 'GET' ) );
490
- $form->add_hidden( 'page', 'itsec-logs' );
491
  $list->display();
492
  $form->end_form();
493
  ?>
37
  }
38
 
39
  public function add_scripts() {
 
 
40
  foreach ( $this->modules as $id => $module ) {
41
  $module->enqueue_scripts_and_styles();
42
  }
272
  'module' => array(
273
  'header' => esc_html__( 'Module', 'better-wp-security' ),
274
  'content' => esc_html( $entry['module'] ),
 
275
  ),
276
  'type' => array(
277
  'header' => esc_html__( 'Type', 'better-wp-security' ),
278
  'content' => $type,
 
279
  ),
280
  'description' => array(
281
  'header' => esc_html__( 'Description', 'better-wp-security' ),
282
  'content' => esc_html( $code ),
 
283
  ),
284
  'timestamp' => array(
285
  'header' => esc_html__( 'Timestamp', 'better-wp-security' ),
286
  'content' => esc_html( $datetime ),
 
287
  ),
288
  'host' => array(
289
  'header' => esc_html__( 'Host', 'better-wp-security' ),
290
  'content' => '<code>' . esc_html( $entry['remote_ip'] ) . '</code>',
 
291
  ),
292
  'user' => array(
293
  'header' => esc_html__( 'User', 'better-wp-security' ),
294
  'content' => esc_html( $username ),
 
295
  ),
296
  'url' => array(
297
  'header' => esc_html__( 'URL', 'better-wp-security' ),
298
  'content' => '<code>' . $url . '</code>',
 
299
  ),
300
  'raw-details' => array(
301
  'header' => esc_html__( 'Raw Details', 'better-wp-security' ),
302
  'content' => true,
 
303
  ),
304
  );
305
 
327
 
328
  $details['raw-details']['content'] = '<p><a class="itsec-log-raw-details-toggle" href="#">' . $this->translations['show_raw_details'] . '</a></p><div class="itsec-log-raw-details">' . $details['raw-details']['content'] . '</div>';
329
  }
 
 
 
 
 
 
 
 
 
 
 
330
  }
331
 
332
  ob_start();
466
  $list->prepare_items();
467
  $list->views();
468
  $form->start_form( array( 'method' => 'GET' ) );
 
469
  $list->display();
470
  $form->end_form();
471
  ?>
core/admin-pages/page-settings.php CHANGED
@@ -68,8 +68,6 @@ final class ITSEC_Settings_Page {
68
  }
69
 
70
  public function add_scripts() {
71
- ITSEC_Lib::enqueue_util();
72
-
73
  foreach ( $this->modules as $id => $module ) {
74
  $module->enqueue_scripts_and_styles();
75
  }
68
  }
69
 
70
  public function add_scripts() {
 
 
71
  foreach ( $this->modules as $id => $module ) {
72
  $module->enqueue_scripts_and_styles();
73
  }
core/core.php CHANGED
@@ -24,7 +24,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
24
  *
25
  * @access private
26
  */
27
- private $plugin_build = 4106;
28
 
29
  /**
30
  * Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
@@ -163,6 +163,13 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
163
  add_action( 'admin_bar_menu', array( $this, 'modify_admin_bar' ), 99 );
164
  }
165
 
 
 
 
 
 
 
 
166
  $login_interstitial = new ITSEC_Lib_Login_Interstitial();
167
  $login_interstitial->run();
168
 
@@ -321,13 +328,8 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
321
  }
322
 
323
  ITSEC_Modules::register_module( 'network-brute-force', "$path/modules/ipcheck", 'default-active' );
324
-
325
- if ( ! defined( 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS') || ! ITSEC_DISABLE_PASSWORD_REQUIREMENTS ) {
326
- ITSEC_Modules::register_module( 'password-requirements', "$path/modules/password-requirements/", 'always-active' );
327
- }
328
-
329
  ITSEC_Modules::register_module( 'ssl', "$path/modules/ssl" );
330
- ITSEC_Modules::register_module( 'strong-passwords', "$path/modules/strong-passwords", 'always-active' );
331
  ITSEC_Modules::register_module( 'system-tweaks', "$path/modules/system-tweaks" );
332
  ITSEC_Modules::register_module( 'wordpress-salts', "$path/modules/salts", 'always-active' );
333
  ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
@@ -604,21 +606,11 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
604
  return $url;
605
  }
606
 
607
- public static function get_logs_page_url( $module = false, $type = false ) {
608
  $url = network_admin_url( 'admin.php?page=itsec-logs' );
609
 
610
- $filters = array();
611
-
612
- if ( $module ) {
613
- $filters[] = rawurlencode("module|{$module}");
614
- }
615
-
616
- if ( $type ) {
617
- $filters[] = rawurlencode( "type|{$type}" );
618
- }
619
-
620
- if ( $filters ) {
621
- $url = add_query_arg( array( 'filters' => $filters ), $url );
622
  }
623
 
624
  return $url;
24
  *
25
  * @access private
26
  */
27
+ private $plugin_build = 4095;
28
 
29
  /**
30
  * Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
163
  add_action( 'admin_bar_menu', array( $this, 'modify_admin_bar' ), 99 );
164
  }
165
 
166
+ $disabled = defined( 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS') && ITSEC_DISABLE_PASSWORD_REQUIREMENTS;
167
+
168
+ if ( ! $disabled && has_action( 'itsec_validate_password' ) ) {
169
+ $pass_requirements = new ITSEC_Lib_Password_Requirements();
170
+ $pass_requirements->run();
171
+ }
172
+
173
  $login_interstitial = new ITSEC_Lib_Login_Interstitial();
174
  $login_interstitial->run();
175
 
328
  }
329
 
330
  ITSEC_Modules::register_module( 'network-brute-force', "$path/modules/ipcheck", 'default-active' );
 
 
 
 
 
331
  ITSEC_Modules::register_module( 'ssl', "$path/modules/ssl" );
332
+ ITSEC_Modules::register_module( 'strong-passwords', "$path/modules/strong-passwords", 'default-active' );
333
  ITSEC_Modules::register_module( 'system-tweaks', "$path/modules/system-tweaks" );
334
  ITSEC_Modules::register_module( 'wordpress-salts', "$path/modules/salts", 'always-active' );
335
  ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
606
  return $url;
607
  }
608
 
609
+ public static function get_logs_page_url( $filter = false ) {
610
  $url = network_admin_url( 'admin.php?page=itsec-logs' );
611
 
612
+ if ( ! empty( $filter ) ) {
613
+ $url = add_query_arg( array( 'filters' => rawurlencode( "module|{$filter}" ) ), $url );
 
 
 
 
 
 
 
 
 
 
614
  }
615
 
616
  return $url;
core/history.txt CHANGED
@@ -697,53 +697,4 @@
697
  Bug Fix: Changed the rules generated by the Filter Suspicious Query Strings feature in order to avoid blocking privacy export/erasure request confirmations.
698
  4.5.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
699
  Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
700
- Bug Fix: Skip recovery if File Change storage is empty.
701
- 4.5.2 - 2018-05-31 - Chris Jean & Timothy Jacobs
702
- Enhancement: Add UI to cancel in progress File Scan.
703
- Tweak: Don't write to the tracked files setting if the file hash has not changed.
704
- Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
705
- Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
706
- Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
707
- 4.6.0 - 2018-06-07 - Chris Jean & Timothy Jacobs
708
- Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
709
- Enhancement: Continually evaluate password strength for users instead of only during registration.
710
- Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
711
- Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
712
- Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
713
- 4.6.1 - 2018-06-11 - Chris Jean & Timothy Jacobs
714
- Enhancement: Add debug settings JSON editor.
715
- Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
716
- Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
717
- Bug Fix: Remove distributed storage table on uninstall.
718
- 4.6.2 - 2018-06-12 - Chris Jean & Timothy Jacobs
719
- Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
720
- 4.6.3 - 2018-06-14 - Chris Jean & Timothy Jacobs
721
- Bug Fix: Enforce the Strong Passwords requirement during Security Check.
722
- 4.6.4 - 2018-06-18 - Chris Jean & Timothy Jacobs
723
- Security Fix: Fixed SQL injection vulnerability in the logs page. Note: Admin privileges are required to exploit this vulnerability. Thanks to Çlirim Emini, Penetration Tester at sentry.co.com, for reporting this vulnerability.
724
- Bug Fix: Provide default values for enabled requirements.
725
- 4.6.5 - 2018-06-27 - Chris Jean & Timothy Jacobs
726
- Enhancement: Add mitigation for the WordPress Attachment File Traversal and Deletion vulnerability.
727
- Tweak: Fire a WordPress action whenever settings are updated.
728
- Bug Fix: Improved input sanitization on the logs page to prevent triggering warnings.
729
- 4.6.6 - 2018-07-09 - Chris Jean & Timothy Jacobs
730
- Tweak: Check if an IP is blacklisted on page load for compatibility with servers that cannot process server configuration level bans immediately.
731
- 4.7.0 - 2018-07-17 - Chris Jean & Timothy Jacobs
732
- Tweak: Display a time diff until the next event on the Debug page.
733
- Compatibility Fix: 404 detection for plugins that mark is_404 later in the hook sequence.
734
- 4.7.1 - 2018-07-24 - Chris Jean & Timothy Jacobs
735
- Tweak: Use Logging API for tracking Notification Center errors.
736
- Tweak: Register Scheduler Events whenever the plugin build changes.
737
- Tweak: Allow for filtering logs by any module recorded.
738
- Bug Fix: Account for any CLI PHP SAPI instead of just WP-CLI in the SSL Module.
739
- 4.7.2 - 2018-07-31 - Chris Jean & Timothy Jacobs
740
- New Feature: Allow for globally setting recipients for admin-targeted notifications. All new notifications will default to the recipients in this list. Notifications can be set to use the default list or switch to a custom list.
741
- Tweak: Account for 3rd-party Backup Plugin in Security Check.
742
- 4.7.3 - 2018-08-01 - Chris Jean & Timothy Jacobs
743
- Bug Fix: Fix serialization of closure error when a plugin registering a hook with a closure is in the boot-up stack and the notification center is triggered too early in the cycle.
744
- 4.7.4 - 2018-08-06 - Chris Jean & Timothy Jacobs
745
- Enhancement: Added a setting to enable/disable the Grade Report feature of Pro.
746
- 4.7.5 - 2018-08-07 - Chris Jean & Timothy Jacobs
747
- Bug Fix: Fixed how the Grade Report enable/disable status is stored to fix admin page loading issues on some sites.
748
- 4.7.6 - 2018-08-14 - Chris Jean & Timothy Jacobs
749
- Bug Fix: REST API Protection blocked the Taxonomies route for all users.
697
  Bug Fix: Changed the rules generated by the Filter Suspicious Query Strings feature in order to avoid blocking privacy export/erasure request confirmations.
698
  4.5.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
699
  Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
700
+ Bug Fix: Skip recovery if File Change storage is empty.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/lib.php CHANGED
@@ -39,7 +39,7 @@ final class ITSEC_Lib {
39
  w3tc_dbcache_flush();
40
  w3tc_objectcache_flush();
41
 
42
- } elseif ( function_exists( 'wp_cache_clear_cache' ) && true == $page ) {
43
 
44
  wp_cache_clear_cache();
45
 
@@ -84,7 +84,7 @@ final class ITSEC_Lib {
84
  *
85
  * @since 4.0.0
86
  *
87
- * @param string $url URL to filter
88
  *
89
  * @return string domain name or '*' on error or domain mapped multisite
90
  * */
@@ -106,7 +106,7 @@ final class ITSEC_Lib {
106
  $host_parts = explode( '.', $host );
107
 
108
  if ( count( $host_parts ) > 2 ) {
109
- $host_parts = array_slice( $host_parts, - 2, 2 );
110
  }
111
 
112
  return implode( '.', $host_parts );
@@ -175,7 +175,6 @@ final class ITSEC_Lib {
175
 
176
  if ( ! empty( $ip ) ) {
177
  $GLOBALS['__itsec_remote_ip'] = $ip;
178
-
179
  return $ip;
180
  }
181
  }
@@ -185,7 +184,6 @@ final class ITSEC_Lib {
185
 
186
  if ( ITSEC_Modules::get_setting( 'global', 'proxy_override' ) ) {
187
  $GLOBALS['__itsec_remote_ip'] = $_SERVER['REMOTE_ADDR'];
188
-
189
  return $GLOBALS['__itsec_remote_ip'];
190
  }
191
 
@@ -205,19 +203,19 @@ final class ITSEC_Lib {
205
 
206
  // Loop through twice. The first run won't accept a reserved or private range IP. If an acceptable IP is not
207
  // found, try again while accepting reserved or private range IPs.
208
- for ( $x = 0; $x < 2; $x ++ ) {
209
  foreach ( $headers as $header ) {
210
- if ( ! isset( $_SERVER[ $header ] ) ) {
211
  continue;
212
  }
213
 
214
- $ip = trim( $_SERVER[ $header ] );
215
 
216
  if ( empty( $ip ) ) {
217
  continue;
218
  }
219
 
220
- if ( false !== ( $comma_index = strpos( $_SERVER[ $header ], ',' ) ) ) {
221
  $ip = substr( $ip, 0, $comma_index );
222
  }
223
 
@@ -408,21 +406,21 @@ final class ITSEC_Lib {
408
 
409
  if ( - 1 < $memory_limit ) {
410
 
411
- $unit = strtolower( substr( $memory_limit, - 1 ) );
412
  $memory_limit = (int) $memory_limit;
413
 
414
- $new_unit = strtolower( substr( $new_memory_limit, - 1 ) );
415
  $new_memory_limit = (int) $new_memory_limit;
416
 
417
  if ( 'm' == $unit ) {
418
 
419
  $memory_limit *= 1048576;
420
 
421
- } elseif ( 'g' == $unit ) {
422
 
423
  $memory_limit *= 1073741824;
424
 
425
- } elseif ( 'k' == $unit ) {
426
 
427
  $memory_limit *= 1024;
428
 
@@ -432,11 +430,11 @@ final class ITSEC_Lib {
432
 
433
  $new_memory_limit *= 1048576;
434
 
435
- } elseif ( 'g' == $new_unit ) {
436
 
437
  $new_memory_limit *= 1073741824;
438
 
439
- } elseif ( 'k' == $new_unit ) {
440
 
441
  $new_memory_limit *= 1024;
442
 
@@ -533,11 +531,11 @@ final class ITSEC_Lib {
533
 
534
  if ( false === $user ) {
535
  $user = wp_get_current_user();
536
- } elseif ( is_int( $user ) ) {
537
  $user = get_user_by( 'id', $user );
538
- } elseif ( is_string( $user ) ) {
539
  $user = get_user_by( 'login', $user );
540
- } elseif ( is_object( $user ) && isset( $user->ID ) ) {
541
  $user = get_user_by( 'id', $user->ID );
542
  } else {
543
  if ( is_object( $user ) ) {
@@ -609,7 +607,7 @@ final class ITSEC_Lib {
609
 
610
  if ( 'Basic ' === $http_auth_type ) {
611
  $authentication_types[] = 'header_http_basic_auth';
612
- } elseif ( 'OAuth ' === $http_auth_type ) {
613
  $authentication_types[] = 'header_http_oauth';
614
  }
615
  }
@@ -626,14 +624,14 @@ final class ITSEC_Lib {
626
  $authentication_types[] = 'post_oauth';
627
  }
628
 
629
- if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
630
- $source = 'xmlrpc';
631
  $authentication_types = array( 'username_and_password' );
632
- } elseif ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
633
- $source = 'rest_api';
634
  $authentication_types[] = 'cookie';
635
  } else {
636
- $source = 'wp-login.php';
637
  $authentication_types = array( 'username_and_password' );
638
  }
639
 
@@ -675,7 +673,7 @@ final class ITSEC_Lib {
675
  */
676
  public static function get_request_path() {
677
  if ( ! isset( $GLOBALS['__itsec_lib_get_request_path'] ) ) {
678
- $request_uri = preg_replace( '|//+|', '/', $_SERVER['REQUEST_URI'] );
679
  $GLOBALS['__itsec_lib_get_request_path'] = self::get_url_path( $request_uri, self::get_home_root() );
680
  }
681
 
@@ -698,8 +696,8 @@ final class ITSEC_Lib {
698
  global $wpdb;
699
  $main_options = $wpdb->base_prefix . 'options';
700
 
701
- $lock = "itsec-lock-{$name}";
702
- $now = ITSEC_Core::get_current_time_gmt();
703
  $release_at = $now + $expires_in;
704
 
705
  if ( is_multisite() ) {
@@ -782,7 +780,7 @@ final class ITSEC_Lib {
782
  $alloptions = wp_cache_get( 'alloptions' );
783
 
784
  if ( is_array( $alloptions ) && isset( $alloptions[ $lock ] ) ) {
785
- unset( $alloptions[ $lock ] );
786
  wp_cache_set( 'alloptions', $alloptions, 'options' );
787
  } else {
788
  wp_cache_delete( $lock, 'options' );
@@ -847,13 +845,13 @@ final class ITSEC_Lib {
847
  wp_cache_switch_to_blog( 1 );
848
 
849
  $alloptions = wp_cache_get( 'alloptions' );
850
- $set_all = false;
851
 
852
  foreach ( $rows as $row ) {
853
  $lock = $row->option_name;
854
 
855
  if ( is_array( $alloptions ) && isset( $alloptions[ $lock ] ) ) {
856
- unset( $alloptions[ $lock ] );
857
  $set_all = true;
858
  } else {
859
  wp_cache_delete( $lock, 'options' );
@@ -920,7 +918,7 @@ final class ITSEC_Lib {
920
  public static function get_ssl_support_probability() {
921
  if ( is_ssl() ) {
922
  $probability = 50; // The site appears to be on an SSL connection but it could be self-signed or otherwise
923
- // not valid to a visitor.
924
  } else {
925
  $probability = 0;
926
  }
@@ -1070,10 +1068,8 @@ final class ITSEC_Lib {
1070
  * Enqueue the itsec_util script.
1071
  *
1072
  * Will only be included once per page.
1073
- *
1074
- * @param array $args
1075
  */
1076
- public static function enqueue_util( $args = array() ) {
1077
 
1078
  static $enqueued = false;
1079
 
@@ -1101,8 +1097,8 @@ final class ITSEC_Lib {
1101
 
1102
  wp_enqueue_script( 'itsec-util', plugins_url( 'admin-pages/js/util.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build(), true );
1103
  wp_localize_script( 'itsec-util', 'itsec_util', array(
1104
- 'ajax_action' => isset( $args['action'] ) ? $args['action'] : 'itsec_settings_page',
1105
- 'ajax_nonce' => wp_create_nonce( isset( $args['nonce'] ) ? $args['nonce'] : 'itsec-settings-nonce' ),
1106
  'translations' => $translations,
1107
  ) );
1108
 
@@ -1157,111 +1153,4 @@ final class ITSEC_Lib {
1157
 
1158
  return $array;
1159
  }
1160
-
1161
- /**
1162
- * Insert an element after a given key.
1163
- *
1164
- * @param string|int $key
1165
- * @param array $array
1166
- * @param string|int $new_key
1167
- * @param mixed $new_value
1168
- *
1169
- * @return array
1170
- */
1171
- public static function array_insert_after( $key, $array, $new_key, $new_value ) {
1172
- if ( array_key_exists( $key, $array ) ) {
1173
- $new = array();
1174
- foreach ( $array as $k => $value ) {
1175
- $new[ $k ] = $value;
1176
- if ( $k === $key ) {
1177
- $new[ $new_key ] = $new_value;
1178
- }
1179
- }
1180
-
1181
- return $new;
1182
- }
1183
-
1184
- $array[ $new_key ] = $new_value;
1185
-
1186
- return $array;
1187
- }
1188
-
1189
- /**
1190
- * Array unique implementation that allows for non-scalar values.
1191
- *
1192
- * Will compare elements using `serialize()`.
1193
- *
1194
- * Keys are preserved. If a numeric array is given, the array will be re-indexed.
1195
- *
1196
- * @param array $array
1197
- *
1198
- * @return array
1199
- */
1200
- public static function non_scalar_array_unique( $array ) {
1201
-
1202
- $is_numeric = wp_is_numeric_array( $array );
1203
-
1204
- $hashes = array();
1205
-
1206
- foreach ( $array as $key => $value ) {
1207
- $hash = serialize( $value );
1208
-
1209
- if ( isset( $hashes[ $hash ] ) ) {
1210
- unset( $array[ $key ] );
1211
- } else {
1212
- $hashes[ $hash ] = 1;
1213
- }
1214
- }
1215
-
1216
- if ( $is_numeric ) {
1217
- return array_values( $array );
1218
- }
1219
-
1220
- return $array;
1221
- }
1222
-
1223
- /**
1224
- * Get whatever backup plugin is being used on this site.
1225
- *
1226
- * @return string
1227
- */
1228
- public static function get_backup_plugin() {
1229
-
1230
- $possible = array(
1231
- 'backupbuddy/backupbuddy.php',
1232
- 'updraftplus/updraftplus.php',
1233
- 'backwpup/backwpup.php',
1234
- 'xcloner-backup-and-restore/xcloner.php',
1235
- 'duplicator/duplicator.php',
1236
- 'backup/backup.php',
1237
- 'wp-db-backup/wp-db-backup.php',
1238
- 'backupwordpress/backupwordpress.php',
1239
- 'blogvault-real-time-backup/blogvault.php',
1240
- 'wp-all-backup/wp-all-backup.php',
1241
- 'vaultpress/vaultpress.php',
1242
- );
1243
-
1244
- /**
1245
- * Filter the list of possible backup plugins.
1246
- *
1247
- * @param string[] List of Backup Plugin __FILE__.
1248
- */
1249
- $possible = apply_filters( 'itsec_possible_backup_plugins', $possible );
1250
-
1251
- if ( ! function_exists( 'is_plugin_active' ) ) {
1252
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1253
- }
1254
-
1255
- if ( ! function_exists( 'is_plugin_active' ) ) {
1256
- return '';
1257
- }
1258
-
1259
- foreach ( $possible as $file ) {
1260
- if ( is_plugin_active( $file ) ) {
1261
- return $file;
1262
- }
1263
- }
1264
-
1265
- return '';
1266
- }
1267
  }
39
  w3tc_dbcache_flush();
40
  w3tc_objectcache_flush();
41
 
42
+ } else if ( function_exists( 'wp_cache_clear_cache' ) && true == $page ) {
43
 
44
  wp_cache_clear_cache();
45
 
84
  *
85
  * @since 4.0.0
86
  *
87
+ * @param string $url URL to filter
88
  *
89
  * @return string domain name or '*' on error or domain mapped multisite
90
  * */
106
  $host_parts = explode( '.', $host );
107
 
108
  if ( count( $host_parts ) > 2 ) {
109
+ $host_parts = array_slice( $host_parts, -2, 2 );
110
  }
111
 
112
  return implode( '.', $host_parts );
175
 
176
  if ( ! empty( $ip ) ) {
177
  $GLOBALS['__itsec_remote_ip'] = $ip;
 
178
  return $ip;
179
  }
180
  }
184
 
185
  if ( ITSEC_Modules::get_setting( 'global', 'proxy_override' ) ) {
186
  $GLOBALS['__itsec_remote_ip'] = $_SERVER['REMOTE_ADDR'];
 
187
  return $GLOBALS['__itsec_remote_ip'];
188
  }
189
 
203
 
204
  // Loop through twice. The first run won't accept a reserved or private range IP. If an acceptable IP is not
205
  // found, try again while accepting reserved or private range IPs.
206
+ for ( $x = 0; $x < 2; $x++ ) {
207
  foreach ( $headers as $header ) {
208
+ if ( ! isset( $_SERVER[$header] ) ) {
209
  continue;
210
  }
211
 
212
+ $ip = trim( $_SERVER[$header] );
213
 
214
  if ( empty( $ip ) ) {
215
  continue;
216
  }
217
 
218
+ if ( false !== ( $comma_index = strpos( $_SERVER[$header], ',' ) ) ) {
219
  $ip = substr( $ip, 0, $comma_index );
220
  }
221
 
406
 
407
  if ( - 1 < $memory_limit ) {
408
 
409
+ $unit = strtolower( substr( $memory_limit, - 1 ) );
410
  $memory_limit = (int) $memory_limit;
411
 
412
+ $new_unit = strtolower( substr( $new_memory_limit, - 1 ) );
413
  $new_memory_limit = (int) $new_memory_limit;
414
 
415
  if ( 'm' == $unit ) {
416
 
417
  $memory_limit *= 1048576;
418
 
419
+ } else if ( 'g' == $unit ) {
420
 
421
  $memory_limit *= 1073741824;
422
 
423
+ } else if ( 'k' == $unit ) {
424
 
425
  $memory_limit *= 1024;
426
 
430
 
431
  $new_memory_limit *= 1048576;
432
 
433
+ } else if ( 'g' == $new_unit ) {
434
 
435
  $new_memory_limit *= 1073741824;
436
 
437
+ } else if ( 'k' == $new_unit ) {
438
 
439
  $new_memory_limit *= 1024;
440
 
531
 
532
  if ( false === $user ) {
533
  $user = wp_get_current_user();
534
+ } else if ( is_int( $user ) ) {
535
  $user = get_user_by( 'id', $user );
536
+ } else if ( is_string( $user ) ) {
537
  $user = get_user_by( 'login', $user );
538
+ } else if ( is_object( $user ) && isset( $user->ID ) ) {
539
  $user = get_user_by( 'id', $user->ID );
540
  } else {
541
  if ( is_object( $user ) ) {
607
 
608
  if ( 'Basic ' === $http_auth_type ) {
609
  $authentication_types[] = 'header_http_basic_auth';
610
+ } else if ( 'OAuth ' === $http_auth_type ) {
611
  $authentication_types[] = 'header_http_oauth';
612
  }
613
  }
624
  $authentication_types[] = 'post_oauth';
625
  }
626
 
627
+ if ( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ) {
628
+ $source = 'xmlrpc';
629
  $authentication_types = array( 'username_and_password' );
630
+ } else if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
631
+ $source = 'rest_api';
632
  $authentication_types[] = 'cookie';
633
  } else {
634
+ $source = 'wp-login.php';
635
  $authentication_types = array( 'username_and_password' );
636
  }
637
 
673
  */
674
  public static function get_request_path() {
675
  if ( ! isset( $GLOBALS['__itsec_lib_get_request_path'] ) ) {
676
+ $request_uri = preg_replace( '|//+|', '/', $_SERVER['REQUEST_URI'] );
677
  $GLOBALS['__itsec_lib_get_request_path'] = self::get_url_path( $request_uri, self::get_home_root() );
678
  }
679
 
696
  global $wpdb;
697
  $main_options = $wpdb->base_prefix . 'options';
698
 
699
+ $lock = "itsec-lock-{$name}";
700
+ $now = ITSEC_Core::get_current_time_gmt();
701
  $release_at = $now + $expires_in;
702
 
703
  if ( is_multisite() ) {
780
  $alloptions = wp_cache_get( 'alloptions' );
781
 
782
  if ( is_array( $alloptions ) && isset( $alloptions[ $lock ] ) ) {
783
+ unset( $alloptions[$lock] );
784
  wp_cache_set( 'alloptions', $alloptions, 'options' );
785
  } else {
786
  wp_cache_delete( $lock, 'options' );
845
  wp_cache_switch_to_blog( 1 );
846
 
847
  $alloptions = wp_cache_get( 'alloptions' );
848
+ $set_all = false;
849
 
850
  foreach ( $rows as $row ) {
851
  $lock = $row->option_name;
852
 
853
  if ( is_array( $alloptions ) && isset( $alloptions[ $lock ] ) ) {
854
+ unset( $alloptions[$lock] );
855
  $set_all = true;
856
  } else {
857
  wp_cache_delete( $lock, 'options' );
918
  public static function get_ssl_support_probability() {
919
  if ( is_ssl() ) {
920
  $probability = 50; // The site appears to be on an SSL connection but it could be self-signed or otherwise
921
+ // not valid to a visitor.
922
  } else {
923
  $probability = 0;
924
  }
1068
  * Enqueue the itsec_util script.
1069
  *
1070
  * Will only be included once per page.
 
 
1071
  */
1072
+ public static function enqueue_util() {
1073
 
1074
  static $enqueued = false;
1075
 
1097
 
1098
  wp_enqueue_script( 'itsec-util', plugins_url( 'admin-pages/js/util.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build(), true );
1099
  wp_localize_script( 'itsec-util', 'itsec_util', array(
1100
+ 'ajax_action' => 'itsec_settings_page',
1101
+ 'ajax_nonce' => wp_create_nonce( 'itsec-settings-nonce' ),
1102
  'translations' => $translations,
1103
  ) );
1104
 
1153
 
1154
  return $array;
1155
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1156
  }
core/lib/class-itsec-job.php CHANGED
@@ -34,11 +34,10 @@ class ITSEC_Job {
34
  *
35
  * The original event will not fire while a reschedule is pending.
36
  *
37
- * @param int $seconds
38
- * @param array $data Additional data to attach to the rescheduled event.
39
  */
40
- public function reschedule_in( $seconds, $data = array() ) {
41
- $data = array_merge( $this->data, $data );
42
 
43
  if ( isset( $data['retry_count'] ) ) {
44
  $data['retry_count'] ++;
34
  *
35
  * The original event will not fire while a reschedule is pending.
36
  *
37
+ * @param int $seconds
 
38
  */
39
+ public function reschedule_in( $seconds ) {
40
+ $data = $this->get_data();
41
 
42
  if ( isset( $data['retry_count'] ) ) {
43
  $data['retry_count'] ++;
core/lib/class-itsec-lib-canonical-roles.php CHANGED
@@ -122,76 +122,6 @@ final class ITSEC_Lib_Canonical_Roles {
122
  return '';
123
  }
124
 
125
- /**
126
- * Get the canonical role from any WordPress role.
127
- *
128
- * @param string $role
129
- *
130
- * @return string
131
- */
132
- public static function get_canonical_role_from_role( $role ) {
133
- return self::get_role_from_caps( array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) ) );
134
- }
135
-
136
- /**
137
- * Retrieve a canonical role for a user and a role.
138
- *
139
- * @param string $role
140
- * @param WP_User $user
141
- *
142
- * @return string
143
- */
144
- public static function get_canonical_role_from_role_and_user( $role, $user ) {
145
- $user = ITSEC_Lib::get_user( $user );
146
-
147
- if ( empty( $role ) ) {
148
- $role_caps = array();
149
- } else {
150
- $role_caps = array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) );
151
- }
152
-
153
- $user_caps = array();
154
-
155
- if ( isset( $user->caps ) ) {
156
- $wp_roles = wp_roles();
157
-
158
- foreach ( $user->caps as $cap => $has ) {
159
- if ( $has && ! $wp_roles->is_role( $cap ) ) {
160
- $user_caps[] = $has;
161
- }
162
- }
163
- }
164
-
165
- return self::get_role_from_caps( array_merge( $role_caps, $user_caps ) );
166
- }
167
-
168
- /**
169
- * Get all users that have the given canonical role.
170
- *
171
- * @param string|string[] $canonical
172
- * @param array $additional_args
173
- *
174
- * @return WP_User[]
175
- */
176
- public static function get_users_with_canonical_role( $canonical, $additional_args = array() ) {
177
-
178
- $canonical = (array) $canonical;
179
-
180
- $roles = array();
181
-
182
- foreach ( wp_roles()->roles as $role => $_ ) {
183
- if ( in_array( self::get_canonical_role_from_role( $role ), $canonical, true ) ) {
184
- $roles[] = $role;
185
- }
186
- }
187
-
188
- if ( empty( $roles ) ) {
189
- return array();
190
- }
191
-
192
- return get_users( array_merge( $additional_args, array( 'role__in' => $roles ) ) );
193
- }
194
-
195
  /**
196
  * Get a list of all of the capabilities that are unique to each role.
197
  *
122
  return '';
123
  }
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  /**
126
  * Get a list of all of the capabilities that are unique to each role.
127
  *
core/lib/class-itsec-lib-password-requirements.php CHANGED
@@ -1,75 +1,186 @@
1
  <?php
 
 
 
 
 
 
2
 
3
  /**
4
  * Class ITSEC_Lib_Password_Requirements
5
  */
6
  class ITSEC_Lib_Password_Requirements {
7
 
8
- /** @var array[] */
9
- private static $requirements;
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  /**
12
- * Get all registered password requirements.
13
  *
14
- * @return array
 
 
15
  */
16
- public static function get_registered() {
17
- if ( null === self::$requirements ) {
18
- self::$requirements = array();
19
-
20
- /**
21
- * Fires when password requirements should be registered.
22
- */
23
- do_action( 'itsec_register_password_requirements' );
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- return self::$requirements;
27
  }
28
 
29
  /**
30
- * Register a password requirement.
31
  *
32
- * @param string $reason_code
33
- * @param array $opts
34
  */
35
- public static function register( $reason_code, $opts ) {
36
- $merged = wp_parse_args( $opts, array(
37
- 'evaluate' => null,
38
- 'validate' => null,
39
- 'flag_check' => null,
40
- 'reason' => null,
41
- 'defaults' => null,
42
- 'settings_config' => null, // Callable returning label, description, render & sanitize callbacks.
43
- 'meta' => "_itsec_password_evaluation_{$reason_code}",
44
- 'evaluate_if_not_enabled' => false,
45
- ) );
46
 
47
- if (
48
- ( array_key_exists( 'validate', $opts ) || array_key_exists( 'evaluate', $opts ) ) &&
49
- ( ! is_callable( $merged['validate'] ) || ! is_callable( $merged['evaluate'] ) )
50
- ) {
51
  return;
52
  }
53
 
54
- if ( array_key_exists( 'flag_check', $opts ) && ! is_callable( $merged['flag_check'] ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  return;
56
  }
57
 
58
- if ( array_key_exists( 'defaults', $opts ) ) {
59
- if ( ! is_array( $merged['defaults'] ) ) {
60
- return;
61
- }
62
 
63
- if ( ! array_key_exists( 'settings_config', $opts ) ) {
64
- return;
65
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
 
68
- if ( array_key_exists( 'settings_config', $opts ) && ! is_callable( $merged['settings_config'] ) ) {
69
- return;
 
 
70
  }
71
 
72
- self::$requirements[ $reason_code ] = $merged;
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  /**
@@ -85,24 +196,14 @@ class ITSEC_Lib_Password_Requirements {
85
  return '';
86
  }
87
 
88
- $message = '';
89
-
90
- $registered = self::get_registered();
91
-
92
- if ( isset( $registered[ $reason ] ) ) {
93
- $settings = self::get_requirement_settings( $reason );
94
- $message = call_user_func( $registered[ $reason ]['reason'], get_user_meta( $user->ID, $registered[ $reason ]['meta'], true ), $settings );
95
- }
96
-
97
  /**
98
  * Retrieve a human readable description as to why a password change has been required for the current user.
99
  *
100
  * Modules MUST HTML escape their reason strings before returning them with this filter.
101
  *
102
- * @param string $message
103
- * @param WP_User $user
104
  */
105
- $message = apply_filters( "itsec_password_change_requirement_description_for_{$reason}", $message, $user );
106
 
107
  if ( $message ) {
108
  return $message;
@@ -124,12 +225,12 @@ class ITSEC_Lib_Password_Requirements {
124
 
125
  $args = wp_parse_args( $args, array(
126
  'error' => new WP_Error(),
127
- 'context' => '',
128
  ) );
129
 
130
- /** @var WP_Error $error */
131
- $error = $args['error'];
132
- $user = $user instanceof stdClass ? $user : ITSEC_Lib::get_user( $user );
133
 
134
  if ( ! $user ) {
135
  $error->add( 'invalid_user', esc_html__( 'Invalid User', 'better-wp-security' ) );
@@ -137,27 +238,6 @@ class ITSEC_Lib_Password_Requirements {
137
  return $error;
138
  }
139
 
140
- if ( ! empty( $user->ID ) && wp_check_password( $new_password, get_userdata( $user->ID )->user_pass, $user->ID ) ) {
141
- $message = wp_kses( __( '<strong>ERROR</strong>: The password you have chosen appears to have been used before. You must choose a new password.', 'better-wp-security' ), array( 'strong' => array() ) );
142
- $error->add( 'pass', $message );
143
-
144
- return $error;
145
- }
146
-
147
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
148
-
149
- if ( isset( $args['role'] ) && $user instanceof WP_User ) {
150
- $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $args['role'], $user );
151
- } elseif ( isset( $args['role'] ) ) {
152
- $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $args['role'] );
153
- } elseif ( empty( $user->ID ) || ! is_numeric( $user->ID ) ) {
154
- $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( get_option( 'default_role', 'subscriber' ) );
155
- } else {
156
- $canonical = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
157
- }
158
-
159
- $args['canonical'] = $canonical;
160
-
161
  /**
162
  * Fires when modules should validate a password according to their rules.
163
  *
@@ -207,28 +287,9 @@ class ITSEC_Lib_Password_Requirements {
207
  return false;
208
  }
209
 
210
- $registered = self::get_registered();
211
-
212
- if ( isset( $registered[ $reason ] ) ) {
213
- return self::is_requirement_enabled( $reason ) ? $reason : false;
214
- }
215
-
216
- if ( ! has_filter( "itsec_password_change_requirement_description_for_{$reason}" ) ) {
217
- return false;
218
- }
219
-
220
  return $reason;
221
  }
222
 
223
- /**
224
- * Globally clear all required password changes with a particular reason code.
225
- *
226
- * @param string $reason
227
- */
228
- public static function global_clear_required_password_change( $reason ) {
229
- delete_metadata( 'user', 0, 'itsec_password_change_required', $reason, true );
230
- }
231
-
232
  /**
233
  * Get the GMT time the user's password has last been changed.
234
  *
@@ -251,64 +312,6 @@ class ITSEC_Lib_Password_Requirements {
251
  return $deprecated;
252
  }
253
 
254
- if ( ! $changed ) {
255
- return strtotime( $user->user_registered );
256
- }
257
-
258
  return $changed;
259
  }
260
-
261
- /**
262
- * Is a password requirement enabled.
263
- *
264
- * @param string $requirement
265
- *
266
- * @return bool
267
- */
268
- public static function is_requirement_enabled( $requirement ) {
269
-
270
- $requirements = self::get_registered();
271
-
272
- if ( ! isset( $requirements[ $requirement ] ) ) {
273
- return false;
274
- }
275
-
276
- // If the requirement does not have any settings, than it is always enabled.
277
- if ( null === $requirements[ $requirement ]['settings_config'] ) {
278
- return true;
279
- }
280
-
281
- $enabled = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
282
-
283
- if ( ! empty( $enabled[ $requirement ] ) ) {
284
- return true;
285
- }
286
-
287
- return false;
288
- }
289
-
290
- /**
291
- * Get requirement settings.
292
- *
293
- * @param string $requirement
294
- *
295
- * @return array|false
296
- */
297
- public static function get_requirement_settings( $requirement ) {
298
-
299
- $requirements = self::get_registered();
300
-
301
- if ( ! isset( $requirements[ $requirement ] ) ) {
302
- return false;
303
- }
304
-
305
- if ( null === $requirements[ $requirement ]['settings_config'] ) {
306
- return false;
307
- }
308
-
309
- $all_settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
310
- $settings = isset( $all_settings[ $requirement ] ) ? $all_settings[ $requirement ] : array();
311
-
312
- return wp_parse_args( $settings, $requirements[ $requirement ]['defaults'] );
313
- }
314
  }
1
  <?php
2
+ /**
3
+ * Tool to manage password requirements across modules.
4
+ *
5
+ * @since 3.9.0
6
+ * @license GPLv2+
7
+ */
8
 
9
  /**
10
  * Class ITSEC_Lib_Password_Requirements
11
  */
12
  class ITSEC_Lib_Password_Requirements {
13
 
14
+ const LOGIN_ACTION = 'itsec_update_password';
15
+ const META_KEY = '_itsec_update_password_key';
16
+
17
+ /** @var string */
18
+ private $error_message = '';
19
+
20
+ public function run() {
21
+
22
+ add_action( 'user_profile_update_errors', array( $this, 'forward_profile_pass_update' ), 0, 3 );
23
+ add_action( 'validate_password_reset', array( $this, 'forward_reset_pass' ), 10, 2 );
24
+ add_action( 'profile_update', array( $this, 'set_password_last_updated' ), 10, 2 );
25
+
26
+ add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
27
+ }
28
 
29
  /**
30
+ * When a user's password is updated, or a new user created, verify that the new password is valid.
31
  *
32
+ * @param WP_Error $errors
33
+ * @param bool $update
34
+ * @param WP_User|stdClass $user
35
  */
36
+ public function forward_profile_pass_update( $errors, $update, $user ) {
37
+
38
+ if ( ! isset( $user->user_pass ) ) {
39
+ return;
40
+ }
41
+
42
+ if ( ! $update ) {
43
+ $context = 'admin-user-create';
44
+ } elseif ( isset( $user->ID ) && $user->ID === get_current_user_id() ) {
45
+ $context = 'profile-update';
46
+ } else {
47
+ $context = 'admin-profile-update';
48
+ }
49
+
50
+ $args = array(
51
+ 'error' => $errors,
52
+ 'context' => $context
53
+ );
54
+
55
+ if ( isset( $user->role ) ) {
56
+ $args['role'] = $user->role;
57
  }
58
 
59
+ self::validate_password( $user, $user->user_pass, $args );
60
  }
61
 
62
  /**
63
+ * When a user attempts to reset their password, verify that the new password is valid.
64
  *
65
+ * @param WP_Error $errors
66
+ * @param WP_User $user
67
  */
68
+ public function forward_reset_pass( $errors, $user ) {
 
 
 
 
 
 
 
 
 
 
69
 
70
+ if ( ! isset( $_POST['pass1'] ) || is_wp_error( $user ) ) {
71
+ // The validate_password_reset action fires when first rendering the reset page and when handling the form
72
+ // submissions. Since the pass1 data is missing, this must be the initial page render. So, we don't need to
73
+ // do anything yet.
74
  return;
75
  }
76
 
77
+ self::validate_password( $user, $_POST['pass1'], array(
78
+ 'error' => $errors,
79
+ 'context' => 'reset-password',
80
+ ) );
81
+ }
82
+
83
+ /**
84
+ * Whenever a user object is updated, set when their password was last updated.
85
+ *
86
+ * @param int $user_id
87
+ * @param object $old_user_data
88
+ */
89
+ public function set_password_last_updated( $user_id, $old_user_data ) {
90
+
91
+ $user = get_userdata( $user_id );
92
+
93
+ if ( $user->user_pass === $old_user_data->user_pass ) {
94
  return;
95
  }
96
 
97
+ delete_user_meta( $user_id, 'itsec_password_change_required' );
98
+ update_user_meta( $user_id, 'itsec_last_password_change', ITSEC_Core::get_current_time_gmt() );
99
+ }
 
100
 
101
+ /**
102
+ * Register the password change interstitial.
103
+ *
104
+ * @param ITSEC_Lib_Login_Interstitial $lib
105
+ */
106
+ public function register_interstitial( $lib ) {
107
+ $lib->register( 'update-password', array( $this, 'render_interstitial' ), array(
108
+ 'show_to_user' => array( __CLASS__, 'password_change_required' ),
109
+ 'info_message' => array( __CLASS__, 'get_message_for_password_change_reason' ),
110
+ 'submit' => array( $this, 'submit' ),
111
+ ) );
112
+ }
113
+
114
+ /**
115
+ * Render the interstitial.
116
+ *
117
+ * @param WP_User $user
118
+ */
119
+ public function render_interstitial( $user ) {
120
+ ?>
121
+
122
+ <div class="user-pass1-wrap">
123
+ <p><label for="pass1"><?php _e( 'New Password', 'better-wp-security' ); ?></label></p>
124
+ </div>
125
+
126
+ <div class="wp-pwd">
127
+ <span class="password-input-wrapper">
128
+ <input type="password" data-reveal="1"
129
+ data-pw="<?php echo esc_attr( wp_generate_password( 16 ) ); ?>" name="pass1" id="pass1"
130
+ class="input" size="20" value="" autocomplete="off" aria-describedby="pass-strength-result"/>
131
+ </span>
132
+ <div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator', 'better-wp-security' ); ?></div>
133
+ </div>
134
+
135
+ <p class="user-pass2-wrap">
136
+ <label for="pass2"><?php _e( 'Confirm new password' ) ?></label><br/>
137
+ <input type="password" name="pass2" id="pass2" class="input" size="20" value="" autocomplete="off"/>
138
+ </p>
139
+
140
+ <p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
141
+ <br class="clear"/>
142
+
143
+ <p class="submit">
144
+ <input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large"
145
+ value="<?php esc_attr_e( 'Update Password', 'better-wp-security' ); ?>"/>
146
+ </p>
147
+
148
+ <?php
149
+ }
150
+
151
+ /**
152
+ * Handle the request to update the user's password.
153
+ *
154
+ * @param WP_User $user
155
+ * @param array $data POSTed data.
156
+ *
157
+ * @return WP_Error|null
158
+ */
159
+ public function submit( $user, $data ) {
160
+
161
+ if ( empty( $data['pass1'] ) ) {
162
+ return new WP_Error(
163
+ 'itsec-password-requirements-empty-password',
164
+ __( 'Please enter your new password.', 'better-wp-security' )
165
+ );
166
  }
167
 
168
+ $error = self::validate_password( $user, $data['pass1'] );
169
+
170
+ if ( $error->get_error_message() ) {
171
+ return $error;
172
  }
173
 
174
+ $error = wp_update_user( array(
175
+ 'ID' => $user->ID,
176
+ 'user_pass' => $data['pass1']
177
+ ) );
178
+
179
+ if ( is_wp_error( $error ) ) {
180
+ return $error;
181
+ }
182
+
183
+ return null;
184
  }
185
 
186
  /**
196
  return '';
197
  }
198
 
 
 
 
 
 
 
 
 
 
199
  /**
200
  * Retrieve a human readable description as to why a password change has been required for the current user.
201
  *
202
  * Modules MUST HTML escape their reason strings before returning them with this filter.
203
  *
204
+ * @param string $message
 
205
  */
206
+ $message = apply_filters( "itsec_password_change_requirement_description_for_{$reason}", '' );
207
 
208
  if ( $message ) {
209
  return $message;
225
 
226
  $args = wp_parse_args( $args, array(
227
  'error' => new WP_Error(),
228
+ 'context' => ''
229
  ) );
230
 
231
+ $error = isset( $args['error'] ) ? $args['error'] : new WP_Error();
232
+
233
+ $user = $user instanceof stdClass ? $user : ITSEC_Lib::get_user( $user );
234
 
235
  if ( ! $user ) {
236
  $error->add( 'invalid_user', esc_html__( 'Invalid User', 'better-wp-security' ) );
238
  return $error;
239
  }
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  /**
242
  * Fires when modules should validate a password according to their rules.
243
  *
287
  return false;
288
  }
289
 
 
 
 
 
 
 
 
 
 
 
290
  return $reason;
291
  }
292
 
 
 
 
 
 
 
 
 
 
293
  /**
294
  * Get the GMT time the user's password has last been changed.
295
  *
312
  return $deprecated;
313
  }
314
 
 
 
 
 
315
  return $changed;
316
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  }
core/lib/class-itsec-mail.php CHANGED
@@ -1,19 +1,14 @@
1
  <?php
2
 
3
  final class ITSEC_Mail {
4
- private $name;
5
  private $content = '';
6
- private $groups = array();
7
- private $current_group;
8
- private $deferred = '';
9
  private $subject = '';
10
  private $recipients = array();
11
  private $attachments = array();
12
  private $template_path = '';
13
 
14
- public function __construct( $name = '' ) {
15
  $this->template_path = dirname( __FILE__ ) . '/mail-templates/';
16
- $this->name = $name;
17
  }
18
 
19
  public function add_header( $title, $banner_title, $use_site_logo = false ) {
@@ -36,7 +31,7 @@ final class ITSEC_Mail {
36
  'title' => $title,
37
  );
38
 
39
- $this->add_html( $this->replace_all( $header, $replacements ), 'header' );
40
  }
41
 
42
  public function add_footer() {
@@ -79,13 +74,13 @@ final class ITSEC_Mail {
79
 
80
  );
81
 
82
- $this->add_html( $this->replace_all( $footer, $replacements ) );
83
 
84
  if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG ) {
85
  $this->include_debug_info();
86
  }
87
 
88
- $this->add_html( $this->get_template( 'close.html' ), 'footer' );
89
  }
90
 
91
  public function add_user_footer() {
@@ -101,79 +96,51 @@ final class ITSEC_Mail {
101
  ) );
102
 
103
  $footer .= $this->get_template( 'close.html' );
104
- $this->add_html( $footer, 'user-footer' );
105
  }
106
 
107
  public function add_text( $content ) {
108
- $this->add_html( $this->get_text( $content ) );
109
- }
110
-
111
- public function get_text( $content ) {
112
  $module = $this->get_template( 'text.html' );
113
  $module = $this->replace( $module, 'content', $content );
114
 
115
- return $module;
116
  }
117
 
118
  public function add_divider() {
119
- $this->add_html( $this->get_divider() );
120
- }
121
-
122
- public function get_divider() {
123
- return $this->get_template( 'divider.html' );
124
  }
125
 
126
  public function add_large_text( $content ) {
127
- $this->add_html( $this->get_large_text( $content ) );
128
- }
129
-
130
- public function get_large_text( $content ) {
131
  $module = $this->get_template( 'large-text.html' );
132
  $module = $this->replace( $module, 'content', $content );
133
 
134
- return $module;
135
  }
136
 
137
  public function add_info_box( $content, $icon_type = 'info' ) {
138
- $this->add_html( $this->get_info_box( $content, $icon_type ) );
139
- }
140
-
141
- public function get_info_box( $content, $icon_type = 'info' ) {
142
  $icon_url = $this->get_image_url( $icon_type === 'warning' ? 'warning_icon_yellow' : "{$icon_type}_icon" );
143
 
144
  $module = $this->get_template( 'info-box.html' );
145
  $module = $this->replace_all( $module, compact( 'content', 'icon_url' ) );
146
 
147
- return $module;
148
  }
149
 
150
  public function add_details_box( $content ) {
151
- $this->add_html( $this->get_details_box( $content ) );
152
- }
153
-
154
- public function get_details_box( $content ) {
155
  $module = $this->get_template( 'details-box.html' );
156
  $module = $this->replace( $module, 'content', $content );
157
 
158
- return $module;
159
  }
160
 
161
  public function add_large_code( $content ) {
162
- $this->add_html( $this->get_large_code( $content ) );
163
- }
164
-
165
- public function get_large_code( $content ) {
166
  $module = $this->get_template( 'large-code.html' );
167
  $module = $this->replace( $module, 'content', $content );
168
 
169
- return $module;
170
  }
171
 
172
  public function add_section_heading( $content, $icon_type = false ) {
173
- $this->add_html( $this->get_section_heading( $content, $icon_type ) );
174
- }
175
-
176
- public function get_section_heading( $content, $icon_type = false ) {
177
  if ( empty( $icon_type ) ) {
178
  $heading = $this->get_template( 'section-heading.html' );
179
  $heading = $this->replace_all( $heading, compact( 'content' ) );
@@ -184,7 +151,7 @@ final class ITSEC_Mail {
184
  $heading = $this->replace_all( $heading, compact( 'content', 'icon_url' ) );
185
  }
186
 
187
- return $heading;
188
  }
189
 
190
  public function add_lockouts_summary( $user_count, $host_count ) {
@@ -199,7 +166,7 @@ final class ITSEC_Mail {
199
 
200
  $lockouts = $this->replace_all( $lockouts, $replacements );
201
 
202
- $this->add_html( $lockouts, 'lockouts-summary' );
203
  }
204
 
205
  public function add_file_change_summary( $added, $removed, $modified ) {
@@ -216,24 +183,19 @@ final class ITSEC_Mail {
216
 
217
  $lockouts = $this->replace_all( $lockouts, $replacements );
218
 
219
- $this->add_html( $lockouts, 'file-change-summary' );
220
  }
221
 
222
  public function add_button( $link_text, $href ) {
223
- $this->add_html( $this->get_button( $link_text, $href ) );
224
- }
225
-
226
- public function get_button( $link_text, $href ) {
227
-
228
  $module = $this->get_template( 'module-button.html' );
229
  $module = $this->replace( $module, 'href', $href );
230
  $module = $this->replace( $module, 'link_text', $link_text );
231
 
232
- return $module;
233
  }
234
 
235
  public function add_lockouts_table( $lockouts ) {
236
- $entry = $this->get_template( 'lockouts-entry.html' );
237
  $entries = '';
238
 
239
  foreach ( $lockouts as $lockout ) {
@@ -251,15 +213,15 @@ final class ITSEC_Mail {
251
  $table = $this->get_template( 'lockouts-table.html' );
252
 
253
  $replacements = array(
254
- 'heading_types' => __( 'Host/User', 'better-wp-security' ),
255
- 'heading_until' => __( 'Lockout in Effect Until', 'better-wp-security' ),
256
- 'heading_reason' => __( 'Reason', 'better-wp-security' ),
257
- 'entries' => $entries,
258
  );
259
 
260
  $table = $this->replace_all( $table, $replacements );
261
 
262
- $this->add_html( $table, 'lockouts-table' );
263
  }
264
 
265
  /**
@@ -269,10 +231,6 @@ final class ITSEC_Mail {
269
  * @param array[] $entries
270
  */
271
  public function add_table( $headers, $entries ) {
272
- $this->add_html( $this->get_table( $headers, $entries ) );
273
- }
274
-
275
- public function get_table( $headers, $entries ) {
276
 
277
  $template = $this->get_template( 'table.html' );
278
  $html = $this->build_table_header( $headers );
@@ -281,7 +239,7 @@ final class ITSEC_Mail {
281
  $html .= $this->build_table_row( $entry, count( $headers ) );
282
  }
283
 
284
- return $this->replace( $template, 'html', $html );
285
  }
286
 
287
  /**
@@ -348,10 +306,6 @@ final class ITSEC_Mail {
348
  * @param bool $bold_first Whether to emphasize the first item of the list.
349
  */
350
  public function add_list( $items, $bold_first = false ) {
351
- $this->add_html( $this->get_list( $items, $bold_first ) );
352
- }
353
-
354
- public function get_list( $items, $bold_first = false ) {
355
 
356
  $template = $this->get_template( 'list.html' );
357
  $html = '';
@@ -360,7 +314,7 @@ final class ITSEC_Mail {
360
  $html .= $this->build_list_item( $item, $bold_first && 0 === $i );
361
  }
362
 
363
- return $this->replace( $template, 'html', $html );
364
  }
365
 
366
  private function build_list_item( $item, $bold = false ) {
@@ -369,55 +323,13 @@ final class ITSEC_Mail {
369
  return "<li style=\"margin: 0; padding: 5px 10px;{$bold_tag}\">{$item}</li>";
370
  }
371
 
372
- /**
373
- * Add a section of HTML to the email.
374
- *
375
- * @param string $html
376
- * @param string|null $identifier
377
- */
378
- public function add_html( $html, $identifier = null ) {
379
-
380
- if ( null !== $this->current_group ) {
381
- $this->deferred .= $html;
382
- } elseif ( null !== $identifier ) {
383
- $this->groups[ $identifier ] = $html;
384
- } else {
385
- $this->groups[] = $html;
386
- }
387
- }
388
-
389
- public function start_group( $identifier ) {
390
- $this->current_group = $identifier;
391
- }
392
-
393
- public function end_group() {
394
- $group = $this->current_group;
395
- $deferred = $this->deferred;
396
-
397
- $this->current_group = null;
398
- $this->deferred = '';
399
-
400
- $this->add_html( $deferred, $group );
401
- }
402
-
403
  /**
404
  * Include debug info in the email.
405
  *
406
  * This is automatically included in non-user emails if ITSEC_DEBUG is turned on.
407
  */
408
  public function include_debug_info() {
409
-
410
- if ( ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( function_exists( 'wp_doing_cron' ) && wp_doing_cron() ) ) {
411
- $page = 'WP-Cron';
412
- } elseif ( defined( 'WP_CLI' ) && WP_CLI ) {
413
- $page = 'WP-CLI';
414
- } elseif ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
415
- $page = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
416
- } else {
417
- $page = 'unknown';
418
- }
419
-
420
- $this->add_text( sprintf( esc_html__( 'Debug info (source page): %s', 'better-wp-security' ), esc_html( $page ) ) );
421
  }
422
 
423
  /**
@@ -446,22 +358,8 @@ final class ITSEC_Mail {
446
  $this->content = $content;
447
  }
448
 
449
- public function get_content( $recipient = '' ) {
450
-
451
- $groups = $this->groups;
452
-
453
- if ( $this->name ) {
454
- /**
455
- * Filter the HTML groups before building the content.
456
- *
457
- * @param array $groups
458
- * @param ITSEC_Mail $this
459
- * @param string $recipient
460
- */
461
- $groups = apply_filters( "itsec_mail_{$this->name}", $groups, $this, $recipient );
462
- }
463
-
464
- return implode( '', $groups );
465
  }
466
 
467
  public function set_subject( $subject, $add_site_url = true ) {
@@ -498,7 +396,7 @@ final class ITSEC_Mail {
498
  }
499
 
500
  public function set_default_recipients() {
501
- $recipients = ITSEC_Modules::get_setting( 'global', 'notification_email' );
502
  $this->set_recipients( $recipients );
503
  }
504
 
@@ -523,25 +421,7 @@ final class ITSEC_Mail {
523
  $this->set_default_subject();
524
  }
525
 
526
- $headers = array(
527
- 'Content-Type: text/html; charset=UTF-8',
528
- );
529
-
530
- if ( $from = ITSEC_Modules::get_setting( 'notification-center', 'from_email' ) ) {
531
- $headers[] = "From: <{$from}>";
532
- }
533
-
534
- if ( $this->name ) {
535
- $result = true;
536
-
537
- foreach ( $this->recipients as $recipient ) {
538
- $result = wp_mail( $recipient, $this->get_subject(), $this->content ? $this->content : $this->get_content( $recipient ), $headers, $this->attachments ) && $result;
539
- }
540
-
541
- return $result;
542
- }
543
-
544
- return wp_mail( $this->recipients, $this->get_subject(), $this->content ? $this->content : $this->get_content(), $headers, $this->attachments );
545
  }
546
 
547
  /**
1
  <?php
2
 
3
  final class ITSEC_Mail {
 
4
  private $content = '';
 
 
 
5
  private $subject = '';
6
  private $recipients = array();
7
  private $attachments = array();
8
  private $template_path = '';
9
 
10
+ public function __construct() {
11
  $this->template_path = dirname( __FILE__ ) . '/mail-templates/';
 
12
  }
13
 
14
  public function add_header( $title, $banner_title, $use_site_logo = false ) {
31
  'title' => $title,
32
  );
33
 
34
+ $this->content .= $this->replace_all( $header, $replacements );
35
  }
36
 
37
  public function add_footer() {
74
 
75
  );
76
 
77
+ $this->content .= $this->replace_all( $footer, $replacements );
78
 
79
  if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG ) {
80
  $this->include_debug_info();
81
  }
82
 
83
+ $this->content .= $this->get_template( 'close.html' );
84
  }
85
 
86
  public function add_user_footer() {
96
  ) );
97
 
98
  $footer .= $this->get_template( 'close.html' );
99
+ $this->content .= $footer;
100
  }
101
 
102
  public function add_text( $content ) {
 
 
 
 
103
  $module = $this->get_template( 'text.html' );
104
  $module = $this->replace( $module, 'content', $content );
105
 
106
+ $this->content .= $module;
107
  }
108
 
109
  public function add_divider() {
110
+ $this->content .= $this->get_template( 'divider.html' );
 
 
 
 
111
  }
112
 
113
  public function add_large_text( $content ) {
 
 
 
 
114
  $module = $this->get_template( 'large-text.html' );
115
  $module = $this->replace( $module, 'content', $content );
116
 
117
+ $this->content .= $module;
118
  }
119
 
120
  public function add_info_box( $content, $icon_type = 'info' ) {
 
 
 
 
121
  $icon_url = $this->get_image_url( $icon_type === 'warning' ? 'warning_icon_yellow' : "{$icon_type}_icon" );
122
 
123
  $module = $this->get_template( 'info-box.html' );
124
  $module = $this->replace_all( $module, compact( 'content', 'icon_url' ) );
125
 
126
+ $this->content .= $module;
127
  }
128
 
129
  public function add_details_box( $content ) {
 
 
 
 
130
  $module = $this->get_template( 'details-box.html' );
131
  $module = $this->replace( $module, 'content', $content );
132
 
133
+ $this->content .= $module;
134
  }
135
 
136
  public function add_large_code( $content ) {
 
 
 
 
137
  $module = $this->get_template( 'large-code.html' );
138
  $module = $this->replace( $module, 'content', $content );
139
 
140
+ $this->content .= $module;
141
  }
142
 
143
  public function add_section_heading( $content, $icon_type = false ) {
 
 
 
 
144
  if ( empty( $icon_type ) ) {
145
  $heading = $this->get_template( 'section-heading.html' );
146
  $heading = $this->replace_all( $heading, compact( 'content' ) );
151
  $heading = $this->replace_all( $heading, compact( 'content', 'icon_url' ) );
152
  }
153
 
154
+ $this->content .= $heading;
155
  }
156
 
157
  public function add_lockouts_summary( $user_count, $host_count ) {
166
 
167
  $lockouts = $this->replace_all( $lockouts, $replacements );
168
 
169
+ $this->content .= $lockouts;
170
  }
171
 
172
  public function add_file_change_summary( $added, $removed, $modified ) {
183
 
184
  $lockouts = $this->replace_all( $lockouts, $replacements );
185
 
186
+ $this->content .= $lockouts;
187
  }
188
 
189
  public function add_button( $link_text, $href ) {
 
 
 
 
 
190
  $module = $this->get_template( 'module-button.html' );
191
  $module = $this->replace( $module, 'href', $href );
192
  $module = $this->replace( $module, 'link_text', $link_text );
193
 
194
+ $this->content .= $module;
195
  }
196
 
197
  public function add_lockouts_table( $lockouts ) {
198
+ $entry = $this->get_template( 'lockouts-entry.html' );
199
  $entries = '';
200
 
201
  foreach ( $lockouts as $lockout ) {
213
  $table = $this->get_template( 'lockouts-table.html' );
214
 
215
  $replacements = array(
216
+ 'heading_types' => __( 'Host/User', 'better-wp-security' ),
217
+ 'heading_until' => __( 'Lockout in Effect Until', 'better-wp-security' ),
218
+ 'heading_reason' => __( 'Reason', 'better-wp-security' ),
219
+ 'entries' => $entries,
220
  );
221
 
222
  $table = $this->replace_all( $table, $replacements );
223
 
224
+ $this->content .= $table;
225
  }
226
 
227
  /**
231
  * @param array[] $entries
232
  */
233
  public function add_table( $headers, $entries ) {
 
 
 
 
234
 
235
  $template = $this->get_template( 'table.html' );
236
  $html = $this->build_table_header( $headers );
239
  $html .= $this->build_table_row( $entry, count( $headers ) );
240
  }
241
 
242
+ $this->content .= $this->replace( $template, 'html', $html );
243
  }
244
 
245
  /**
306
  * @param bool $bold_first Whether to emphasize the first item of the list.
307
  */
308
  public function add_list( $items, $bold_first = false ) {
 
 
 
 
309
 
310
  $template = $this->get_template( 'list.html' );
311
  $html = '';
314
  $html .= $this->build_list_item( $item, $bold_first && 0 === $i );
315
  }
316
 
317
+ $this->content .= $this->replace( $template, 'html', $html );
318
  }
319
 
320
  private function build_list_item( $item, $bold = false ) {
323
  return "<li style=\"margin: 0; padding: 5px 10px;{$bold_tag}\">{$item}</li>";
324
  }
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  /**
327
  * Include debug info in the email.
328
  *
329
  * This is automatically included in non-user emails if ITSEC_DEBUG is turned on.
330
  */
331
  public function include_debug_info() {
332
+ $this->add_text( sprintf( esc_html__( 'Debug info (source page): %s', 'better-wp-security' ), esc_url( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) );
 
 
 
 
 
 
 
 
 
 
 
333
  }
334
 
335
  /**
358
  $this->content = $content;
359
  }
360
 
361
+ public function get_content() {
362
+ return $this->content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  }
364
 
365
  public function set_subject( $subject, $add_site_url = true ) {
396
  }
397
 
398
  public function set_default_recipients() {
399
+ $recipients = ITSEC_Modules::get_setting( 'global', 'notification_email' );
400
  $this->set_recipients( $recipients );
401
  }
402
 
421
  $this->set_default_subject();
422
  }
423
 
424
+ return wp_mail( $this->recipients, $this->subject, $this->content, array( 'Content-Type: text/html; charset=UTF-8' ), $this->attachments );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  }
426
 
427
  /**
core/lib/class-itsec-scheduler-cron.php CHANGED
@@ -12,19 +12,15 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
12
 
13
  public function register_cron_schedules( $schedules ) {
14
 
15
- $schedules[ 'itsec-' . self::S_TWICE_HOURLY ] = array(
16
- 'display' => esc_html__( 'Twice Hourly', 'better-wp-security' ),
17
- 'interval' => HOUR_IN_SECONDS / 2,
18
- );
19
- $schedules[ 'itsec-' . self::S_FOUR_DAILY ] = array(
20
  'display' => esc_html__( 'Four Times per Day', 'better-wp-security' ),
21
  'interval' => DAY_IN_SECONDS / 4,
22
  );
23
- $schedules[ 'itsec-' . self::S_WEEKLY ] = array(
24
  'display' => esc_html__( 'Weekly', 'better-wp-security' ),
25
  'interval' => WEEK_IN_SECONDS,
26
  );
27
- $schedules[ 'itsec-' . self::S_MONTHLY ] = array(
28
  'display' => esc_html__( 'Monthly', 'better-wp-security' ),
29
  'interval' => MONTH_IN_SECONDS,
30
  );
@@ -66,18 +62,20 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
66
  $this->run_single_event_by_hash( $id, $this->hash_data( $data ) );
67
  }
68
 
69
- public function run_single_event_by_hash( $id, $hash ) {
 
 
 
 
 
 
70
 
71
  $opts = array( 'single' => true );
72
 
73
  $storage = $this->get_options();
 
74
 
75
- if ( ! isset( $storage['single'][ $id ][ $hash ] ) ) {
76
- return;
77
- }
78
-
79
- $data = $storage['single'][ $id ][ $hash ]['data'];
80
- $job = $this->make_job( $id, $data, $opts );
81
 
82
  $this->unschedule_single( $id, $data );
83
  $this->call_action( $job );
@@ -90,8 +88,6 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
90
  }
91
 
92
  if ( ! $crons = _get_cron_array() ) {
93
- ITSEC_Lib::release_lock( 'scheduler' );
94
-
95
  return;
96
  }
97
 
@@ -101,14 +97,12 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
101
  }
102
 
103
  if ( get_transient( 'doing_cron' ) ) {
104
- ITSEC_Lib::release_lock( 'scheduler' );
105
  is_multisite() && restore_current_blog();
106
 
107
  return;
108
  }
109
 
110
  if ( ITSEC_Lib::get_uncached_option( '_transient_doing_cron' ) ) {
111
- ITSEC_Lib::release_lock( 'scheduler' );
112
  is_multisite() && restore_current_blog();
113
 
114
  return;
@@ -353,7 +347,6 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
353
  'id' => $id,
354
  'data' => $options['single'][ $id ][ $hash ]['data'],
355
  'fire_at' => $timestamp,
356
- 'hash' => $hash,
357
  );
358
  }
359
  }
12
 
13
  public function register_cron_schedules( $schedules ) {
14
 
15
+ $schedules[ 'itsec-' . self::S_FOUR_DAILY ] = array(
 
 
 
 
16
  'display' => esc_html__( 'Four Times per Day', 'better-wp-security' ),
17
  'interval' => DAY_IN_SECONDS / 4,
18
  );
19
+ $schedules[ 'itsec-' . self::S_WEEKLY ] = array(
20
  'display' => esc_html__( 'Weekly', 'better-wp-security' ),
21
  'interval' => WEEK_IN_SECONDS,
22
  );
23
+ $schedules[ 'itsec-' . self::S_MONTHLY ] = array(
24
  'display' => esc_html__( 'Monthly', 'better-wp-security' ),
25
  'interval' => MONTH_IN_SECONDS,
26
  );
62
  $this->run_single_event_by_hash( $id, $this->hash_data( $data ) );
63
  }
64
 
65
+ /**
66
+ * Run a single event.
67
+ *
68
+ * @param string $id
69
+ * @param string $hash
70
+ */
71
+ private function run_single_event_by_hash( $id, $hash ) {
72
 
73
  $opts = array( 'single' => true );
74
 
75
  $storage = $this->get_options();
76
+ $data = $storage['single'][ $id ][ $hash ]['data'];
77
 
78
+ $job = $this->make_job( $id, $data, $opts );
 
 
 
 
 
79
 
80
  $this->unschedule_single( $id, $data );
81
  $this->call_action( $job );
88
  }
89
 
90
  if ( ! $crons = _get_cron_array() ) {
 
 
91
  return;
92
  }
93
 
97
  }
98
 
99
  if ( get_transient( 'doing_cron' ) ) {
 
100
  is_multisite() && restore_current_blog();
101
 
102
  return;
103
  }
104
 
105
  if ( ITSEC_Lib::get_uncached_option( '_transient_doing_cron' ) ) {
 
106
  is_multisite() && restore_current_blog();
107
 
108
  return;
347
  'id' => $id,
348
  'data' => $options['single'][ $id ][ $hash ]['data'],
349
  'fire_at' => $timestamp,
 
350
  );
351
  }
352
  }
core/lib/class-itsec-scheduler-page-load.php CHANGED
@@ -159,7 +159,6 @@ class ITSEC_Scheduler_Page_Load extends ITSEC_Scheduler {
159
  'id' => $id,
160
  'data' => $event['data'],
161
  'fire_at' => $event['fire_at'],
162
- 'hash' => $hash,
163
  );
164
  }
165
  }
@@ -266,13 +265,6 @@ class ITSEC_Scheduler_Page_Load extends ITSEC_Scheduler {
266
  }
267
 
268
  public function run_single_event( $id, $data = array() ) {
269
- $this->run_single_event_by_hash( $id, $this->hash_data( $data ) );
270
- }
271
-
272
- /**
273
- * @inheritDoc
274
- */
275
- public function run_single_event_by_hash( $id, $hash ) {
276
 
277
  if ( $this->operating_data ) {
278
  $clear_operating_data = false;
@@ -282,19 +274,12 @@ class ITSEC_Scheduler_Page_Load extends ITSEC_Scheduler {
282
  $storage = $this->operating_data = $this->get_options();
283
  }
284
 
285
- if ( ! isset( $storage['single'][ $id ][ $hash ] ) ) {
286
- if ( $clear_operating_data ) {
287
- $this->operating_data = null;
288
- }
289
-
290
- return;
291
- }
292
-
293
  $event = $storage['single'][ $id ][ $hash ];
294
 
295
  $job = $this->make_job( $id, $event['data'], array( 'single' => true ) );
296
 
297
- $this->unschedule_single( $id, $event['data'] );
298
  $this->call_action( $job );
299
 
300
  if ( $clear_operating_data ) {
159
  'id' => $id,
160
  'data' => $event['data'],
161
  'fire_at' => $event['fire_at'],
 
162
  );
163
  }
164
  }
265
  }
266
 
267
  public function run_single_event( $id, $data = array() ) {
 
 
 
 
 
 
 
268
 
269
  if ( $this->operating_data ) {
270
  $clear_operating_data = false;
274
  $storage = $this->operating_data = $this->get_options();
275
  }
276
 
277
+ $hash = $this->hash_data( $data );
 
 
 
 
 
 
 
278
  $event = $storage['single'][ $id ][ $hash ];
279
 
280
  $job = $this->make_job( $id, $event['data'], array( 'single' => true ) );
281
 
282
+ $this->unschedule_single( $id, $data );
283
  $this->call_action( $job );
284
 
285
  if ( $clear_operating_data ) {
core/lib/class-itsec-scheduler.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  abstract class ITSEC_Scheduler {
4
 
5
- const S_TWICE_HOURLY = 'twice-hourly';
6
  const S_HOURLY = 'hourly';
7
  const S_FOUR_DAILY = 'four-daily';
8
  const S_TWICE_DAILY = 'twice-daily';
@@ -141,7 +140,6 @@ abstract class ITSEC_Scheduler {
141
  * - id: The ID the event was scheduled with.
142
  * - data: The data the event was scheduled with.
143
  * - fire_at: The time the event should be fired.
144
- * - hash: The event's data hash.
145
  *
146
  * @return array
147
  */
@@ -170,16 +168,6 @@ abstract class ITSEC_Scheduler {
170
  */
171
  abstract public function run_single_event( $id, $data = array() );
172
 
173
- /**
174
- * Run a single event by it's hash.
175
- *
176
- * @param string $id
177
- * @param string $hash
178
- *
179
- * @return void
180
- */
181
- abstract public function run_single_event_by_hash( $id, $hash );
182
-
183
  /**
184
  * Run any events that are due now.
185
  *
@@ -338,8 +326,6 @@ abstract class ITSEC_Scheduler {
338
  */
339
  final public function get_schedule_interval( $schedule ) {
340
  switch ( $schedule ) {
341
- case self::S_TWICE_HOURLY:
342
- return HOUR_IN_SECONDS / 2;
343
  case self::S_HOURLY:
344
  return HOUR_IN_SECONDS;
345
  case self::S_FOUR_DAILY:
2
 
3
  abstract class ITSEC_Scheduler {
4
 
 
5
  const S_HOURLY = 'hourly';
6
  const S_FOUR_DAILY = 'four-daily';
7
  const S_TWICE_DAILY = 'twice-daily';
140
  * - id: The ID the event was scheduled with.
141
  * - data: The data the event was scheduled with.
142
  * - fire_at: The time the event should be fired.
 
143
  *
144
  * @return array
145
  */
168
  */
169
  abstract public function run_single_event( $id, $data = array() );
170
 
 
 
 
 
 
 
 
 
 
 
171
  /**
172
  * Run any events that are due now.
173
  *
326
  */
327
  final public function get_schedule_interval( $schedule ) {
328
  switch ( $schedule ) {
 
 
329
  case self::S_HOURLY:
330
  return HOUR_IN_SECONDS;
331
  case self::S_FOUR_DAILY:
core/lib/class-itsec-wp-list-table.php CHANGED
@@ -757,9 +757,9 @@ class ITSEC_WP_List_Table {
757
  }
758
 
759
  if ( $disable_first ) {
760
- $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
761
  } else {
762
- $page_links[] = sprintf( "<a class='first-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
763
  esc_url( remove_query_arg( 'paged', $current_url ) ),
764
  __( 'First page' ),
765
  '&laquo;'
@@ -767,9 +767,9 @@ class ITSEC_WP_List_Table {
767
  }
768
 
769
  if ( $disable_prev ) {
770
- $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
771
  } else {
772
- $page_links[] = sprintf( "<a class='prev-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
773
  esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
774
  __( 'Previous page' ),
775
  '&lsaquo;'
@@ -790,9 +790,9 @@ class ITSEC_WP_List_Table {
790
  $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
791
 
792
  if ( $disable_next ) {
793
- $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
794
  } else {
795
- $page_links[] = sprintf( "<a class='next-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
796
  esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
797
  __( 'Next page' ),
798
  '&rsaquo;'
@@ -800,9 +800,9 @@ class ITSEC_WP_List_Table {
800
  }
801
 
802
  if ( $disable_last ) {
803
- $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
804
  } else {
805
- $page_links[] = sprintf( "<a class='last-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
806
  esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
807
  __( 'Last page' ),
808
  '&raquo;'
@@ -1134,7 +1134,9 @@ class ITSEC_WP_List_Table {
1134
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
1135
 
1136
  <?php if ( $this->has_items() ): ?>
1137
- <div class="alignleft actions bulkactions"><?php $this->bulk_actions( $which ); ?></div>
 
 
1138
  <?php endif;
1139
  $this->extra_tablenav( $which );
1140
  $this->pagination( $which );
757
  }
758
 
759
  if ( $disable_first ) {
760
+ $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&laquo;</span>';
761
  } else {
762
+ $page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
763
  esc_url( remove_query_arg( 'paged', $current_url ) ),
764
  __( 'First page' ),
765
  '&laquo;'
767
  }
768
 
769
  if ( $disable_prev ) {
770
+ $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>';
771
  } else {
772
+ $page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
773
  esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
774
  __( 'Previous page' ),
775
  '&lsaquo;'
790
  $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
791
 
792
  if ( $disable_next ) {
793
+ $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>';
794
  } else {
795
+ $page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
796
  esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
797
  __( 'Next page' ),
798
  '&rsaquo;'
800
  }
801
 
802
  if ( $disable_last ) {
803
+ $page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&raquo;</span>';
804
  } else {
805
+ $page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
806
  esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
807
  __( 'Last page' ),
808
  '&raquo;'
1134
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
1135
 
1136
  <?php if ( $this->has_items() ): ?>
1137
+ <div class="alignleft actions bulkactions">
1138
+ <?php $this->bulk_actions( $which ); ?>
1139
+ </div>
1140
  <?php endif;
1141
  $this->extra_tablenav( $which );
1142
  $this->pagination( $which );
core/lib/form.php CHANGED
@@ -27,9 +27,6 @@ final class ITSEC_Form {
27
  parse_str( $data['data']['--itsec-form-serialized-data'], $data );
28
  }
29
 
30
- if ( get_magic_quotes_gpc() ) {
31
- $data = stripslashes_deep( $data );
32
- }
33
 
34
  $defaults = array(
35
  'booleans' => false,
@@ -465,42 +462,6 @@ final class ITSEC_Form {
465
  $this->add_custom_input( $var, $options );
466
  }
467
 
468
- public function add_canonical_roles( $var, $options = array() ) {
469
- $roles = array(
470
- 'administrator' => translate_user_role( 'Administrator' ),
471
- 'editor' => translate_user_role( 'Editor' ),
472
- 'author' => translate_user_role( 'Author' ),
473
- 'contributor' => translate_user_role( 'Contributor' ),
474
- 'subscriber' => translate_user_role( 'Subscriber' ),
475
- );
476
-
477
- if ( isset( $options['value'] ) ) {
478
- $options['value'] = wp_parse_args( $options['value'], $roles );
479
- } else {
480
- $options['value'] = $roles;
481
- }
482
-
483
- $this->add_select( $var, $options );
484
- }
485
-
486
- public function get_clean_var( $var ) {
487
- $clean_var = trim( preg_replace( '/[^a-z0-9_]+/i', '-', $var ), '-' );
488
-
489
- if ( ! empty( $this->input_group ) ) {
490
- if ( false === strpos( $var, '[' ) ) {
491
- $var = "[{$var}]";
492
- } else {
493
- $var = preg_replace( '/^([^\[]+)\[/', '[$1][', $var );
494
- }
495
-
496
- $var = "{$this->input_group}{$var}";
497
-
498
- $clean_var = trim( preg_replace( '/[^a-z0-9_]+/i', '-', $var ), '-' );
499
- }
500
-
501
- return "itsec-$clean_var";
502
- }
503
-
504
  private function add_custom_input( $var, $options ) {
505
  if ( empty( $options['type'] ) ) {
506
  trigger_error( 'add_custom_input called without a type option set' );
27
  parse_str( $data['data']['--itsec-form-serialized-data'], $data );
28
  }
29
 
 
 
 
30
 
31
  $defaults = array(
32
  'booleans' => false,
462
  $this->add_custom_input( $var, $options );
463
  }
464
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  private function add_custom_input( $var, $options ) {
466
  if ( empty( $options['type'] ) ) {
467
  trigger_error( 'add_custom_input called without a type option set' );
core/lib/log-util.php CHANGED
@@ -42,22 +42,6 @@ final class ITSEC_Log_Util {
42
  global $wpdb;
43
 
44
 
45
- $valid_columns = array(
46
- 'id',
47
- 'parent_id',
48
- 'module',
49
- 'type',
50
- 'code',
51
- 'timestamp',
52
- 'init_timestamp',
53
- 'remote_ip',
54
- 'user_id',
55
- 'url',
56
- 'memory_current',
57
- 'memory_peak',
58
- );
59
-
60
-
61
  $get_count = false;
62
  $min_timestamp = false;
63
 
@@ -77,29 +61,27 @@ final class ITSEC_Log_Util {
77
  $limit = max( 0, min( 100, intval( $limit ) ) );
78
  $page = max( 1, intval( $page ) );
79
 
80
- if ( is_array( $sort_by_column ) ) {
81
- $regex_valid_columns = '(?:' . implode( '|', $valid_columns ) . ')';
82
-
83
- foreach ( $sort_by_column as $index => $sort_by ) {
84
- if ( in_array( $sort_by, $valid_columns ) ) {
85
- $sort_by_column[$index] = "$sort_by DESC";
86
- } else if ( ! preg_match( "/^$regex_valid_columns\s+(?:DESC|ASC)$/i", $sort_by ) ) {
87
- unset( $sort_by_column[$index] );
88
- }
89
- }
90
-
91
- if ( empty( $sort_by_column ) ) {
92
- $sort_by_column = 'timestamp';
93
- }
94
- } else if ( ! in_array( $sort_by_column, $valid_columns ) ) {
95
- $sort_by_column = 'timestamp';
96
- }
97
-
98
  $sort_direction = strtoupper( $sort_direction );
99
  if ( ! in_array( $sort_direction, array( 'DESC', 'ASC' ) ) ) {
100
  $sort_direction = 'DESC';
101
  }
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  if ( false === $columns ) {
104
  $columns = $valid_columns;
105
  } else if ( 'all' === $columns ) {
42
  global $wpdb;
43
 
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  $get_count = false;
46
  $min_timestamp = false;
47
 
61
  $limit = max( 0, min( 100, intval( $limit ) ) );
62
  $page = max( 1, intval( $page ) );
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  $sort_direction = strtoupper( $sort_direction );
65
  if ( ! in_array( $sort_direction, array( 'DESC', 'ASC' ) ) ) {
66
  $sort_direction = 'DESC';
67
  }
68
 
69
+
70
+ $valid_columns = array(
71
+ 'id',
72
+ 'parent_id',
73
+ 'module',
74
+ 'type',
75
+ 'code',
76
+ 'timestamp',
77
+ 'init_timestamp',
78
+ 'remote_ip',
79
+ 'user_id',
80
+ 'url',
81
+ 'memory_current',
82
+ 'memory_peak',
83
+ );
84
+
85
  if ( false === $columns ) {
86
  $columns = $valid_columns;
87
  } else if ( 'all' === $columns ) {
core/lib/mail-templates/header.html CHANGED
@@ -77,7 +77,6 @@
77
  #security-guide a{font-weight:bold;}
78
  #footer-source-details .container-cell{line-height:200%;padding-top:60px;padding-bottom:0;}
79
  #footer-source-details a{font-size:11px;font-weight:bold;line-height:200%;}
80
- .template-container {max-width: 600px !important;}
81
 
82
  @media only screen and (max-width:600px){
83
  body{width:100% !important;min-width:100% !important;}
77
  #security-guide a{font-weight:bold;}
78
  #footer-source-details .container-cell{line-height:200%;padding-top:60px;padding-bottom:0;}
79
  #footer-source-details a{font-size:11px;font-weight:bold;line-height:200%;}
 
80
 
81
  @media only screen and (max-width:600px){
82
  body{width:100% !important;min-width:100% !important;}
core/lib/schema.php CHANGED
@@ -96,6 +96,5 @@ CREATE TABLE {$wpdb->base_prefix}itsec_distributed_storage (
96
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_log;" );
97
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_lockouts;" );
98
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_temp;" );
99
- $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_distributed_storage;" );
100
  }
101
  }
96
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_log;" );
97
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_lockouts;" );
98
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_temp;" );
 
99
  }
100
  }
core/lib/settings.php CHANGED
@@ -2,45 +2,45 @@
2
 
3
  abstract class ITSEC_Settings {
4
  protected $settings;
5
-
6
  public function __construct() {
7
  $this->load();
8
-
9
  add_action( 'itsec-lib-clear-caches', array( $this, 'load' ), 0 );
10
  }
11
-
12
  abstract public function get_id();
13
  abstract public function get_defaults();
14
  protected function after_save() {}
15
  protected function handle_settings_changes( $old_settings ) {}
16
-
17
  public function export() {
18
  return $this->settings;
19
  }
20
-
21
  public function import( $settings ) {
22
  $this->set_all( $settings );
23
  }
24
-
25
  public function get( $name, $default = null ) {
26
  if ( isset( $this->settings[$name] ) ) {
27
  return $this->settings[$name];
28
  }
29
-
30
  return $default;
31
  }
32
-
33
  public function get_all() {
34
  return $this->settings;
35
  }
36
-
37
  public function set( $name, $value ) {
38
  $settings = $this->settings;
39
  $settings[$name] = $value;
40
-
41
  return $this->set_all( $settings );
42
  }
43
-
44
  public function set_all( $settings ) {
45
  $retval = array(
46
  'old_settings' => $this->settings,
@@ -49,47 +49,45 @@ abstract class ITSEC_Settings {
49
  'messages' => array(),
50
  'saved' => false,
51
  );
52
-
53
  $validator = ITSEC_Modules::get_validator( $this->get_id() );
54
-
55
  if ( is_null( $validator ) ) {
56
  $retval['errors'][] = new WP_Error( 'itsec-settings-missing-validator-for-' . $this->get_id(), sprintf( __( 'The data validator for %1$s is missing. Data for the module cannot be saved without the validator. This error could indicate a bad install of iThemes Security. Please remove the plugin and reinstall it. If this message persists, please contact support and send them this error message.', 'better-wp-security' ), $this->get_id() ) );
57
  } else {
58
  $validator->validate( $settings );
59
-
60
  $retval['errors'] = $validator->get_errors();
61
  $retval['messages'] = $validator->get_messages();
62
-
63
  if ( $validator->can_save() ) {
64
  $this->settings = $validator->get_settings();
65
-
66
  ITSEC_Storage::set( $this->get_id(), $this->settings );
67
  $this->after_save();
68
  $this->handle_settings_changes( $retval['old_settings'] );
69
-
70
  $retval['new_settings'] = $this->settings;
71
  $retval['saved'] = true;
72
-
73
- do_action( 'itsec-settings-updated', $this->get_id() );
74
  } else {
75
  ITSEC_Response::set_success( false );
76
  }
77
  }
78
-
79
  ITSEC_Response::add_errors( $retval['errors'] );
80
  ITSEC_Response::add_messages( $retval['messages'] );
81
-
82
  return $retval;
83
  }
84
-
85
  public function load() {
86
  $this->settings = ITSEC_Storage::get( $this->get_id() );
87
  $defaults = $this->get_defaults();
88
-
89
  if ( ! is_array( $this->settings ) ) {
90
  $this->settings = array();
91
  }
92
-
93
  $this->settings = array_merge( $defaults, $this->settings );
94
  }
95
  }
2
 
3
  abstract class ITSEC_Settings {
4
  protected $settings;
5
+
6
  public function __construct() {
7
  $this->load();
8
+
9
  add_action( 'itsec-lib-clear-caches', array( $this, 'load' ), 0 );
10
  }
11
+
12
  abstract public function get_id();
13
  abstract public function get_defaults();
14
  protected function after_save() {}
15
  protected function handle_settings_changes( $old_settings ) {}
16
+
17
  public function export() {
18
  return $this->settings;
19
  }
20
+
21
  public function import( $settings ) {
22
  $this->set_all( $settings );
23
  }
24
+
25
  public function get( $name, $default = null ) {
26
  if ( isset( $this->settings[$name] ) ) {
27
  return $this->settings[$name];
28
  }
29
+
30
  return $default;
31
  }
32
+
33
  public function get_all() {
34
  return $this->settings;
35
  }
36
+
37
  public function set( $name, $value ) {
38
  $settings = $this->settings;
39
  $settings[$name] = $value;
40
+
41
  return $this->set_all( $settings );
42
  }
43
+
44
  public function set_all( $settings ) {
45
  $retval = array(
46
  'old_settings' => $this->settings,
49
  'messages' => array(),
50
  'saved' => false,
51
  );
52
+
53
  $validator = ITSEC_Modules::get_validator( $this->get_id() );
54
+
55
  if ( is_null( $validator ) ) {
56
  $retval['errors'][] = new WP_Error( 'itsec-settings-missing-validator-for-' . $this->get_id(), sprintf( __( 'The data validator for %1$s is missing. Data for the module cannot be saved without the validator. This error could indicate a bad install of iThemes Security. Please remove the plugin and reinstall it. If this message persists, please contact support and send them this error message.', 'better-wp-security' ), $this->get_id() ) );
57
  } else {
58
  $validator->validate( $settings );
59
+
60
  $retval['errors'] = $validator->get_errors();
61
  $retval['messages'] = $validator->get_messages();
62
+
63
  if ( $validator->can_save() ) {
64
  $this->settings = $validator->get_settings();
65
+
66
  ITSEC_Storage::set( $this->get_id(), $this->settings );
67
  $this->after_save();
68
  $this->handle_settings_changes( $retval['old_settings'] );
69
+
70
  $retval['new_settings'] = $this->settings;
71
  $retval['saved'] = true;
 
 
72
  } else {
73
  ITSEC_Response::set_success( false );
74
  }
75
  }
76
+
77
  ITSEC_Response::add_errors( $retval['errors'] );
78
  ITSEC_Response::add_messages( $retval['messages'] );
79
+
80
  return $retval;
81
  }
82
+
83
  public function load() {
84
  $this->settings = ITSEC_Storage::get( $this->get_id() );
85
  $defaults = $this->get_defaults();
86
+
87
  if ( ! is_array( $this->settings ) ) {
88
  $this->settings = array();
89
  }
90
+
91
  $this->settings = array_merge( $defaults, $this->settings );
92
  }
93
  }
core/lib/validator.php CHANGED
@@ -142,19 +142,6 @@ abstract class ITSEC_Validator {
142
  if ( empty( $this->settings[$var] ) ) {
143
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
144
  }
145
- } elseif ( 'text' === $type || 'non-empty-text' === $type ) {
146
- $string = (string) $this->settings[ $var ];
147
- $string = wp_strip_all_tags( $string );
148
-
149
- if ( $trim_value ) {
150
- $string = trim( $string );
151
- }
152
-
153
- $this->settings[ $var ] = $string;
154
-
155
- if ( 'non-empty-text' === $type && empty( $this->settings[ $var ] ) ) {
156
- $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
157
- }
158
  } else if ( 'array' === $type ) {
159
  if ( ! is_array( $this->settings[$var] ) ) {
160
  if ( empty( $this->settings[$var] ) ) {
@@ -318,32 +305,6 @@ abstract class ITSEC_Validator {
318
  $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $type ), 'better-wp-security' ), $name, $type );
319
  $type = 'array';
320
  }
321
- } elseif ( 'canonical-roles' === $type ) {
322
- $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
323
-
324
- if ( is_array( $this->settings[$var] ) ) {
325
- $invalid_entries = array();
326
-
327
- foreach ( $this->settings[$var] as $index => $entry ) {
328
- $entry = sanitize_text_field( trim( $entry ) );
329
- $this->settings[$var][$index] = $entry;
330
-
331
- if ( empty( $entry ) ) {
332
- unset( $this->settings[$var][$index] );
333
- } else if ( ! in_array( $entry, $roles, true ) ) {
334
- $invalid_entries[] = $entry;
335
- }
336
- }
337
-
338
- $this->settings[$var] = array_unique( $this->settings[$var] );
339
-
340
- if ( ! empty( $invalid_entries ) ) {
341
- $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
342
- }
343
- } else if ( ! in_array( $this->settings[$var], $roles, true ) ) {
344
- $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $roles ), 'better-wp-security' ), $name, $roles );
345
- $type = 'array';
346
- }
347
  } else if ( 'newline-separated-array' === $type ) {
348
  $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
349
 
@@ -427,44 +388,13 @@ abstract class ITSEC_Validator {
427
  $error = wp_sprintf( _n( 'The following extension in %1$s is invalid: %2$l', 'The following extensions in %1$s are invalid: %2$l', count( $invalid_extensions ), 'better-wp-security' ), $name, $invalid_extensions );
428
  }
429
  }
430
- } elseif ( is_string( $type ) && 0 === strpos( $type, 'cb-items:' ) ) {
431
-
432
- list( , $method ) = explode( ':', $type );
433
-
434
- if ( ! is_array( $this->settings[ $var ] ) ) {
435
- $error = sprintf( __( 'The %1$s value must be an array.', 'better-wp-security' ), $name );
436
- } else {
437
- $invalid_entries = array();
438
-
439
- foreach ( $this->settings[ $var ] as $index => $entry ) {
440
-
441
- if ( empty( $entry ) ) {
442
- unset( $this->settings[ $var ][ $index ] );
443
- } else {
444
- $result = $this->{$method}( $entry, $index );
445
-
446
- if ( false === $result ) {
447
- $invalid_entries[] = is_string( $entry ) ? $entry : $index;
448
- } elseif ( is_wp_error( $result ) ) {
449
- $invalid_entries[] = "'{$index}': {$result->get_error_message()}";
450
- } else {
451
- $this->settings[ $var ][ $index ] = $result;
452
- }
453
- }
454
- }
455
-
456
- if ( ! empty( $invalid_entries ) ) {
457
- $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
458
- }
459
- }
460
-
461
  } else {
462
  /* translators: 1: sanitize type, 2: input name */
463
  $error = sprintf( __( 'An invalid sanitize type of "%1$s" was received for the %2$s input.', 'better-wp-security' ), $type, $name );
464
  }
465
 
466
  if ( false !== $error ) {
467
- $this->add_error( $this->generate_error( $id, $var, $type, $error ) );
468
  $this->vars_to_skip_validate_matching_types[] = $var;
469
 
470
  if ( $prevent_save_on_error && ITSEC_Core::is_interactive() ) {
@@ -477,10 +407,6 @@ abstract class ITSEC_Validator {
477
  return true;
478
  }
479
 
480
- protected function generate_error( $id, $var, $type, $error ) {
481
- return new WP_Error( "itsec-validator-$id-invalid-type-$var-$type", $error );
482
- }
483
-
484
  final protected function convert_string_to_array( $string ) {
485
  if ( is_string( $string ) ) {
486
  $array = preg_split( "/[\r\n]+/", $string );
142
  if ( empty( $this->settings[$var] ) ) {
143
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
144
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  } else if ( 'array' === $type ) {
146
  if ( ! is_array( $this->settings[$var] ) ) {
147
  if ( empty( $this->settings[$var] ) ) {
305
  $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $type ), 'better-wp-security' ), $name, $type );
306
  $type = 'array';
307
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  } else if ( 'newline-separated-array' === $type ) {
309
  $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
310
 
388
  $error = wp_sprintf( _n( 'The following extension in %1$s is invalid: %2$l', 'The following extensions in %1$s are invalid: %2$l', count( $invalid_extensions ), 'better-wp-security' ), $name, $invalid_extensions );
389
  }
390
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  } else {
392
  /* translators: 1: sanitize type, 2: input name */
393
  $error = sprintf( __( 'An invalid sanitize type of "%1$s" was received for the %2$s input.', 'better-wp-security' ), $type, $name );
394
  }
395
 
396
  if ( false !== $error ) {
397
+ $this->add_error( new WP_Error( "itsec-validator-$id-invalid-type-$var-$type", $error ) );
398
  $this->vars_to_skip_validate_matching_types[] = $var;
399
 
400
  if ( $prevent_save_on_error && ITSEC_Core::is_interactive() ) {
407
  return true;
408
  }
409
 
 
 
 
 
410
  final protected function convert_string_to_array( $string ) {
411
  if ( is_string( $string ) ) {
412
  $array = preg_split( "/[\r\n]+/", $string );
core/lockout.php CHANGED
@@ -84,6 +84,9 @@ final class ITSEC_Lockout {
84
  //Set an error message on improper logout
85
  add_action( 'login_head', array( $this, 'set_lockout_error' ) );
86
 
 
 
 
87
  add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
88
  add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
89
 
@@ -129,7 +132,7 @@ final class ITSEC_Lockout {
129
 
130
  $host = ITSEC_Lib::get_ip();
131
 
132
- if ( $this->is_host_locked_out( $host ) || ITSEC_Lib::is_ip_blacklisted() ) {
133
  $this->execute_lock();
134
  }
135
  }
84
  //Set an error message on improper logout
85
  add_action( 'login_head', array( $this, 'set_lockout_error' ) );
86
 
87
+ //Process clear lockout form
88
+ add_action( 'itsec_admin_init', array( $this, 'release_lockout' ) );
89
+
90
  add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
91
  add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
92
 
132
 
133
  $host = ITSEC_Lib::get_ip();
134
 
135
+ if ( $this->is_host_locked_out( $host ) ) {
136
  $this->execute_lock();
137
  }
138
  }
core/modules/404-detection/class-itsec-four-oh-four.php CHANGED
@@ -10,7 +10,7 @@ class ITSEC_Four_Oh_Four {
10
 
11
  add_filter( 'itsec_lockout_modules', array( $this, 'register_lockout' ) );
12
 
13
- add_action( 'template_redirect', array( $this, 'check_404' ), 9999 );
14
  }
15
 
16
  /**
@@ -29,15 +29,19 @@ class ITSEC_Four_Oh_Four {
29
 
30
  $uri = explode( '?', $_SERVER['REQUEST_URI'] );
31
 
32
- if ( in_array( '/' . ITSEC_Lib::get_request_path(), $this->settings['white_list'], true ) ) {
33
- return; // white listed page.
 
34
  }
35
 
36
  ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => $_SERVER ) );
37
 
38
- if ( ! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'], true ) ) {
 
39
  $itsec_lockout->do_lockout( 'four_oh_four' );
 
40
  }
 
41
  }
42
 
43
  /**
10
 
11
  add_filter( 'itsec_lockout_modules', array( $this, 'register_lockout' ) );
12
 
13
+ add_action( 'wp', array( $this, 'check_404' ), 9999 );
14
  }
15
 
16
  /**
29
 
30
  $uri = explode( '?', $_SERVER['REQUEST_URI'] );
31
 
32
+ if ( in_array( '/' . ITSEC_Lib::get_request_path(), $this->settings['white_list'] ) ) {
33
+ // white listed page.
34
+ return;
35
  }
36
 
37
  ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => $_SERVER ) );
38
 
39
+ if ( ! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'] ) ) {
40
+
41
  $itsec_lockout->do_lockout( 'four_oh_four' );
42
+
43
  }
44
+
45
  }
46
 
47
  /**
core/modules/404-detection/logs.php CHANGED
@@ -4,7 +4,6 @@ final class ITSEC_Four_Oh_Four_Logs {
4
  public function __construct() {
5
  add_filter( 'itsec_logs_prepare_four_oh_four_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
6
  add_filter( 'itsec_logs_prepare_four_oh_four_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
7
- add_filter( 'itsec_logs_prepare_four_oh_four_filter_row_action_for_code', array( $this, 'code_row_action' ), 10, 2 );
8
  }
9
 
10
  public function filter_entry_for_list_display( $entry ) {
@@ -25,9 +24,5 @@ final class ITSEC_Four_Oh_Four_Logs {
25
 
26
  return $details;
27
  }
28
-
29
- public function code_row_action( $vars, $entry ) {
30
- return array( 'filters[10]' => "url|{$entry['url']}", 'filters[11]' => 'module|four_oh_four' );
31
- }
32
  }
33
  new ITSEC_Four_Oh_Four_Logs();
4
  public function __construct() {
5
  add_filter( 'itsec_logs_prepare_four_oh_four_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
6
  add_filter( 'itsec_logs_prepare_four_oh_four_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
 
7
  }
8
 
9
  public function filter_entry_for_list_display( $entry ) {
24
 
25
  return $details;
26
  }
 
 
 
 
27
  }
28
  new ITSEC_Four_Oh_Four_Logs();
core/modules/away-mode/class-itsec-away-mode.php CHANGED
@@ -5,7 +5,7 @@ final class ITSEC_Away_Mode {
5
  public function run() {
6
 
7
  //Execute away mode functions on admin init
8
- add_action( 'admin_init', array( $this, 'run_active_check' ) );
9
  add_action( 'login_init', array( $this, 'run_active_check' ) );
10
 
11
  add_filter( 'itsec_managed_files', array( $this, 'register_managed_file' ) );
@@ -78,11 +78,6 @@ final class ITSEC_Away_Mode {
78
  * @return void
79
  */
80
  public function run_active_check() {
81
-
82
- if ( wp_doing_ajax() ) {
83
- return;
84
- }
85
-
86
  $away_mode_details = self::is_active( true );
87
 
88
  if ( $away_mode_details['active'] ) {
5
  public function run() {
6
 
7
  //Execute away mode functions on admin init
8
+ add_action( 'itsec_admin_init', array( $this, 'run_active_check' ) );
9
  add_action( 'login_init', array( $this, 'run_active_check' ) );
10
 
11
  add_filter( 'itsec_managed_files', array( $this, 'register_managed_file' ) );
78
  * @return void
79
  */
80
  public function run_active_check() {
 
 
 
 
 
81
  $away_mode_details = self::is_active( true );
82
 
83
  if ( $away_mode_details['active'] ) {
core/modules/file-change/class-itsec-file-change.php CHANGED
@@ -77,7 +77,7 @@ class ITSEC_File_Change {
77
  $hashes = ITSEC_Modules::get_setting( 'file-change', 'expected_hashes', array() );
78
  $hash = @md5_file( $file );
79
 
80
- if ( $hash && ( ! isset( $hashes[ $file ] ) || $hashes[ $file ] !== $hash ) ) {
81
  $hashes[ $file ] = $hash;
82
  ITSEC_Modules::set_setting( 'file-change', 'expected_hashes', $hashes );
83
  }
77
  $hashes = ITSEC_Modules::get_setting( 'file-change', 'expected_hashes', array() );
78
  $hash = @md5_file( $file );
79
 
80
+ if ( $hash ) {
81
  $hashes[ $file ] = $hash;
82
  ITSEC_Modules::set_setting( 'file-change', 'expected_hashes', $hashes );
83
  }
core/modules/file-change/css/settings.css CHANGED
@@ -90,17 +90,3 @@ UL.jqueryFileTree LI {
90
  width : 100%;
91
  }
92
  }
93
-
94
- #itsec-file-change-abort {
95
- color: #a00;
96
- text-decoration: none;
97
- border-color: transparent;
98
- box-shadow: none;
99
- background: transparent;
100
- }
101
-
102
- #itsec-file-change-abort:hover {
103
- background: #d54e21;
104
- color: #fff;
105
- border-color: #d54e21;
106
- }
90
  width : 100%;
91
  }
92
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/file-change/js/settings-page.js CHANGED
@@ -62,19 +62,4 @@ jQuery( document ).ready( function ( $ ) {
62
  }
63
 
64
  initializeScan();
65
-
66
- $( document ).on( 'click', '#itsec-file-change-abort', function () {
67
- var $this = $( this );
68
-
69
- $this.prop( 'disabled', true );
70
-
71
- itsecUtil.sendModuleAJAXRequest( 'file-change', { method: 'abort' }, function ( results ) {
72
- var $button = $( '#itsec-file-change-one_time_check' );
73
- $button.prop( 'disabled', false );
74
- $button.prop( 'class', 'button-primary' );
75
- $button.val( ITSECFileChangeScannerl10n.button_text );
76
-
77
- $this.remove();
78
- } );
79
- } );
80
  } );
62
  }
63
 
64
  initializeScan();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  } );
core/modules/file-change/logs.php CHANGED
@@ -44,17 +44,7 @@ final class ITSEC_File_Change_Logs {
44
  } elseif ( 'recovery-scheduled' === $code ) {
45
  $entry['description'] = esc_html__( 'Recovery Scheduled', 'better-wp-security' );
46
  } elseif ( 'file-scan-aborted' === $code ) {
47
- if ( ! empty( $code_data[0] ) ) {
48
- if ( $user = get_userdata( $code_data[0] ) ) {
49
- $by = $user->display_name;
50
- } else {
51
- $by = "#{$code_data[0]}";
52
- }
53
-
54
- $entry['description'] = sprintf( esc_html__( 'Scan Cancelled by %s', 'better-wp-security' ), $by );
55
- } else {
56
- $entry['description'] = esc_html__( 'Scan Failed', 'better-wp-security' );
57
- }
58
  } elseif ( 'rescheduling' === $code ) {
59
  $entry['description'] = esc_html__( 'Rescheduling', 'better-wp-security' );
60
  }
44
  } elseif ( 'recovery-scheduled' === $code ) {
45
  $entry['description'] = esc_html__( 'Recovery Scheduled', 'better-wp-security' );
46
  } elseif ( 'file-scan-aborted' === $code ) {
47
+ $entry['description'] = esc_html__( 'Scan Failed', 'better-wp-security' );
 
 
 
 
 
 
 
 
 
 
48
  } elseif ( 'rescheduling' === $code ) {
49
  $entry['description'] = esc_html__( 'Rescheduling', 'better-wp-security' );
50
  }
core/modules/file-change/scanner.php CHANGED
@@ -21,6 +21,7 @@ do_action( 'itsec_load_file_change_scanner' );
21
  class ITSEC_File_Change_Scanner {
22
 
23
  const DESTROYED = 'itsec_file_change_scan_destroyed';
 
24
 
25
  const C_ADMIN = 'admin';
26
  const C_INCLUDES = 'includes';
@@ -286,7 +287,7 @@ class ITSEC_File_Change_Scanner {
286
  $job_data['step'] = $store['step'];
287
  $job_data['chunk'] = $store['chunk'];
288
 
289
- if ( 1 === $job_data['loop_item'] || ( 'get-files' === $job_data['step'] && self::C_ADMIN === $job_data['chunk'] ) ) {
290
  ITSEC_Log::add_debug( 'file_change', 'recovery-failed-first-loop' );
291
 
292
  self::abort();
@@ -318,10 +319,8 @@ class ITSEC_File_Change_Scanner {
318
 
319
  /**
320
  * Abort an in-progress scan.
321
- *
322
- * @param bool $user_initiated
323
  */
324
- public static function abort( $user_initiated = false ) {
325
  $storage = ITSEC_File_Change::make_progress_storage();
326
 
327
  if ( 'file-change-fast' === $storage->get( 'id' ) ) {
@@ -335,20 +334,11 @@ class ITSEC_File_Change_Scanner {
335
  ITSEC_Log::add_process_stop( $process, array( 'aborted' => true ) );
336
  }
337
 
338
- if ( $user_initiated ) {
339
- $user = get_current_user_id();
340
- ITSEC_Log::add_warning( 'file_change', "file-scan-aborted::{$user}", array(
341
- 'id' => $storage->get( 'id' ),
342
- 'step' => $storage->get( 'step' ),
343
- 'chunk' => $storage->get( 'chunk' ),
344
- ) );
345
- } else {
346
- ITSEC_Log::add_fatal_error( 'file_change', 'file-scan-aborted', array(
347
- 'id' => $storage->get( 'id' ),
348
- 'step' => $storage->get( 'step' ),
349
- 'chunk' => $storage->get( 'chunk' ),
350
- ) );
351
- }
352
 
353
  $storage->clear();
354
  update_site_option( self::DESTROYED, ITSEC_Core::get_current_time_gmt() );
21
  class ITSEC_File_Change_Scanner {
22
 
23
  const DESTROYED = 'itsec_file_change_scan_destroyed';
24
+ const FILE_LIST = 'itsec_file_list';
25
 
26
  const C_ADMIN = 'admin';
27
  const C_INCLUDES = 'includes';
287
  $job_data['step'] = $store['step'];
288
  $job_data['chunk'] = $store['chunk'];
289
 
290
+ if ( 'get-files' === $job_data['step'] && self::C_ADMIN === $job_data['chunk'] ) {
291
  ITSEC_Log::add_debug( 'file_change', 'recovery-failed-first-loop' );
292
 
293
  self::abort();
319
 
320
  /**
321
  * Abort an in-progress scan.
 
 
322
  */
323
+ public static function abort() {
324
  $storage = ITSEC_File_Change::make_progress_storage();
325
 
326
  if ( 'file-change-fast' === $storage->get( 'id' ) ) {
334
  ITSEC_Log::add_process_stop( $process, array( 'aborted' => true ) );
335
  }
336
 
337
+ ITSEC_Log::add_fatal_error( 'file_change', 'file-scan-aborted', array(
338
+ 'id' => $storage->get( 'id' ),
339
+ 'step' => $storage->get( 'step' ),
340
+ 'chunk' => $storage->get( 'chunk' ),
341
+ ) );
 
 
 
 
 
 
 
 
 
342
 
343
  $storage->clear();
344
  update_site_option( self::DESTROYED, ITSEC_Core::get_current_time_gmt() );
core/modules/file-change/settings-page.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
4
- private $script_version = 4;
5
 
6
 
7
  public function __construct() {
@@ -26,9 +26,8 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
26
  require_once( dirname( __FILE__ ) . '/admin.php' );
27
  }
28
 
29
- ITSEC_Lib::enqueue_util();
30
  ITSEC_File_Change_Admin::enqueue_scanner();
31
- wp_enqueue_script( 'itsec-file-change-settings-script', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery', 'itsec-file-change-scanner', 'itsec-util' ), $this->script_version, true );
32
  wp_localize_script( 'itsec-file-change-settings-script', 'itsec_file_change_settings', $vars );
33
 
34
 
@@ -55,11 +54,6 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
55
  } else {
56
  ITSEC_Response::set_success( true );
57
  }
58
- } elseif ( 'abort' === $data['method'] ) {
59
- require_once( dirname( __FILE__ ) . '/scanner.php' );
60
- ITSEC_File_Change_Scanner::abort( true );
61
-
62
- ITSEC_Response::set_success( true );
63
  } else if ( 'get-filetree-data' === $data['method'] ) {
64
  ITSEC_Response::set_response( $this->get_filetree_data( $data ) );
65
  }
@@ -87,7 +81,7 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
87
 
88
  require_once( dirname( __FILE__ ) . '/scanner.php' );
89
 
90
- if ( $is_running = ITSEC_File_Change_Scanner::is_running() ) {
91
  $status = ITSEC_File_Change_Scanner::get_status();
92
 
93
  $button = array(
@@ -104,12 +98,7 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
104
  ?>
105
  <div class="hide-if-no-js">
106
  <p><?php _e( "Press the button below to scan your site's files for changes. Note that if changes are found this will take you to the logs page for details.", 'better-wp-security' ); ?></p>
107
- <p>
108
- <?php $form->add_button( 'one_time_check', $button ); ?>
109
- <?php if ( $is_running ) : ?>
110
- <?php $form->add_button( 'abort', array( 'value' => _x( 'Cancel', 'Cancel File Change scan.', 'better-wp-security' ), 'class' => 'button' ) ); ?>
111
- <?php endif; ?>
112
- </p>
113
  <div id="itsec_file_change_status"></div>
114
  </div>
115
 
1
  <?php
2
 
3
  final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
4
+ private $script_version = 3;
5
 
6
 
7
  public function __construct() {
26
  require_once( dirname( __FILE__ ) . '/admin.php' );
27
  }
28
 
 
29
  ITSEC_File_Change_Admin::enqueue_scanner();
30
+ wp_enqueue_script( 'itsec-file-change-settings-script', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery', 'itsec-file-change-scanner' ), $this->script_version, true );
31
  wp_localize_script( 'itsec-file-change-settings-script', 'itsec_file_change_settings', $vars );
32
 
33
 
54
  } else {
55
  ITSEC_Response::set_success( true );
56
  }
 
 
 
 
 
57
  } else if ( 'get-filetree-data' === $data['method'] ) {
58
  ITSEC_Response::set_response( $this->get_filetree_data( $data ) );
59
  }
81
 
82
  require_once( dirname( __FILE__ ) . '/scanner.php' );
83
 
84
+ if ( ITSEC_File_Change_Scanner::is_running() ) {
85
  $status = ITSEC_File_Change_Scanner::get_status();
86
 
87
  $button = array(
98
  ?>
99
  <div class="hide-if-no-js">
100
  <p><?php _e( "Press the button below to scan your site's files for changes. Note that if changes are found this will take you to the logs page for details.", 'better-wp-security' ); ?></p>
101
+ <p><?php $form->add_button( 'one_time_check', $button ); ?></p>
 
 
 
 
 
102
  <div id="itsec_file_change_status"></div>
103
  </div>
104
 
core/modules/global/active.php CHANGED
@@ -157,23 +157,4 @@ function itsec_record_first_login( $username, $user ) {
157
  }
158
  }
159
 
160
- add_action( 'wp_login', 'itsec_record_first_login', 15, 2 );
161
-
162
- /**
163
- * Basename the 'thumb' for attachments to prevent directory traversal
164
- * when deleting the main attachment.
165
- *
166
- * @param array $data
167
- *
168
- * @return array
169
- */
170
- function itsec_basename_attachment_thumbs( $data ) {
171
-
172
- if ( isset( $data['thumb'] ) && ITSEC_Modules::get_setting( 'wordpress-tweaks', 'patch_thumb_file_traversal' ) ) {
173
- $data['thumb'] = basename( $data['thumb'] );
174
- }
175
-
176
- return $data;
177
- }
178
-
179
- add_filter( 'wp_update_attachment_metadata', 'itsec_basename_attachment_thumbs' );
157
  }
158
  }
159
 
160
+ add_action( 'wp_login', 'itsec_record_first_login', 15, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/global/settings-page.php CHANGED
@@ -73,11 +73,6 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
73
  true => __( 'Yes' ),
74
  );
75
 
76
- $enable_grade_report_options = array(
77
- false => __( 'No (default)' ),
78
- true => __( 'Yes' ),
79
- );
80
-
81
  ?>
82
  <table class="form-table itsec-settings-section">
83
  <tr>
@@ -247,15 +242,6 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
247
  <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>
248
  </td>
249
  </tr>
250
- <?php if ( ITSEC_Core::is_pro() ) : ?>
251
- <tr>
252
- <th scope="row"><label for="itsec-global-enable_grade_report"><?php _e( 'Enable Grade Report', 'better-wp-security' ); ?></label></th>
253
- <td>
254
- <?php $form->add_select( 'enable_grade_report', $enable_grade_report_options ); ?>
255
- <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>
256
- </td>
257
- </tr>
258
- <?php endif; ?>
259
  </table>
260
  <?php
261
 
73
  true => __( 'Yes' ),
74
  );
75
 
 
 
 
 
 
76
  ?>
77
  <table class="form-table itsec-settings-section">
78
  <tr>
242
  <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>
243
  </td>
244
  </tr>
 
 
 
 
 
 
 
 
 
245
  </table>
246
  <?php
247
 
core/modules/global/settings.php CHANGED
@@ -35,7 +35,6 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
35
  'cron_status' => - 1,
36
  'use_cron' => true,
37
  'cron_test_time' => 0,
38
- 'enable_grade_report' => false,
39
  );
40
  }
41
 
@@ -48,17 +47,6 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
48
  if ( $this->settings['use_cron'] !== $old_settings['use_cron'] ) {
49
  $this->handle_cron_change( $this->settings['use_cron'] );
50
  }
51
-
52
- if ( $this->settings['enable_grade_report'] && ! $old_settings['enable_grade_report'] ) {
53
- update_site_option( 'itsec-enable-grade-report', true );
54
- ITSEC_Modules::load_module_file( 'activate.php', 'grade-report' );
55
- ITSEC_Response::flag_new_notifications_available();
56
- ITSEC_Response::refresh_page();
57
- } else if ( ! $this->settings['enable_grade_report'] && $old_settings['enable_grade_report'] ) {
58
- update_site_option( 'itsec-enable-grade-report', false );
59
- ITSEC_Modules::load_module_file( 'deactivate.php', 'grade-report' );
60
- ITSEC_Response::refresh_page();
61
- }
62
  }
63
 
64
  private function handle_cron_change( $new_use_cron ) {
35
  'cron_status' => - 1,
36
  'use_cron' => true,
37
  'cron_test_time' => 0,
 
38
  );
39
  }
40
 
47
  if ( $this->settings['use_cron'] !== $old_settings['use_cron'] ) {
48
  $this->handle_cron_change( $this->settings['use_cron'] );
49
  }
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
  private function handle_cron_change( $new_use_cron ) {
core/modules/global/validator.php CHANGED
@@ -21,7 +21,7 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
21
 
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' );
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' ) );
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' ) );
26
 
27
 
@@ -31,7 +31,6 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
31
  $this->sanitize_setting( 'bool', 'proxy_override', __( 'Override Proxy Detection', 'better-wp-security' ) );
32
  $this->sanitize_setting( 'bool', 'hide_admin_bar', __( 'Hide Security Menu in Admin Bar', 'better-wp-security' ) );
33
  $this->sanitize_setting( 'bool', 'show_error_codes', __( 'Show Error Codes', 'better-wp-security' ) );
34
- $this->sanitize_setting( 'bool', 'enable_grade_report', __( 'Enable Grade Report', 'better-wp-security' ) );
35
 
36
  $this->sanitize_setting( 'string', 'lockout_message', __( 'Host Lockout Message', 'better-wp-security' ) );
37
  $this->sanitize_setting( 'string', 'user_lockout_message', __( 'User Lockout Message', 'better-wp-security' ) );
21
 
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' );
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' ) );
24
+ $this->set_default_if_empty( array( 'log_location', 'nginx_file' ) );
25
  $this->preserve_setting_if_exists( array( 'digest_email', 'email_notifications', 'notification_email', 'backup_email' ) );
26
 
27
 
31
  $this->sanitize_setting( 'bool', 'proxy_override', __( 'Override Proxy Detection', 'better-wp-security' ) );
32
  $this->sanitize_setting( 'bool', 'hide_admin_bar', __( 'Hide Security Menu in Admin Bar', 'better-wp-security' ) );
33
  $this->sanitize_setting( 'bool', 'show_error_codes', __( 'Show Error Codes', 'better-wp-security' ) );
 
34
 
35
  $this->sanitize_setting( 'string', 'lockout_message', __( 'Host Lockout Message', 'better-wp-security' ) );
36
  $this->sanitize_setting( 'string', 'user_lockout_message', __( 'User Lockout Message', 'better-wp-security' ) );
core/modules/notification-center/class-notification-center.php CHANGED
@@ -71,14 +71,6 @@ final class ITSEC_Notification_Center {
71
  public function get_notifications() {
72
 
73
  if ( null === $this->notifications ) {
74
-
75
- if ( ! did_action( 'itsec_initialized' ) ) {
76
- ITSEC_Log::add_debug( 'core', 'doing-it-wrong', array(
77
- 'method' => __METHOD__,
78
- 'backtrace' => wp_debug_backtrace_summary(),
79
- ) );
80
- }
81
-
82
  /**
83
  * Filter the registered notifications.
84
  *
@@ -171,10 +163,9 @@ final class ITSEC_Notification_Center {
171
 
172
  $schedules = self::get_schedule_order();
173
  $schedule = array(
174
- 'min' => $schedules[0],
175
- 'max' => $schedules[ count( $schedules ) - 1 ],
176
- 'default' => self::S_DAILY,
177
- 'setting_only' => false,
178
  );
179
 
180
  if ( $args['schedule'] === self::S_CONFIGURABLE ) {
@@ -326,39 +317,27 @@ final class ITSEC_Notification_Center {
326
  }
327
 
328
  $settings = $this->get_notification_settings( $notification );
329
-
330
- if ( empty( $settings['recipient_type'] ) || 'custom' === $settings['recipient_type'] ) {
331
- $contacts = $settings['user_list'];
332
- } else {
333
- $contacts = ITSEC_Modules::get_setting( 'notification-center', 'default_recipients' );
334
- $contacts = isset( $contacts['user_list'] ) ? $contacts['user_list'] : array();
335
- }
336
 
337
  $addresses = array();
338
 
339
- $users = array();
340
- $roles = array();
341
-
342
  foreach ( $contacts as $contact ) {
343
  if ( (string) $contact === (string) intval( $contact ) ) {
344
- $users[] = get_userdata( $contact );
345
  } else {
346
- list( , $role ) = explode( ':', $contact, 2 );
347
 
348
  if ( empty( $role ) ) {
349
  continue;
350
  }
351
 
352
- $roles[] = $role;
353
  }
354
- }
355
 
356
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
357
- $users = array_merge( $users, ITSEC_Lib_Canonical_Roles::get_users_with_canonical_role( $roles ) );
358
-
359
- foreach ( $users as $user ) {
360
- if ( is_object( $user ) && ! empty( $user->user_email ) ) {
361
- $addresses[] = $user->user_email;
362
  }
363
  }
364
 
@@ -382,18 +361,6 @@ final class ITSEC_Notification_Center {
382
  return isset( $last_sent[ $notification ] ) ? $last_sent[ $notification ] : 0;
383
  }
384
 
385
- /**
386
- * Update the last sent time for a notification.
387
- *
388
- * @param string $notification
389
- */
390
- public function update_last_sent( $notification ) {
391
- $setting = ITSEC_Modules::get_setting( 'notification-center', 'last_sent', array() );
392
- $setting[ $notification ] = ITSEC_Core::get_current_time_gmt();
393
-
394
- ITSEC_Modules::set_setting( 'notification-center', 'last_sent', $setting );
395
- }
396
-
397
  /**
398
  * Get the time that the notification should next be sent.
399
  *
@@ -419,7 +386,7 @@ final class ITSEC_Notification_Center {
419
  $notification_data[] = $data;
420
 
421
  if ( $enforce_unique ) {
422
- $notification_data = ITSEC_Lib::non_scalar_array_unique( $notification_data );
423
  }
424
 
425
  $all_data[ $notification ] = $notification_data;
@@ -444,14 +411,12 @@ final class ITSEC_Notification_Center {
444
  /**
445
  * Initialize a Mail instance.
446
  *
447
- * @param string $name
448
- *
449
  * @return ITSEC_Mail
450
  */
451
- public function mail( $name = '' ) {
452
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-mail.php' );
453
 
454
- return new ITSEC_Mail( $name );
455
  }
456
 
457
  /**
@@ -478,11 +443,6 @@ final class ITSEC_Notification_Center {
478
 
479
  add_action( 'wp_mail_failed', array( $this, 'capture_mail_fail' ) );
480
 
481
- ITSEC_Log::add_debug( 'notification_center', "send::{$notification}", array(
482
- 'recipients' => $mail->get_recipients(),
483
- 'subject' => $mail->get_subject(),
484
- ) );
485
-
486
  $this->_sending_notification = $notification;
487
  $result = $mail->send();
488
  $this->_sending_notification = '';
@@ -498,7 +458,9 @@ final class ITSEC_Notification_Center {
498
  * @param string $error_id
499
  */
500
  public function dismiss_mail_error( $error_id ) {
501
- _deprecated_function( __METHOD__, '4.7.1' );
 
 
502
  }
503
 
504
  /**
@@ -507,10 +469,7 @@ final class ITSEC_Notification_Center {
507
  * @return array
508
  */
509
  public function get_mail_errors() {
510
-
511
- _deprecated_function( __METHOD__, '4.7.1' );
512
-
513
- return array();
514
  }
515
 
516
  /**
@@ -529,9 +488,15 @@ final class ITSEC_Notification_Center {
529
  */
530
  public function capture_mail_fail( $error ) {
531
 
532
- ITSEC_Log::add_error( 'notification_center', "send_failed::{$this->_sending_notification}", compact( 'error' ) );
533
 
534
- ITSEC_Modules::set_setting( 'notification-center', 'last_mail_error', $error->get_error_message() );
 
 
 
 
 
 
535
 
536
  if ( ITSEC_Core::is_interactive() ) {
537
  ITSEC_Response::reload_module( 'notification-center' );
@@ -693,13 +658,6 @@ final class ITSEC_Notification_Center {
693
  */
694
  public function send_scheduled_notifications( $notification_slugs, $silent = false ) {
695
 
696
- if ( doing_action( self::CRON_ACTION ) || doing_action( 'init' ) ) {
697
- ITSEC_Log::add_debug( 'notification_center', 'send_scheduled', array(
698
- 'notifications' => $notification_slugs,
699
- 'silent' => $silent,
700
- ) );
701
- }
702
-
703
  @set_time_limit( 120 );
704
  $sent = array();
705
 
@@ -921,10 +879,6 @@ final class ITSEC_Notification_Center {
921
  return false; // This is an on-demand
922
  }
923
 
924
- if ( ( $config = $this->get_notification( $notification ) ) && is_array( $config['schedule'] ) && ! empty( $config['schedule']['setting_only'] ) ) {
925
- return false;
926
- }
927
-
928
  switch ( $schedule ) {
929
  case self::S_DAILY:
930
  $period = DAY_IN_SECONDS;
71
  public function get_notifications() {
72
 
73
  if ( null === $this->notifications ) {
 
 
 
 
 
 
 
 
74
  /**
75
  * Filter the registered notifications.
76
  *
163
 
164
  $schedules = self::get_schedule_order();
165
  $schedule = array(
166
+ 'min' => $schedules[0],
167
+ 'max' => $schedules[ count( $schedules ) - 1 ],
168
+ 'default' => self::S_DAILY,
 
169
  );
170
 
171
  if ( $args['schedule'] === self::S_CONFIGURABLE ) {
317
  }
318
 
319
  $settings = $this->get_notification_settings( $notification );
320
+ $contacts = $settings['user_list'];
 
 
 
 
 
 
321
 
322
  $addresses = array();
323
 
 
 
 
324
  foreach ( $contacts as $contact ) {
325
  if ( (string) $contact === (string) intval( $contact ) ) {
326
+ $users = array( get_userdata( $contact ) );
327
  } else {
328
+ list( $prefix, $role ) = explode( ':', $contact, 2 );
329
 
330
  if ( empty( $role ) ) {
331
  continue;
332
  }
333
 
334
+ $users = get_users( array( 'role' => $role ) );
335
  }
 
336
 
337
+ foreach ( $users as $user ) {
338
+ if ( is_object( $user ) && ! empty( $user->user_email ) ) {
339
+ $addresses[] = $user->user_email;
340
+ }
 
 
341
  }
342
  }
343
 
361
  return isset( $last_sent[ $notification ] ) ? $last_sent[ $notification ] : 0;
362
  }
363
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  /**
365
  * Get the time that the notification should next be sent.
366
  *
386
  $notification_data[] = $data;
387
 
388
  if ( $enforce_unique ) {
389
+ $notification_data = array_unique( $notification_data, SORT_REGULAR );
390
  }
391
 
392
  $all_data[ $notification ] = $notification_data;
411
  /**
412
  * Initialize a Mail instance.
413
  *
 
 
414
  * @return ITSEC_Mail
415
  */
416
+ public function mail() {
417
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-mail.php' );
418
 
419
+ return new ITSEC_Mail();
420
  }
421
 
422
  /**
443
 
444
  add_action( 'wp_mail_failed', array( $this, 'capture_mail_fail' ) );
445
 
 
 
 
 
 
446
  $this->_sending_notification = $notification;
447
  $result = $mail->send();
448
  $this->_sending_notification = '';
458
  * @param string $error_id
459
  */
460
  public function dismiss_mail_error( $error_id ) {
461
+ $errors = ITSEC_Modules::get_setting( 'notification-center', 'mail_errors', array() );
462
+ unset( $errors[ $error_id ] );
463
+ ITSEC_Modules::set_setting( 'notification-center', 'mail_errors', $errors );
464
  }
465
 
466
  /**
469
  * @return array
470
  */
471
  public function get_mail_errors() {
472
+ return ITSEC_Modules::get_setting( 'notification-center', 'mail_errors', array() );
 
 
 
473
  }
474
 
475
  /**
488
  */
489
  public function capture_mail_fail( $error ) {
490
 
491
+ $errors = ITSEC_Modules::get_setting( 'notification-center', 'mail_errors', array() );
492
 
493
+ $errors[ uniqid() ] = array(
494
+ 'error' => array( 'message' => $error->get_error_message(), 'code' => $error->get_error_code() ),
495
+ 'time' => ITSEC_Core::get_current_time_gmt(),
496
+ 'notification' => $this->_sending_notification,
497
+ );
498
+
499
+ ITSEC_Modules::set_setting( 'notification-center', 'mail_errors', $errors );
500
 
501
  if ( ITSEC_Core::is_interactive() ) {
502
  ITSEC_Response::reload_module( 'notification-center' );
658
  */
659
  public function send_scheduled_notifications( $notification_slugs, $silent = false ) {
660
 
 
 
 
 
 
 
 
661
  @set_time_limit( 120 );
662
  $sent = array();
663
 
879
  return false; // This is an on-demand
880
  }
881
 
 
 
 
 
882
  switch ( $schedule ) {
883
  case self::S_DAILY:
884
  $period = DAY_IN_SECONDS;
core/modules/notification-center/debug.php DELETED
@@ -1,92 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class ITSEC_Notification_Center_Debug
5
- */
6
- class ITSEC_Notification_Center_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_notification-center', array( $this, 'handle_ajax_request' ) );
12
- }
13
-
14
- public function enqueue_scripts_and_styles() {
15
- wp_enqueue_script( 'itsec-notification-center-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
- if ( empty( $data['id'] ) ) {
20
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-notification-center-missing-id', __( 'The server did not receive a valid request. The notification id is missing.', 'better-wp-security' ) ) );
21
-
22
- return;
23
- }
24
-
25
- $result = ITSEC_Core::get_notification_center()->send_scheduled_notifications( array( $data['id'] ), ! empty( $data['silent'] ) );
26
-
27
- if ( is_wp_error( $result ) ) {
28
- ITSEC_Response::add_error( $result );
29
- } elseif ( ! $result ) {
30
- ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-notification-center-send-failed', __( 'The server could not send the requested notification.', 'better-wp-security' ) ) );
31
- } else {
32
- ITSEC_Response::set_response( $this->get_table() );
33
- ITSEC_Response::set_success( true );
34
- ITSEC_Response::add_message( __( 'Notification sent.', 'better-wp-security' ) );
35
- }
36
- }
37
-
38
- /**
39
- * Render our data to the Debug Page.
40
- */
41
- public function render() {
42
- ?>
43
-
44
- <div id="itsec-notification-center-notifications">
45
- <h2><?php esc_html_e( 'Notification Center', 'better-wp-security' ); ?></h2>
46
- <?php echo $this->get_table(); ?>
47
- </div>
48
-
49
- <?php
50
- }
51
-
52
- private function get_table() {
53
- $nc = ITSEC_Core::get_notification_center();
54
- ob_start();
55
- ?>
56
- <table class="widefat striped">
57
- <thead>
58
- <tr>
59
- <th><?php esc_html_e( 'ID', 'better-wp-security' ) ?></th>
60
- <th><?php esc_html_e( 'Last Sent', 'better-wp-security' ) ?></th>
61
- <th><?php esc_html_e( 'Next Send', 'better-wp-security' ) ?></th>
62
- <th><?php esc_html_e( 'Schedule', 'better-wp-security' ) ?></th>
63
- <th></th>
64
- </tr>
65
- </thead>
66
- <tbody>
67
- <?php foreach ( $nc->get_notifications() as $slug => $notification ) : $scheduled = ITSEC_Notification_Center::S_NONE !== $notification['schedule']; ?>
68
- <tr>
69
- <td><?php echo esc_html( $slug ); ?></td>
70
- <td><?php echo $scheduled ? date( 'Y-m-d H:i:s', $nc->get_last_sent( $slug ) ) : '–'; ?></td>
71
- <td><?php echo $scheduled && ( $next = $nc->get_next_send_time( $slug ) ) ? date( 'Y-m-d H:i:s', $next ) : '–'; ?></td>
72
- <td><?php echo $nc->get_schedule( $slug ); ?></td>
73
- <td>
74
- <?php if ( $scheduled && ( ! is_array( $notification['schedule'] ) || empty( $notification['schedule']['setting_only'] ) ) ): ?>
75
- <button class="button itsec__send-notification itsec__send-notification--force" data-id="<?php echo esc_attr( $slug ); ?>">
76
- <?php esc_html_e( 'Force', 'better-wp-security' ) ?>
77
- </button>
78
- <button class="button itsec__send-notification itsec__send-notification--silent" data-id="<?php echo esc_attr( $slug ); ?>">
79
- <?php esc_html_e( 'Silent', 'better-wp-security' ) ?>
80
- </button>
81
- <?php endif; ?>
82
- </td>
83
- </tr>
84
- <?php endforeach; ?>
85
- </tbody>
86
- </table>
87
- <?php
88
- return ob_get_clean();
89
- }
90
- }
91
-
92
- new ITSEC_Notification_Center_Debug();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/notification-center/js/debug.js DELETED
@@ -1,22 +0,0 @@
1
- (function ( $, itsecUtil ) {
2
- "use strict";
3
-
4
- $( function () {
5
- $( '#itsec-notification-center-notifications' ).on( 'click', '.button', function () {
6
-
7
- var $btn = $( this );
8
- $btn.prop( 'disabled', true );
9
-
10
- itsecUtil.sendModuleAJAXRequest( 'notification-center', { id: $btn.data( 'id' ), silent: $btn.hasClass( 'itsec__send-notification--silent' ) ? 1 : 0 }, function ( response ) {
11
-
12
- $btn.prop( 'disabled', false );
13
-
14
- if ( response.success ) {
15
- $( 'table', '#itsec-notification-center-notifications' ).replaceWith( response.response );
16
- }
17
-
18
- itsecUtil.displayNotices( response, $( '#itsec-messages' ) );
19
- } );
20
- } );
21
- } );
22
- })( jQuery, window.itsecUtil );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/notification-center/js/settings-page.js CHANGED
@@ -5,8 +5,10 @@ jQuery( function ( $ ) {
5
  } );
6
 
7
  $( document ).on( 'itsec-dismiss-notice', '.itsec-notification-center-mail-errors-container .notice.itsec-is-dismissible', function () {
8
- itsecUtil.sendModuleAJAXRequest( 'notification-center', { method: 'dismiss-mail-error' }, function ( r ) {
9
- if ( r.success ) {
 
 
10
  jQuery( '#itsec-module-card-notification-center' ).removeClass( 'itsec-module-status--warning' );
11
  }
12
  } )
@@ -33,10 +35,6 @@ jQuery( function ( $ ) {
33
  }
34
  }
35
 
36
- $( document ).on( 'change', '.itsec-notification-center-user-list-type', function ( e ) {
37
- $( this ).next().toggleClass( 'hidden' );
38
- } );
39
-
40
  itsecSettingsPage.events.on( 'modulesReloaded', initializeHiding );
41
  itsecSettingsPage.events.on( 'moduleReloaded', function ( _, module ) {
42
  if ( 'notification-center' === module ) {
5
  } );
6
 
7
  $( document ).on( 'itsec-dismiss-notice', '.itsec-notification-center-mail-errors-container .notice.itsec-is-dismissible', function () {
8
+ var errorId = $( this ).data( 'id' );
9
+
10
+ itsecUtil.sendModuleAJAXRequest( 'notification-center', { method: 'dismiss-mail-error', mail_error: errorId }, function ( r ) {
11
+ if ( r.response && r.response.status === 'all-cleared' ) {
12
  jQuery( '#itsec-module-card-notification-center' ).removeClass( 'itsec-module-status--warning' );
13
  }
14
  } )
35
  }
36
  }
37
 
 
 
 
 
38
  itsecSettingsPage.events.on( 'modulesReloaded', initializeHiding );
39
  itsecSettingsPage.events.on( 'moduleReloaded', function ( _, module ) {
40
  if ( 'notification-center' === module ) {
core/modules/notification-center/logs.php DELETED
@@ -1,94 +0,0 @@
1
- <?php
2
-
3
- class ITSEC_Notification_Center_Logs {
4
-
5
- public function __construct() {
6
- add_filter( 'itsec_logs_prepare_notification_center_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ), 10, 3 );
7
- add_filter( 'itsec_logs_prepare_notification_center_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
8
- }
9
-
10
- public function filter_entry_for_list_display( $entry, $code, $data ) {
11
-
12
- $entry['module_display'] = esc_html__( 'Notification Center', 'better-wp-security' );
13
-
14
- switch ( $code ) {
15
- case 'send':
16
- list ( $notification ) = $data;
17
-
18
- if ( $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification ) ) {
19
- $notification = $strings['label'];
20
- }
21
-
22
- $entry['description'] = sprintf( esc_html__( 'Sending %s', 'better-wp-security' ), $notification );
23
- break;
24
- case 'send_failed':
25
- list ( $notification ) = $data;
26
-
27
- if ( $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification ) ) {
28
- $notification = $strings['label'];
29
- }
30
-
31
- $entry['description'] = sprintf( esc_html__( 'Sending %s Failed', 'better-wp-security' ), $notification );
32
- break;
33
- case 'send_scheduled':
34
- $entry['description'] = esc_html__( 'Sending scheduled notifications', 'better-wp-security' );
35
- break;
36
- }
37
-
38
- return $entry;
39
- }
40
-
41
- public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
42
-
43
- $details['module']['content'] = esc_html__( 'Notification Center', 'better-wp-security' );
44
-
45
- switch ( $code ) {
46
- case 'send':
47
- list ( $notification ) = $code_data;
48
-
49
- if ( $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification ) ) {
50
- $notification = $strings['label'];
51
- }
52
-
53
- $details['description']['content'] = esc_html__( 'Sending Notification', 'better-wp-security' );
54
- $details['notification'] = array(
55
- 'header' => esc_html__( 'Notification', 'better-wp-security' ),
56
- 'content' => $notification,
57
- 'order' => 21,
58
- );
59
- break;
60
- case 'send_failed':
61
- list ( $notification ) = $code_data;
62
-
63
- if ( $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification ) ) {
64
- $notification = $strings['label'];
65
- }
66
-
67
- $details['description']['content'] = esc_html__( 'Sending Notification Failed', 'better-wp-security' );
68
- $details['notification'] = array(
69
- 'header' => esc_html__( 'Notification', 'better-wp-security' ),
70
- 'content' => $notification,
71
- 'order' => 21,
72
- );
73
- $details['error_message'] = array(
74
- 'header' => esc_html__( 'Error', 'better-wp-security' ),
75
- 'content' => wp_sprintf( '%l', ITSEC_Response::get_error_strings( $entry['data']['error'] ) ),
76
- 'order' => 22,
77
- );
78
- break;
79
- case 'send_scheduled':
80
- $details['description']['content'] = esc_html__( 'Sending Scheduled Notification', 'better-wp-security' );
81
- $details['notifications'] = array(
82
- 'header' => esc_html__( 'Notifications', 'better-wp-security' ),
83
- 'content' => wp_sprintf( '%l', $entry['data']['notifications'] ),
84
- 'order' => 21,
85
- );
86
- break;
87
- break;
88
- }
89
-
90
- return $details;
91
- }
92
- }
93
-
94
- new ITSEC_Notification_Center_Logs();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/notification-center/settings-page.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page {
4
 
5
- private $version = 3;
6
 
7
  /** @var ITSEC_Notification_Center_Validator */
8
  private $validator;
@@ -19,7 +19,7 @@ class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page
19
 
20
  $this->validator = ITSEC_Modules::get_validator( 'notification-center' );
21
 
22
- if ( ITSEC_Modules::get_setting( 'notification-center', 'last_mail_error' ) ) {
23
  $this->status = 'warning';
24
  }
25
 
@@ -39,8 +39,15 @@ class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page
39
 
40
  switch ( $data['method'] ) {
41
  case 'dismiss-mail-error':
42
- ITSEC_Modules::set_setting( 'notification-center', 'last_mail_error', '' );
43
- ITSEC_Response::set_success( true );
 
 
 
 
 
 
 
44
  break;
45
  }
46
  }
@@ -65,31 +72,10 @@ class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page
65
 
66
  <table class="form-table itsec-settings-section">
67
  <tbody>
68
- <tr>
69
- <th><label for="itsec-notification-center-from_email"><?php esc_html_e( 'From Email', 'better-wp-security' ); ?></label></th>
70
- <td>
71
- <?php $form->add_text( 'from_email' ); ?>
72
- <p class="description">
73
- <?php esc_html_e( 'iThemes Security will send notifications from this email address. Leave blank to use the WordPress default.', 'better-wp-security' ); ?>
74
- </p>
75
- </td>
76
- </tr>
77
- <tr class="itsec-email-contacts-setting">
78
- <th><label for="itsec-notification-center-default_recipients"><?php esc_html_e( 'Default Recipients', 'better-wp-security' ); ?></label></th>
79
- <td>
80
- <?php
81
- $form->add_input_group( 'default_recipients' );
82
- $this->render_user_list_fieldset( $form, ITSEC_Notification_Center::R_USER_LIST );
83
- $form->remove_input_group();
84
- ?>
85
- <p class="description">
86
- <?php esc_html_e( 'Set the default recipients for any admin-facing notifications.', 'better-wp-security' ); ?>
87
- </p>
88
- </td>
89
- </tr>
90
  </tbody>
91
  </table>
92
 
 
93
  <?php
94
 
95
  $notifications = ITSEC_Core::get_notification_center()->get_notifications();
@@ -103,20 +89,30 @@ class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page
103
  }
104
 
105
  protected function render_mail_errors() {
106
- if ( ! $message = ITSEC_Modules::get_setting( 'notification-center', 'last_mail_error' ) ) {
 
 
107
  return;
108
  }
109
 
110
- $link = esc_url( ITSEC_Core::get_logs_page_url( 'notification_center', 'error' ) );
111
  ?>
112
  <div class="itsec-notification-center-mail-errors-container">
113
- <div class="notice notice-alt notice-error below-h2 itsec-is-dismissible itsec-notification-center-mail-error">
114
- <?php if ( 'file' !== ITSEC_Modules::get_setting( 'global', 'log_type' ) ): ?>
115
- <p><?php printf( esc_html__( 'Error while sending notification: %1$s. %2$sView All%3$s.', 'better-wp-security' ), $message, "<a href=\"{$link}\">", '</a>' ); ?></p>
116
- <?php else: ?>
117
- <p><?php printf( esc_html__( 'Error while sending notification: %1$s.', 'better-wp-security' ), $message ); ?></p>
118
- <?php endif; ?>
119
- </div>
 
 
 
 
 
 
 
 
 
120
  </div>
121
  <?php
122
  }
@@ -249,34 +245,6 @@ class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page
249
  * @param string $type
250
  */
251
  protected function render_user_list( $slug, $form, $type ) {
252
- ?>
253
-
254
- <tr class="itsec-email-contacts-setting">
255
- <th><?php esc_html_e( 'Recipient', 'better-wp-security' ); ?></th>
256
- <td>
257
- <?php $form->add_select( 'recipient_type', array(
258
- 'class' => 'itsec-notification-center-user-list-type',
259
- 'value' => array(
260
- 'default' => esc_html__( 'Default Recipients', 'better-wp-security' ),
261
- 'custom' => esc_html__( 'Custom', 'better-wp-security' )
262
- ),
263
- )
264
- ); ?>
265
- <div class="<?php 'default' === $form->get_option( 'recipient_type' ) ? print( 'hidden' ) : null; ?>">
266
- <?php $this->render_user_list_fieldset( $form, $type ); ?>
267
- </div>
268
- </td>
269
- </tr>
270
- <?php
271
- }
272
-
273
- /**
274
- * Render the User List fieldset control.
275
- *
276
- * @param ITSEC_Form $form
277
- * @param string $type
278
- */
279
- private function render_user_list_fieldset( $form, $type ) {
280
 
281
  $users_and_roles = $this->validator->get_available_admin_users_and_roles();
282
 
@@ -284,55 +252,59 @@ class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page
284
  $roles = $users_and_roles['roles'];
285
 
286
  natcasesort( $users );
287
-
288
  ?>
289
- <fieldset>
290
- <legend class="screen-reader-text"><?php esc_html_e( 'Recipients for this email.', 'better-wp-security' ); ?></legend>
291
- <p><?php esc_html_e( 'Select which users should be emailed.', 'better-wp-security' ); ?></p>
292
-
293
- <ul>
294
- <?php foreach ( $roles as $role => $name ) : ?>
295
- <li>
296
- <label>
297
- <?php $form->add_multi_checkbox( 'user_list', $role ); ?>
298
- <?php echo esc_html( sprintf( _x( 'All %s users', 'role', 'better-wp-security' ), $name ) ); ?>
299
- </label>
300
- </li>
301
- <?php endforeach; ?>
302
- </ul>
303
-
304
- <ul>
305
- <?php foreach ( $users as $id => $name ) : ?>
306
- <li>
307
- <label>
308
- <?php $form->add_multi_checkbox( 'user_list', $id ); ?>
309
- <?php echo esc_html( $name ); ?>
310
- </label>
311
- </li>
312
- <?php endforeach; ?>
313
- </ul>
314
-
315
- <?php if ( ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE === $type && $form->get_option( 'previous_emails' ) ): ?>
316
-
317
- <div class="itsec-notification-center--deprecated-recipients">
318
- <span><?php esc_html_e( 'Deprecated Recipients', 'better-wp-security' ); ?></span>
319
- <p class="description">
320
- <?php esc_html_e( 'The following email recipients are deprecated. Please create new users for these email addresses or remove them.', 'better-wp-security' ); ?>
321
- </p>
322
  <ul>
323
- <?php foreach ( $form->get_option( 'previous_emails' ) as $email ): ?>
324
  <li>
325
  <label>
326
- <?php $form->add_multi_checkbox( 'previous_emails', $email ); ?>
327
- <?php echo esc_html( $email ); ?>
328
  </label>
329
  </li>
330
  <?php endforeach; ?>
331
  </ul>
332
- </div>
333
- <?php endif; ?>
334
- </fieldset>
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  <?php
337
  }
338
 
2
 
3
  class ITSEC_Notification_Center_Settings_Page extends ITSEC_Module_Settings_Page {
4
 
5
+ private $version = 1;
6
 
7
  /** @var ITSEC_Notification_Center_Validator */
8
  private $validator;
19
 
20
  $this->validator = ITSEC_Modules::get_validator( 'notification-center' );
21
 
22
+ if ( ITSEC_Core::get_notification_center()->get_mail_errors() ) {
23
  $this->status = 'warning';
24
  }
25
 
39
 
40
  switch ( $data['method'] ) {
41
  case 'dismiss-mail-error':
42
+
43
+ if ( ! empty( $data['mail_error'] ) ) {
44
+ ITSEC_Core::get_notification_center()->dismiss_mail_error( $data['mail_error'] );
45
+
46
+ if ( ! ITSEC_Core::get_notification_center()->get_mail_errors() ) {
47
+ ITSEC_Response::set_response( array( 'status' => 'all-cleared' ) );
48
+ }
49
+ }
50
+
51
  break;
52
  }
53
  }
72
 
73
  <table class="form-table itsec-settings-section">
74
  <tbody>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  </tbody>
76
  </table>
77
 
78
+
79
  <?php
80
 
81
  $notifications = ITSEC_Core::get_notification_center()->get_notifications();
89
  }
90
 
91
  protected function render_mail_errors() {
92
+ $errors = ITSEC_Core::get_notification_center()->get_mail_errors();
93
+
94
+ if ( ! $errors ) {
95
  return;
96
  }
97
 
 
98
  ?>
99
  <div class="itsec-notification-center-mail-errors-container">
100
+ <?php foreach ( $errors as $id => $error ):
101
+ $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $error['notification'] );
102
+ $details = $error['error'];
103
+
104
+ if ( is_wp_error( $details ) ) {
105
+ $message = $details->get_error_message();
106
+ } elseif ( is_array( $error ) && isset( $details['message'] ) && is_string( $details['message'] ) ) {
107
+ $message = $details['message'];
108
+ } else {
109
+ $message = __( 'Unknown error encountered while sending.', 'better-wp-security' );
110
+ }
111
+ ?>
112
+ <div class="notice notice-alt notice-error below-h2 itsec-is-dismissible itsec-notification-center-mail-error" data-id="<?php echo esc_attr( $id ); ?>">
113
+ <p><?php printf( esc_html__( 'Error while sending %1$s notification at %2$s: %3$s', 'better-wp-security' ), '<b>' . $strings['label'] . '</b>', '<b>' . ITSEC_Lib::date_format_i18n_and_local_timezone( $error['time'] ) . '</b>', $message ); ?></p>
114
+ </div>
115
+ <?php endforeach; ?>
116
  </div>
117
  <?php
118
  }
245
  * @param string $type
246
  */
247
  protected function render_user_list( $slug, $form, $type ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  $users_and_roles = $this->validator->get_available_admin_users_and_roles();
250
 
252
  $roles = $users_and_roles['roles'];
253
 
254
  natcasesort( $users );
 
255
  ?>
256
+
257
+ <tr class="itsec-email-contacts-setting">
258
+ <th><?php esc_html_e( 'Recipient', 'better-wp-security' ); ?></th>
259
+ <td>
260
+ <fieldset>
261
+ <legend class="screen-reader-text"><?php esc_html_e( 'Recipients for this email.', 'better-wp-security' ); ?></legend>
262
+ <p><?php esc_html_e( 'Select which users should be emailed.', 'better-wp-security' ); ?></p>
263
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  <ul>
265
+ <?php foreach ( $roles as $role => $name ) : ?>
266
  <li>
267
  <label>
268
+ <?php $form->add_multi_checkbox( 'user_list', $role ); ?>
269
+ <?php echo esc_html( sprintf( _x( 'All %s users', 'role', 'better-wp-security' ), $name ) ); ?>
270
  </label>
271
  </li>
272
  <?php endforeach; ?>
273
  </ul>
 
 
 
274
 
275
+ <ul>
276
+ <?php foreach ( $users as $id => $name ) : ?>
277
+ <li>
278
+ <label>
279
+ <?php $form->add_multi_checkbox( 'user_list', $id ); ?>
280
+ <?php echo esc_html( $name ); ?>
281
+ </label>
282
+ </li>
283
+ <?php endforeach; ?>
284
+ </ul>
285
+
286
+ <?php if ( ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE === $type && $form->get_option( 'previous_emails' ) ): ?>
287
+
288
+ <div class="itsec-notification-center--deprecated-recipients">
289
+ <span><?php esc_html_e( 'Deprecated Recipients', 'better-wp-security' ); ?></span>
290
+ <p class="description">
291
+ <?php esc_html_e( 'The following email recipients are deprecated. Please create new users for these email addresses or remove them.', 'better-wp-security' ); ?>
292
+ </p>
293
+ <ul>
294
+ <?php foreach ( $form->get_option( 'previous_emails' ) as $email ): ?>
295
+ <li>
296
+ <label>
297
+ <?php $form->add_multi_checkbox( 'previous_emails', $email ); ?>
298
+ <?php echo esc_html( $email ); ?>
299
+ </label>
300
+ </li>
301
+ <?php endforeach; ?>
302
+ </ul>
303
+ </div>
304
+ <?php endif; ?>
305
+ </fieldset>
306
+ </td>
307
+ </tr>
308
  <?php
309
  }
310
 
core/modules/notification-center/settings.php CHANGED
@@ -18,24 +18,16 @@ class ITSEC_Notification_Center_Settings extends ITSEC_Settings {
18
 
19
  public function get_defaults() {
20
  return array(
21
- 'last_sent' => array(),
22
- 'resend_at' => array(),
23
- 'data' => array(),
24
- 'last_mail_error' => '',
25
- 'notifications' => array(),
26
- 'admin_emails' => array(),
27
- 'from_email' => '',
28
- 'default_recipients' => array(
29
- 'user_list' => array( 'role:administrator' )
30
- ),
31
  );
32
  }
33
 
34
  public function load() {
35
- if ( ! class_exists( 'ITSEC_Notification_Center' ) ) {
36
- ITSEC_Modules::load_module_file( 'active.php', 'notification-center' );
37
- }
38
-
39
  $this->settings = ITSEC_Storage::get( $this->get_id() );
40
  $defaults = $this->get_defaults();
41
 
@@ -56,8 +48,6 @@ class ITSEC_Notification_Center_Settings extends ITSEC_Settings {
56
 
57
  $this->settings['notifications'][ $slug ] = $value;
58
  }
59
-
60
- unset( $this->settings['mail_errors'] );
61
  }
62
 
63
  public function refresh_notification_settings( $save = true ) {
@@ -73,78 +63,39 @@ class ITSEC_Notification_Center_Settings extends ITSEC_Settings {
73
  }
74
  }
75
 
76
- public function continue_upgrade( $old_version ) {
77
-
78
  $nc = ITSEC_Core::get_notification_center();
79
 
80
- if ( $old_version < 4076 ) {
81
- $nc->clear_notifications_cache();
82
- $this->refresh_notification_settings( false );
83
 
84
- $admin_users = array();
85
- $admin_emails = array();
86
 
87
- foreach ( $this->settings['admin_emails'] as $admin_email ) {
88
- $user = get_user_by( 'email', $admin_email );
89
-
90
- if ( $user && $user->has_cap( 'manage_options' ) ) {
91
- $admin_users[] = $user->ID;
92
- } else {
93
- $admin_emails[] = $admin_email;
94
- }
95
- }
96
 
97
- foreach ( $nc->get_notifications() as $slug => $notification ) {
98
-
99
- if ( ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE === $notification['recipient'] ) {
100
- if ( $admin_users ) {
101
- $this->settings['notifications'][ $slug ]['user_list'] = $admin_users;
102
- } elseif ( $admin_emails ) {
103
- $this->settings['notifications'][ $slug ]['user_list'] = array();
104
- }
105
-
106
- $this->settings['notifications'][ $slug ]['previous_emails'] = $admin_emails;
107
- }
108
  }
109
-
110
- $this->set_all( $this->settings );
111
  }
112
 
113
- if ( $old_version < 4101 ) {
114
 
115
- $settings = $this->get_all();
116
- $user_lists = array();
117
-
118
- foreach ( $settings['notifications'] as $slug => $setting ) {
119
- if ( ! $config = $nc->get_notification( $slug ) ) {
120
- $settings['notifications'][ $slug ]['recipient_type'] = 'custom';
121
-
122
- continue;
123
- }
124
-
125
- if ( ITSEC_Notification_Center::R_USER_LIST !== $config['recipient'] && ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE !== $config['recipient'] ) {
126
- continue;
127
  }
128
 
129
- $user_lists[ $slug ] = $setting['user_list'];
130
  }
131
-
132
- $unique_lists = ITSEC_Lib::non_scalar_array_unique( $user_lists );
133
-
134
- if ( 1 === count( $unique_lists ) ) {
135
- $settings['default_recipients'] = array( 'user_list' => reset( $unique_lists ) );
136
-
137
- foreach ( $user_lists as $notification_slug => $_ ) {
138
- $settings['notifications'][ $notification_slug ]['recipient_type'] = 'default';
139
- }
140
- } else {
141
- foreach ( $user_lists as $notification_slug => $_ ) {
142
- $settings['notifications'][ $notification_slug ]['recipient_type'] = 'custom';
143
- }
144
- }
145
-
146
- $this->set_all( $settings );
147
  }
 
 
148
  }
149
 
150
  /**
@@ -178,13 +129,12 @@ class ITSEC_Notification_Center_Settings extends ITSEC_Settings {
178
  }
179
 
180
  if ( ITSEC_Notification_Center::R_USER_LIST === $notification['recipient'] ) {
181
- $defaults['user_list'] = array( 'role:administrator' );
182
- $defaults['recipient_type'] = 'default';
183
  }
184
 
185
  if ( ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE === $notification['recipient'] ) {
186
- $defaults['user_list'] = array( 'role:administrator' );
187
- $defaults['recipient_type'] = 'default';
188
  }
189
 
190
  if ( ITSEC_Notification_Center::R_EMAIL_LIST === $notification['recipient'] ) {
18
 
19
  public function get_defaults() {
20
  return array(
21
+ 'last_sent' => array(),
22
+ 'resend_at' => array(),
23
+ 'data' => array(),
24
+ 'mail_errors' => array(),
25
+ 'notifications' => array(),
26
+ 'admin_emails' => array(),
 
 
 
 
27
  );
28
  }
29
 
30
  public function load() {
 
 
 
 
31
  $this->settings = ITSEC_Storage::get( $this->get_id() );
32
  $defaults = $this->get_defaults();
33
 
48
 
49
  $this->settings['notifications'][ $slug ] = $value;
50
  }
 
 
51
  }
52
 
53
  public function refresh_notification_settings( $save = true ) {
63
  }
64
  }
65
 
66
+ public function continue_upgrade() {
 
67
  $nc = ITSEC_Core::get_notification_center();
68
 
69
+ $nc->clear_notifications_cache();
70
+ $this->refresh_notification_settings( false );
 
71
 
72
+ $admin_users = array();
73
+ $admin_emails = array();
74
 
75
+ foreach ( $this->settings['admin_emails'] as $admin_email ) {
76
+ $user = get_user_by( 'email', $admin_email );
 
 
 
 
 
 
 
77
 
78
+ if ( $user && $user->has_cap( 'manage_options' ) ) {
79
+ $admin_users[] = $user->ID;
80
+ } else {
81
+ $admin_emails[] = $admin_email;
 
 
 
 
 
 
 
82
  }
 
 
83
  }
84
 
85
+ foreach ( $nc->get_notifications() as $slug => $notification ) {
86
 
87
+ if ( ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE === $notification['recipient'] ) {
88
+ if ( $admin_users ) {
89
+ $this->settings['notifications'][ $slug ]['user_list'] = $admin_users;
90
+ } elseif ( $admin_emails ) {
91
+ $this->settings['notifications'][ $slug ]['user_list'] = array();
 
 
 
 
 
 
 
92
  }
93
 
94
+ $this->settings['notifications'][ $slug ]['previous_emails'] = $admin_emails;
95
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  }
97
+
98
+ $this->set_all( $this->settings );
99
  }
100
 
101
  /**
129
  }
130
 
131
  if ( ITSEC_Notification_Center::R_USER_LIST === $notification['recipient'] ) {
132
+ $defaults['user_list'] = array( 'role:administrator' );
 
133
  }
134
 
135
  if ( ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE === $notification['recipient'] ) {
136
+ $defaults['user_list'] = array( 'role:administrator' );
137
+ $defaults['previous_emails'] = array();
138
  }
139
 
140
  if ( ITSEC_Notification_Center::R_EMAIL_LIST === $notification['recipient'] ) {
core/modules/notification-center/setup.php CHANGED
@@ -2,11 +2,9 @@
2
 
3
  class ITSEC_Notification_Center_Setup {
4
 
5
- private $old_version;
6
-
7
  public function __construct() {
8
  add_action( 'itsec_modules_do_plugin_uninstall', array( $this, 'execute_uninstall' ) );
9
- add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ), -100 );
10
  }
11
 
12
  /**
@@ -73,7 +71,6 @@ class ITSEC_Notification_Center_Setup {
73
 
74
  ITSEC_Modules::set_settings( 'notification-center', $settings );
75
 
76
- $this->old_version = $itsec_old_version;
77
  add_action( 'itsec_initialized', array( $this, 'fire_continue_upgrade' ) );
78
  } elseif ( $itsec_old_version < 4077 ) { // Only run this if user is updating from 4076 -> 4077
79
  ITSEC_Modules::load_module_file( 'active.php', 'notification-center' );
@@ -116,24 +113,10 @@ class ITSEC_Notification_Center_Setup {
116
  ITSEC_Modules::set_settings( 'notification-center', $settings );
117
  }
118
  }
119
-
120
- if ( $itsec_old_version < 4099 ) {
121
- ITSEC_Modules::load_module_file( 'active.php', 'notification-center' );
122
-
123
- $settings = ITSEC_Modules::get_settings( 'notification-center' );
124
- unset( $settings['mail_errors'] );
125
- ITSEC_Modules::set_settings( 'notification-center', $settings );
126
- }
127
-
128
- if ( $itsec_old_version < 4101 ) {
129
- $this->old_version = $itsec_old_version;
130
- add_action( 'itsec_initialized', array( $this, 'fire_continue_upgrade' ) );
131
- }
132
  }
133
 
134
  public function fire_continue_upgrade() {
135
- ITSEC_Modules::load_module_file( 'settings.php', 'notification-center' );
136
- do_action( 'itsec_notification_center_continue_upgrade', $this->old_version );
137
  }
138
  }
139
 
2
 
3
  class ITSEC_Notification_Center_Setup {
4
 
 
 
5
  public function __construct() {
6
  add_action( 'itsec_modules_do_plugin_uninstall', array( $this, 'execute_uninstall' ) );
7
+ add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ) );
8
  }
9
 
10
  /**
71
 
72
  ITSEC_Modules::set_settings( 'notification-center', $settings );
73
 
 
74
  add_action( 'itsec_initialized', array( $this, 'fire_continue_upgrade' ) );
75
  } elseif ( $itsec_old_version < 4077 ) { // Only run this if user is updating from 4076 -> 4077
76
  ITSEC_Modules::load_module_file( 'active.php', 'notification-center' );
113
  ITSEC_Modules::set_settings( 'notification-center', $settings );
114
  }
115
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
117
 
118
  public function fire_continue_upgrade() {
119
+ do_action( 'itsec_notification_center_continue_upgrade' );
 
120
  }
121
  }
122
 
core/modules/notification-center/validator.php CHANGED
@@ -13,51 +13,11 @@ class ITSEC_Notification_Center_Validator extends ITSEC_Validator {
13
  }
14
 
15
  protected function sanitize_settings() {
16
- $this->vars_to_skip_validate_matching_fields = array( 'last_sent', 'data', 'resend_at', 'admin_emails', 'last_mail_error' );
17
  $this->set_previous_if_empty( array( 'last_sent', 'data', 'resend_at', 'admin_emails' ) );
18
 
19
- if ( ! isset( $this->settings['last_mail_error'] ) ) {
20
- $this->settings['last_mail_error'] = $this->previous_settings['last_mail_error'];
21
- }
22
-
23
- // We allow an empty email string.
24
- if ( ! empty( $this->settings['from_email'] ) ) {
25
- $this->sanitize_setting( 'email', 'from_email', __( 'Admin Email', 'better-wp-security' ) );
26
- }
27
-
28
- if ( $this->sanitize_setting( 'array', 'default_recipients', esc_html__( 'Default Recipients', 'better-wp-security' ) ) ) {
29
- if ( empty( $this->settings['default_recipients']['user_list'] ) ) {
30
- $this->add_error( new WP_Error(
31
- 'itsec-validator-notification-center-invalid-type-default_recipients[user_list]-non-empty',
32
- esc_html__( 'Selecting "Default Recipients" is required.', 'better-wp-security' )
33
- ) );
34
-
35
- if ( ITSEC_Core::is_interactive() ) {
36
- $this->set_can_save( false );
37
- }
38
- } else {
39
- $users_and_roles = $this->get_available_admin_users_and_roles();
40
- $valid_contacts = $users_and_roles['users'] + $users_and_roles['roles'];
41
-
42
- $contact_errors = array();
43
-
44
- foreach ( $this->settings['default_recipients']['user_list'] as $contact ) {
45
- if ( ! isset( $valid_contacts[ $contact ] ) ) {
46
- $contact_errors[] = $contact;
47
- }
48
- }
49
-
50
- if ( $contact_errors ) {
51
- $this->add_error( new WP_Error(
52
- 'itsec-validator-notification-center-invalid-type-default_recipients[user_list]-invalid-contacts',
53
- wp_sprintf( esc_html__( 'Unknown Default Recipients contacts, %l.', 'better-wp-security' ), $contact_errors )
54
- ) );
55
-
56
- if ( ITSEC_Core::is_interactive() ) {
57
- $this->set_can_save( false );
58
- }
59
- }
60
- }
61
  }
62
 
63
  if ( ! $this->sanitize_setting( 'array', 'notifications', esc_html__( 'Notifications', 'better-wp-security' ) ) ) {
@@ -76,7 +36,7 @@ class ITSEC_Notification_Center_Validator extends ITSEC_Validator {
76
  $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification );
77
 
78
  if ( ITSEC_Notification_Center::R_USER_LIST !== $config['recipient'] && ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE !== $config['recipient'] ) {
79
- unset( $settings['user_list'], $settings['recipient_type'] );
80
  } else {
81
  if ( ! is_array( $settings['user_list'] ) ) {
82
  $settings['user_list'] = array();
@@ -111,19 +71,6 @@ class ITSEC_Notification_Center_Validator extends ITSEC_Validator {
111
  $this->set_can_save( false );
112
  }
113
  }
114
-
115
- if ( ! isset( $settings['recipient_type'] ) ) {
116
- $settings['recipient_type'] = 'default';
117
- } elseif ( ! in_array( $settings['recipient_type'], array( 'default', 'custom' ), true ) ) {
118
- $this->add_error( new WP_Error(
119
- 'itsec-validator-notification-center-invalid-type-notifications[recipient_type]-array',
120
- wp_sprintf( esc_html__( 'Unknown recipient type for %s.', 'better-wp-security' ), $strings['label'] )
121
- ) );
122
-
123
- if ( ITSEC_Core::is_interactive() ) {
124
- $this->set_can_save( false );
125
- }
126
- }
127
  }
128
 
129
  if ( ITSEC_Notification_Center::R_EMAIL_LIST !== $config['recipient'] ) {
@@ -294,10 +241,8 @@ class ITSEC_Notification_Center_Validator extends ITSEC_Validator {
294
  $available_roles = array();
295
  $available_users = array();
296
 
297
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
298
-
299
  foreach ( $roles->roles as $role => $details ) {
300
- if ( 'administrator' === ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $role ) ) {
301
  $available_roles["role:$role"] = translate_user_role( $details['name'] );
302
 
303
  $users = get_users( array( 'role' => $role ) );
13
  }
14
 
15
  protected function sanitize_settings() {
16
+ $this->vars_to_skip_validate_matching_fields = array( 'last_sent', 'data', 'resend_at', 'mail_errors', 'admin_emails' );
17
  $this->set_previous_if_empty( array( 'last_sent', 'data', 'resend_at', 'admin_emails' ) );
18
 
19
+ if ( ! isset( $this->settings['mail_errors'] ) ) {
20
+ $this->settings['mail_errors'] = $this->previous_settings['mail_errors'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
 
23
  if ( ! $this->sanitize_setting( 'array', 'notifications', esc_html__( 'Notifications', 'better-wp-security' ) ) ) {
36
  $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification );
37
 
38
  if ( ITSEC_Notification_Center::R_USER_LIST !== $config['recipient'] && ITSEC_Notification_Center::R_USER_LIST_ADMIN_UPGRADE !== $config['recipient'] ) {
39
+ unset( $settings['user_list'] );
40
  } else {
41
  if ( ! is_array( $settings['user_list'] ) ) {
42
  $settings['user_list'] = array();
71
  $this->set_can_save( false );
72
  }
73
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  if ( ITSEC_Notification_Center::R_EMAIL_LIST !== $config['recipient'] ) {
241
  $available_roles = array();
242
  $available_users = array();
243
 
 
 
244
  foreach ( $roles->roles as $role => $details ) {
245
+ if ( ! empty( $details['capabilities']['manage_options'] ) ) {
246
  $available_roles["role:$role"] = translate_user_role( $details['name'] );
247
 
248
  $users = get_users( array( 'role' => $role ) );
core/modules/password-requirements/active.php DELETED
@@ -1,6 +0,0 @@
1
- <?php
2
-
3
- require_once( dirname( __FILE__ ) . '/class-itsec-password-requirements.php' );
4
-
5
- $requirements = new ITSEC_Password_Requirements();
6
- $requirements->run();
 
 
 
 
 
 
core/modules/password-requirements/class-itsec-password-requirements.php DELETED
@@ -1,480 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class ITSEC_Password_Requirements
5
- */
6
- class ITSEC_Password_Requirements {
7
-
8
- const META_KEY = '_itsec_password_requirements';
9
-
10
- public function run() {
11
-
12
- add_action( 'user_profile_update_errors', array( $this, 'forward_profile_pass_update' ), 0, 3 );
13
- add_action( 'validate_password_reset', array( $this, 'forward_reset_pass' ), 10, 2 );
14
-
15
- add_action( 'profile_update', array( $this, 'handle_update_user' ), 10, 2 );
16
- add_action( 'password_reset', array( $this, 'handle_password_reset' ), 10, 2 );
17
- add_filter( 'wp_authenticate_user', array( $this, 'check_password_on_login' ), 999, 2 );
18
-
19
- add_action( 'add_user_role', array( $this, 'handle_role_change' ) );
20
- add_action( 'set_user_role', array( $this, 'handle_role_change' ) );
21
- add_action( 'remove_user_role', array( $this, 'handle_role_change' ) );
22
-
23
- add_action( 'itsec_validate_password', array( $this, 'validate_password' ), 10, 4 );
24
-
25
- add_action( 'wp_login', array( $this, 'flag_check' ), 9, 2 );
26
-
27
- add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
28
- }
29
-
30
- /**
31
- * When a user's password is updated, or a new user created, verify that the new password is valid.
32
- *
33
- * @param WP_Error $errors
34
- * @param bool $update
35
- * @param WP_User|stdClass $user
36
- */
37
- public function forward_profile_pass_update( $errors, $update, $user ) {
38
-
39
- if ( $errors->get_error_message( 'pass' ) ) {
40
- return;
41
- }
42
-
43
- if ( isset( $user->user_pass ) ) {
44
- $this->handle_profile_update_password( $errors, $update, $user );
45
- } elseif ( $update && isset( $user->role ) ) {
46
- $this->handle_profile_update_role( $errors, $user );
47
- }
48
- }
49
-
50
- /**
51
- * Handle the password being updated for a user.
52
- *
53
- * @param WP_Error $errors
54
- * @param bool $update
55
- * @param WP_User|stdClass $user
56
- */
57
- private function handle_profile_update_password( $errors, $update, $user ) {
58
- if ( ! $update ) {
59
- $context = 'admin-user-create';
60
- } elseif ( isset( $user->ID ) && $user->ID === get_current_user_id() ) {
61
- $context = 'profile-update';
62
- } else {
63
- $context = 'admin-profile-update';
64
- }
65
-
66
- $args = array(
67
- 'error' => $errors,
68
- 'context' => $context
69
- );
70
-
71
- if ( isset( $user->role ) ) {
72
- $args['role'] = $user->role;
73
- }
74
-
75
- ITSEC_Lib_Password_Requirements::validate_password( $user, $user->user_pass, $args );
76
- }
77
-
78
- /**
79
- * Handle the user's role being updated.
80
- *
81
- * @param WP_Error $errors
82
- * @param WP_User|stdClass $user
83
- */
84
- private function handle_profile_update_role( $errors, $user ) {
85
-
86
- $settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
87
-
88
- foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
89
-
90
- if ( ! $requirement['validate'] || ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
91
- continue;
92
- }
93
-
94
- $evaluation = get_user_meta( $user->ID, $requirement['meta'], true );
95
-
96
- if ( '' === $evaluation ) {
97
- continue;
98
- }
99
-
100
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
101
-
102
- $args = array(
103
- 'role' => $user->role,
104
- 'canonical' => ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $user->role, $user ),
105
- );
106
-
107
- $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], $args );
108
-
109
- if ( true === $validated ) {
110
- continue;
111
- }
112
-
113
- $message = $validated ? $validated : esc_html__( "The provided password does not meet this site's requirements.", 'better-wp-security' );
114
- $errors->add( 'pass', $message );
115
- }
116
- }
117
-
118
- /**
119
- * When a user attempts to reset their password, verify that the new password is valid.
120
- *
121
- * @param WP_Error $errors
122
- * @param WP_User $user
123
- */
124
- public function forward_reset_pass( $errors, $user ) {
125
-
126
- if ( ! isset( $_POST['pass1'] ) || is_wp_error( $user ) ) {
127
- // The validate_password_reset action fires when first rendering the reset page and when handling the form
128
- // submissions. Since the pass1 data is missing, this must be the initial page render. So, we don't need to
129
- // do anything yet.
130
- return;
131
- }
132
-
133
- ITSEC_Lib_Password_Requirements::validate_password( $user, $_POST['pass1'], array(
134
- 'error' => $errors,
135
- 'context' => 'reset-password',
136
- ) );
137
- }
138
-
139
- /**
140
- * Whenever a user object is updated, set when their password was last updated.
141
- *
142
- * @param int $user_id
143
- * @param object $old_user_data
144
- */
145
- public function handle_update_user( $user_id, $old_user_data ) {
146
-
147
- $user = get_userdata( $user_id );
148
-
149
- if ( $user->user_pass === $old_user_data->user_pass ) {
150
- return;
151
- }
152
-
153
- $this->handle_password_updated( $user );
154
- }
155
-
156
- /**
157
- * When a user resets their password, update the last change time.
158
- *
159
- * For some unknown reason, the password reset routine uses {@see wp_set_password()} instead of {@see wp_update_user()}.
160
- *
161
- * @param WP_User $user
162
- * @param string $new_password
163
- */
164
- public function handle_password_reset( $user, $new_password ) {
165
- $this->handle_password_updated( $user );
166
- $this->handle_plain_text_password_available( $user, $new_password );
167
- }
168
-
169
- /**
170
- * When a user logs in, if their password hasn't been validated yet,
171
- * validate it.
172
- *
173
- * @param WP_User $user
174
- * @param string $password
175
- *
176
- * @return WP_User
177
- */
178
- public function check_password_on_login( $user, $password ) {
179
-
180
- if ( ! $user instanceof WP_User ) {
181
- return $user;
182
- }
183
-
184
- if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
185
- return $user;
186
- }
187
-
188
- $this->handle_plain_text_password_available( $user, $password );
189
-
190
- return $user;
191
- }
192
-
193
- /**
194
- * When a password is updated, set the last updated time and delete any pending required change.
195
- *
196
- * @param WP_User $user
197
- */
198
- protected function handle_password_updated( $user ) {
199
- delete_user_meta( $user->ID, 'itsec_password_change_required' );
200
- update_user_meta( $user->ID, 'itsec_last_password_change', ITSEC_Core::get_current_time_gmt() );
201
- }
202
-
203
- /**
204
- * When a plain text password is available, we perform any evaluations that have not yet been performed for this password.
205
- *
206
- * @param WP_User $user
207
- * @param string $password
208
- */
209
- protected function handle_plain_text_password_available( $user, $password ) {
210
-
211
- $config = wp_parse_args( get_user_meta( $user->ID, self::META_KEY, true ), array(
212
- 'evaluation_times' => array(),
213
- ) );
214
-
215
- $last_updated = ITSEC_Lib_Password_Requirements::password_last_changed( $user );
216
-
217
- $settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
218
-
219
- foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
220
-
221
- if ( ! $requirement['evaluate'] ) {
222
- continue;
223
- }
224
-
225
- if ( ! $requirement['evaluate_if_not_enabled'] && ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
226
- continue;
227
- }
228
-
229
- if ( isset( $config['evaluation_times'][ $code ] ) && $config['evaluation_times'][ $code ] >= $last_updated ) {
230
- continue;
231
- }
232
-
233
- $evaluation = call_user_func( $requirement['evaluate'], $password, $user );
234
-
235
- if ( is_wp_error( $evaluation ) ) {
236
- continue;
237
- }
238
-
239
- $config['evaluation_times'][ $code ] = ITSEC_Core::get_current_time_gmt();
240
- update_user_meta( $user->ID, $requirement['meta'], $evaluation );
241
-
242
- if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
243
- continue;
244
- }
245
-
246
- $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], array() );
247
-
248
- if ( true === $validated ) {
249
- continue;
250
- }
251
-
252
- ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
253
- }
254
-
255
- update_user_meta( $user->ID, self::META_KEY, $config );
256
- }
257
-
258
- /**
259
- * Validate password.
260
- *
261
- * @param \WP_Error $error
262
- * @param \WP_User|stdClass $user
263
- * @param string $new_password
264
- * @param array $args
265
- */
266
- public function validate_password( $error, $user, $new_password, $args ) {
267
-
268
- $settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
269
-
270
- foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
271
-
272
- if ( ! $requirement['evaluate'] || ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
273
- continue;
274
- }
275
-
276
- $evaluation = call_user_func( $requirement['evaluate'], $new_password, $user );
277
-
278
- if ( is_wp_error( $evaluation ) ) {
279
- continue;
280
- }
281
-
282
- $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], $args );
283
-
284
- if ( true === $validated ) {
285
- continue;
286
- }
287
-
288
- // The default error message is a safeguard that should never occur.
289
- $message = $validated ? $validated : esc_html__( "The provided password does not meet this site's requirements.", 'better-wp-security' );
290
-
291
- switch ( $args['context'] ) {
292
- case 'admin-user-create':
293
- $message .= ' ' . __( 'The user has not been created.', 'better-wp-security' );
294
- break;
295
- case 'admin-profile-update':
296
- $message .= ' ' . __( 'The user changes have not been saved.', 'better-wp-security' );
297
- break;
298
- case 'profile-update':
299
- $message .= ' ' . __( 'Your profile has not been updated.', 'better-wp-security' );
300
- break;
301
- case 'reset-password':
302
- $message .= ' ' . __( 'The password has not been updated.', 'better-wp-security' );
303
- break;
304
- }
305
-
306
- $error->add( 'pass', $message );
307
- }
308
- }
309
-
310
- /**
311
- * When a user logs in, run any flag checks to see if a password change should be forced.
312
- *
313
- * @param string $username
314
- * @param WP_User|null $user
315
- */
316
- public function flag_check( $username, $user = null ) {
317
-
318
- if ( ! $user && is_user_logged_in() ) {
319
- $user = wp_get_current_user();
320
- }
321
-
322
- if ( ! $user instanceof WP_User || ! $user->exists() ) {
323
- return;
324
- }
325
-
326
- foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
327
- if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
328
- continue;
329
- }
330
-
331
- $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( $code );
332
-
333
- if ( $requirement['flag_check'] && call_user_func( $requirement['flag_check'], $user, $settings ) ) {
334
- ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
335
-
336
- return;
337
- }
338
- }
339
- }
340
-
341
- /**
342
- * Is a given requirement enabled.
343
- *
344
- * @param string $requirement
345
- *
346
- * @return bool
347
- */
348
- protected function is_requirement_enabled( $requirement ) {
349
-
350
- $requirements = ITSEC_Lib_Password_Requirements::get_registered();
351
-
352
- if ( ! isset( $requirements[ $requirement ] ) ) {
353
- return false;
354
- }
355
-
356
- // If the requirement does not have any settings, than it is always enabled.
357
- if ( null === $requirements[ $requirement ]['settings_config'] ) {
358
- return true;
359
- }
360
-
361
- $enabled = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
362
-
363
- if ( ! empty( $enabled[ $requirement ] ) ) {
364
- return true;
365
- }
366
-
367
- return false;
368
- }
369
-
370
- /**
371
- * When a user's role changes, clear all the evaluation times as evaluat
372
- *
373
- * @param int $user_id
374
- */
375
- public function handle_role_change( $user_id ) {
376
-
377
- $config = get_user_meta( $user_id, self::META_KEY, true );
378
-
379
- if ( ! $config || ! is_array( $config ) ) {
380
- return;
381
- }
382
-
383
- $config['evaluation_times'] = array();
384
-
385
- update_user_meta( $user_id, self::META_KEY, $config );
386
- }
387
-
388
- /**
389
- * Register the password change interstitial.
390
- *
391
- * @param ITSEC_Lib_Login_Interstitial $lib
392
- */
393
- public function register_interstitial( $lib ) {
394
- $lib->register( 'update-password', array( $this, 'render_interstitial' ), array(
395
- 'show_to_user' => array( 'ITSEC_Lib_Password_Requirements', 'password_change_required' ),
396
- 'info_message' => array( 'ITSEC_Lib_Password_Requirements', 'get_message_for_password_change_reason' ),
397
- 'submit' => array( $this, 'submit' ),
398
- ) );
399
- }
400
-
401
- /**
402
- * Render the interstitial.
403
- *
404
- * @param WP_User $user
405
- */
406
- public function render_interstitial( $user ) {
407
- do_action( 'itsec_password_requirements_change_form', $user );
408
- ?>
409
-
410
- <div class="user-pass1-wrap">
411
- <p><label for="pass1"><?php _e( 'New Password', 'better-wp-security' ); ?></label></p>
412
- </div>
413
-
414
- <div class="wp-pwd">
415
- <span class="password-input-wrapper">
416
- <input type="password" data-reveal="1"
417
- data-pw="<?php echo esc_attr( wp_generate_password( 16 ) ); ?>" name="pass1" id="pass1"
418
- class="input" size="20" value="" autocomplete="off" aria-describedby="pass-strength-result"/>
419
- </span>
420
- <div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator', 'better-wp-security' ); ?></div>
421
- <div class="pw-weak">
422
- <label>
423
- <input type="checkbox" name="pw_weak" class="pw-checkbox" />
424
- <?php _e( 'Confirm use of weak password' ); ?>
425
- </label>
426
- </div>
427
- </div>
428
-
429
- <p class="user-pass2-wrap">
430
- <label for="pass2"><?php _e( 'Confirm new password' ) ?></label><br/>
431
- <input type="password" name="pass2" id="pass2" class="input" size="20" value="" autocomplete="off"/>
432
- </p>
433
-
434
- <p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
435
- <br class="clear"/>
436
-
437
- <p class="submit">
438
- <input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e( 'Update Password', 'better-wp-security' ); ?>"/>
439
- </p>
440
-
441
- <?php
442
- }
443
-
444
- /**
445
- * Handle the request to update the user's password.
446
- *
447
- * @param WP_User $user
448
- * @param array $data POSTed data.
449
- *
450
- * @return WP_Error|null
451
- */
452
- public function submit( $user, $data ) {
453
-
454
- if ( empty( $data['pass1'] ) ) {
455
- return new WP_Error(
456
- 'itsec-password-requirements-empty-password',
457
- __( 'Please enter your new password.', 'better-wp-security' )
458
- );
459
- }
460
-
461
- $error = ITSEC_Lib_Password_Requirements::validate_password( $user, $data['pass1'], array(
462
- 'context' => 'interstitial',
463
- ) );
464
-
465
- if ( $error->get_error_message() ) {
466
- return $error;
467
- }
468
-
469
- $error = wp_update_user( array(
470
- 'ID' => $user->ID,
471
- 'user_pass' => $data['pass1']
472
- ) );
473
-
474
- if ( is_wp_error( $error ) ) {
475
- return $error;
476
- }
477
-
478
- return null;
479
- }
480
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/password-requirements/css/index.php DELETED
@@ -1 +0,0 @@
1
- <?php // Silence is golden.
 
core/modules/password-requirements/css/settings-page.css DELETED
@@ -1,3 +0,0 @@
1
- #itsec-module-card-password-requirements h4 {
2
- margin: .25em 0;
3
- }
 
 
 
core/modules/password-requirements/index.php DELETED
@@ -1 +0,0 @@
1
- <?php //You don't belong here. ?>
 
core/modules/password-requirements/js/index.php DELETED
@@ -1 +0,0 @@
1
- <?php // Silence is golden.
 
core/modules/password-requirements/js/settings-page.js DELETED
@@ -1,24 +0,0 @@
1
- (function ( $ ) {
2
-
3
- $( function () {
4
-
5
- $( '.itsec-password-requirements-container' ).each( function () {
6
- updateVisibility( $( this ).data( 'code' ) );
7
- } );
8
-
9
- $( '.itsec-password-requirements-container__enabled-wrap input[type="checkbox"]' ).on( 'change', function ( e ) {
10
- updateVisibility( $( this ).parents( '.itsec-password-requirements-container' ).data( 'code' ) );
11
- } )
12
- } );
13
-
14
- function updateVisibility( code ) {
15
- var $checkbox = $( '.itsec-password-requirements-container__enabled-wrap--' + code + ' input[type="checkbox"]' ),
16
- $details = $( '.itsec-password-requirements-container__settings-wrap--' + code );
17
-
18
- if ( $checkbox.is( ':checked' ) ) {
19
- $details.show();
20
- } else {
21
- $details.hide();
22
- }
23
- }
24
- })( jQuery );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/password-requirements/settings-page.php DELETED
@@ -1,115 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class ITSEC_Password_Requirements_Settings_Page
5
- */
6
- class ITSEC_Password_Requirements_Settings_Page extends ITSEC_Module_Settings_Page {
7
-
8
- /**
9
- * ITSEC_Password_Requirements_Settings_Page constructor.
10
- */
11
- public function __construct() {
12
- $this->id = 'password-requirements';
13
- $this->title = __( 'Password Requirements', 'better-wp-security' );
14
- $this->description = __( 'Manage and configure Password Requirements for users.', 'better-wp-security' );
15
- $this->can_save = true;
16
-
17
- parent::__construct();
18
- }
19
-
20
- protected function render_description( $form ) {
21
- ?>
22
- <p><?php esc_html_e( 'Manage and configure Password Requirements for users.', 'better-wp-security' ); ?></p>
23
- <?php
24
- }
25
-
26
- public function enqueue_scripts_and_styles() {
27
- wp_enqueue_script( 'itsec-password-requirements-settings-page', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
28
- wp_enqueue_style( 'itsec-password-requirements-settings-page', plugins_url( 'css/settings-page.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
29
-
30
- do_action( 'itsec_password_requirements_enqueue_scripts_and_styles' );
31
- }
32
-
33
- public function handle_ajax_request( $data ) {
34
- if ( ! isset( $data['password_requirement'] ) ) {
35
- return;
36
- }
37
-
38
- /**
39
- * Fires when Password Requirement ajax request is incoming.
40
- *
41
- * The dynamic portion of the hook, $data['password_requirement'] refers to the reason code of the requirement.
42
- *
43
- * @param array $data
44
- */
45
- do_action( 'itsec_password_requirements_ajax_' . $data['password_requirement'], $data );
46
- }
47
-
48
- /**
49
- * Render settings.
50
- *
51
- * @param ITSEC_Form $form
52
- */
53
- protected function render_settings( $form ) {
54
-
55
- $requirements = ITSEC_Lib_Password_Requirements::get_registered();
56
- ?>
57
-
58
- <?php do_action( 'itsec_password_requirements_settings_before', $form ); ?>
59
-
60
- <div class="itsec-password-requirements-settings">
61
- <?php do_action( 'itsec_password_requirements_settings_begin', $form ); ?>
62
- <?php foreach ( $requirements as $code => $requirement ):
63
-
64
- if ( null === $requirement['settings_config'] ) {
65
- continue;
66
- }
67
-
68
- $config = call_user_func( $requirement['settings_config'] );
69
-
70
- $form->add_input_group( 'enabled_requirements' );
71
- ?>
72
- <div class="itsec-settings-section itsec-password-requirements-container itsec-password-requirements-container--<?php echo esc_attr( $code ); ?>"
73
- data-code="<?php echo esc_attr( $code ) ?>">
74
- <h4><?php echo esc_html( isset( $config['label'] ) ? $config['label'] : $code ); ?></h4>
75
-
76
- <?php if ( ! empty( $config['description'] ) ): ?>
77
- <p class="description"><?php echo $config['description']; ?></p>
78
- <?php endif; ?>
79
-
80
- <table class="form-table">
81
- <thead class="itsec-password-requirements-container__enabled-wrap itsec-password-requirements-container__enabled-wrap--<?php echo esc_attr( $code ); ?>">
82
- <tr>
83
- <th scope="row">
84
- <label for="itsec-password-requirements-enabled_requirements-<?php echo esc_attr( $code ); ?>">
85
- <?php esc_html_e( 'Enabled', 'better-wp-security' ); ?>
86
- </label>
87
- </th>
88
- <td><?php $form->add_checkbox( $code ); ?></td>
89
- </tr>
90
- </thead>
91
- <?php
92
- $form->remove_input_group();
93
-
94
- if ( ! empty( $config['render'] ) ) :
95
- $form->add_input_group( 'requirement_settings', $code );
96
- ?>
97
- <tbody class="itsec-password-requirements-container__settings-wrap itsec-password-requirements-container__settings-wrap--<?php echo esc_attr( $code ); ?>">
98
- <?php call_user_func( $config['render'], $form ) ?>
99
- </tbody>
100
- <?php
101
- $form->remove_input_group();
102
- $form->remove_input_group();
103
- endif; ?>
104
- </table>
105
- </div>
106
- <?php endforeach; ?>
107
- <?php do_action( 'itsec_password_requirements_settings_end', $form ); ?>
108
- </div>
109
- <?php do_action( 'itsec_password_requirements_settings_after', $form ); ?>
110
- <?php
111
-
112
- }
113
- }
114
-
115
- new ITSEC_Password_Requirements_Settings_Page();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/password-requirements/settings.php DELETED
@@ -1,51 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class ITSEC_Password_Requirements_Settings
5
- */
6
- class ITSEC_Password_Requirements_Settings extends ITSEC_Settings {
7
-
8
- public function get_id() {
9
- return 'password-requirements';
10
- }
11
-
12
- public function get_defaults() {
13
- return array(
14
- 'enabled_requirements' => array(),
15
- 'requirement_settings' => array(),
16
- );
17
- }
18
-
19
- public function load() {
20
-
21
- $this->settings = ITSEC_Storage::get( $this->get_id() );
22
- $defaults = $this->get_defaults();
23
-
24
- if ( ! is_array( $this->settings ) ) {
25
- $this->settings = array();
26
- }
27
-
28
- $this->settings = array_merge( $defaults, $this->settings );
29
-
30
- foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
31
-
32
- if ( ! isset( $this->settings['enabled_requirements'][ $code ] ) ) {
33
- $this->settings['enabled_requirements'][ $code ] = false;
34
- }
35
-
36
- if ( null === $requirement['defaults'] ) {
37
- continue;
38
- }
39
-
40
- if ( isset( $this->settings['requirement_settings'][ $code ] ) ) {
41
- $current = $this->settings['requirement_settings'][ $code ];
42
- } else {
43
- $current = array();
44
- }
45
-
46
- $this->settings['requirement_settings'][ $code ] = wp_parse_args( $current, $requirement['defaults'] );
47
- }
48
- }
49
- }
50
-
51
- ITSEC_Modules::register_settings( new ITSEC_Password_Requirements_Settings() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/password-requirements/validator.php DELETED
@@ -1,62 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class ITSEC_Password_Requirements_Validator
5
- */
6
- class ITSEC_Password_Requirements_Validator extends ITSEC_Validator {
7
-
8
- /** @var string */
9
- private $current_requirement;
10
-
11
- public function get_id() {
12
- return 'password-requirements';
13
- }
14
-
15
- protected function sanitize_settings() {
16
- $this->sanitize_setting( 'array', 'enabled_requirements', __( 'Enabled Requirements', 'better-wp-security' ) );
17
- $this->sanitize_setting( 'array', 'requirement_settings', __( 'Requirement Settings', 'better-wp-security' ) );
18
-
19
- $requirements = ITSEC_Lib_Password_Requirements::get_registered();
20
-
21
- $settings = $this->settings;
22
-
23
- foreach ( $requirements as $code => $requirement ) {
24
- if ( null === $requirement['settings_config'] ) {
25
- continue;
26
- }
27
-
28
- $config = call_user_func( $requirement['settings_config'] );
29
- $sanitize = call_user_func( $config['sanitize'], $this->settings );
30
-
31
- if ( is_wp_error( $sanitize ) ) {
32
- $this->add_error( $sanitize );
33
-
34
- if ( ITSEC_Core::is_interactive() ) {
35
- $this->set_can_save( false );
36
- }
37
- } elseif ( is_array( $sanitize ) ) {
38
- $this->settings = isset( $settings['requirement_settings'][ $code ] ) ? $settings['requirement_settings'][ $code ] : $requirement['defaults'];
39
- $this->current_requirement = $code;
40
-
41
- foreach ( $sanitize as $args ) {
42
- call_user_func_array( array( $this, 'sanitize_setting' ), $args );
43
- }
44
-
45
- $settings['requirement_settings'][ $code ] = $this->settings;
46
- $this->settings = $settings;
47
- $this->current_requirement = null;
48
- }
49
-
50
- }
51
- }
52
-
53
- protected function generate_error( $id, $var, $type, $error ) {
54
- if ( null === $this->current_requirement ) {
55
- return parent::generate_error( $id, $var, $type, $error );
56
- }
57
-
58
- return new WP_Error( "itsec-validator-$id-invalid-type-enabled_requirements-{$this->current_requirement}-$var-$type", $error );
59
- }
60
- }
61
-
62
- ITSEC_Modules::register_validator( new ITSEC_Password_Requirements_Validator() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/modules/pro/settings-page.php CHANGED
@@ -36,6 +36,22 @@ final class ITSEC_Malware_Scheduling_Settings_Page extends ITSEC_Module_Settings
36
  new ITSEC_Malware_Scheduling_Settings_Page();
37
 
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  final class ITSEC_Privilege_Escalation_Settings_Page extends ITSEC_Module_Settings_Page {
40
  public function __construct() {
41
  $this->id = 'privilege';
36
  new ITSEC_Malware_Scheduling_Settings_Page();
37
 
38
 
39
+ final class ITSEC_Password_Expiration_Settings_Page extends ITSEC_Module_Settings_Page {
40
+ public function __construct() {
41
+ $this->id = 'password-expiration';
42
+ $this->title = __( 'Password Expiration', 'better-wp-security' );
43
+ $this->description = __( 'Strengthen the passwords on the site with automated password expiration.', 'better-wp-security' );
44
+ $this->type = 'recommended';
45
+ $this->pro = true;
46
+ $this->upsell = true;
47
+ $this->upsell_url = 'https://ithemes.com/security/wordpress-password-security/?utm_source=wordpressadmin&utm_medium=widget&utm_campaign=itsecfreecta';
48
+
49
+ parent::__construct();
50
+ }
51
+ }
52
+ new ITSEC_Password_Expiration_Settings_Page();
53
+
54
+
55
  final class ITSEC_Privilege_Escalation_Settings_Page extends ITSEC_Module_Settings_Page {
56
  public function __construct() {
57
  $this->id = 'privilege';
core/modules/security-check/scanner.php CHANGED
@@ -48,27 +48,7 @@ final class ITSEC_Security_Check_Scanner {
48
  self::enforce_activation( 'ban-users', __( 'Banned Users', 'better-wp-security' ) );
49
  self::enforce_setting( 'ban-users', 'enable_ban_lists', true, __( 'Enabled the Enable Ban Lists setting in Banned Users.', 'better-wp-security' ) );
50
 
51
- if ( $backup = ITSEC_Lib::get_backup_plugin() ) {
52
- if ( ! function_exists( 'get_plugin_data' ) ) {
53
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
54
- }
55
-
56
- $name = "'{$backup}'";
57
-
58
- if ( function_exists( 'get_plugin_data' ) ) {
59
- $data = get_plugin_data( WP_PLUGIN_DIR . '/' . $backup, false, true );
60
-
61
- if ( isset( $data['Name'] ) ) {
62
- $name = $data['Name'];
63
- }
64
- }
65
-
66
- self::$feedback->add_section( 'backup-activation' );
67
- self::$feedback->add_text( sprintf( __( 'A 3rd-party Backup Plugin, %s, is being used.', 'better-wp-security' ), $name ) );
68
- } else {
69
- self::enforce_activation( 'backup', __( 'Database Backups', 'better-wp-security' ) );
70
- }
71
-
72
  self::enforce_activation( 'brute-force', __( 'Local Brute Force Protection', 'better-wp-security' ) );
73
  self::enforce_activation( 'magic-links', __( 'Magic Links', 'better-wp-security' ) );
74
  self::enforce_activation( 'malware-scheduling', __( 'Malware Scan Scheduling', 'better-wp-security' ) );
@@ -76,10 +56,9 @@ final class ITSEC_Security_Check_Scanner {
76
 
77
  self::add_network_brute_force_signup();
78
 
79
- self::enforce_password_requirement_enabled( 'strength', __( 'Strong Password Enforcement', 'better-wp-security' ) );
80
  self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
81
  self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
82
- self::enforce_setting( 'two-factor', 'exclude_type', 'disabled', esc_html__( 'Changed the Disabled Force Two-Factor for Certain Users to "None".', 'better-wp-security' ) );
83
  self::enforce_setting( 'two-factor', 'protect_user_type', 'privileged_users', esc_html__( 'Changed the User Type Protection setting in Two-Factor Authentication to "Privileged Users".', 'better-wp-security' ) );
84
  self::enforce_setting( 'two-factor', 'protect_vulnerable_users', true, esc_html__( 'Enabled the Vulnerable User Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
85
  self::enforce_setting( 'two-factor', 'protect_vulnerable_site', true, esc_html__( 'Enabled the Vulnerable Site Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
@@ -176,21 +155,6 @@ final class ITSEC_Security_Check_Scanner {
176
  self::$feedback->add_text( sprintf( $text, $name ) );
177
  }
178
 
179
- private static function enforce_password_requirement_enabled( $requirement, $description ) {
180
-
181
- $active = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
182
-
183
- if ( ! empty( $active[ $requirement ] ) ) {
184
- return;
185
- }
186
-
187
- $active[ $requirement ] = true;
188
-
189
- ITSEC_Modules::set_setting( 'password-requirements', 'enabled_requirements', $active );
190
- self::$feedback->add_section( 'enforce-setting-password-requirements-enabled_requirements', array( 'status' => 'action-taken' ) );
191
- self::$feedback->add_text( $description );
192
- }
193
-
194
  public static function activate_network_brute_force( $data ) {
195
  if ( ! isset( $data['email'] ) ) {
196
  ITSEC_Response::add_error( new WP_Error( 'itsec-security-check-missing-email', __( 'The email value is missing.', 'better-wp-security' ) ) );
48
  self::enforce_activation( 'ban-users', __( 'Banned Users', 'better-wp-security' ) );
49
  self::enforce_setting( 'ban-users', 'enable_ban_lists', true, __( 'Enabled the Enable Ban Lists setting in Banned Users.', 'better-wp-security' ) );
50
 
51
+ self::enforce_activation( 'backup', __( 'Database Backups', 'better-wp-security' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  self::enforce_activation( 'brute-force', __( 'Local Brute Force Protection', 'better-wp-security' ) );
53
  self::enforce_activation( 'magic-links', __( 'Magic Links', 'better-wp-security' ) );
54
  self::enforce_activation( 'malware-scheduling', __( 'Malware Scan Scheduling', 'better-wp-security' ) );
56
 
57
  self::add_network_brute_force_signup();
58
 
59
+ self::enforce_activation( 'strong-passwords', __( 'Strong Password Enforcement', 'better-wp-security' ) );
60
  self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
61
  self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
 
62
  self::enforce_setting( 'two-factor', 'protect_user_type', 'privileged_users', esc_html__( 'Changed the User Type Protection setting in Two-Factor Authentication to "Privileged Users".', 'better-wp-security' ) );
63
  self::enforce_setting( 'two-factor', 'protect_vulnerable_users', true, esc_html__( 'Enabled the Vulnerable User Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
64
  self::enforce_setting( 'two-factor', 'protect_vulnerable_site', true, esc_html__( 'Enabled the Vulnerable Site Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
155
  self::$feedback->add_text( sprintf( $text, $name ) );
156
  }
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  public static function activate_network_brute_force( $data ) {
159
  if ( ! isset( $data['email'] ) ) {
160
  ITSEC_Response::add_error( new WP_Error( 'itsec-security-check-missing-email', __( 'The email value is missing.', 'better-wp-security' ) ) );
core/modules/ssl/class-itsec-ssl.php CHANGED
@@ -69,7 +69,7 @@ class ITSEC_SSL {
69
  add_filter( 'script_loader_src', array( $this, 'script_loader_src' ) );
70
  add_filter( 'style_loader_src', array( $this, 'style_loader_src' ) );
71
  add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
72
- } else if ( 'enabled' === $settings['require_ssl'] && 'cli' !== php_sapi_name() && 'GET' === $_SERVER['REQUEST_METHOD'] ) {
73
  $this->redirect_to_https();
74
  }
75
  }
@@ -87,7 +87,7 @@ class ITSEC_SSL {
87
  */
88
  public function do_conditional_ssl_redirect() {
89
 
90
- if ( 'cli' === php_sapi_name() ) {
91
  return;
92
  }
93
 
69
  add_filter( 'script_loader_src', array( $this, 'script_loader_src' ) );
70
  add_filter( 'style_loader_src', array( $this, 'style_loader_src' ) );
71
  add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
72
+ } else if ( 'enabled' === $settings['require_ssl'] && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) && 'GET' === $_SERVER['REQUEST_METHOD'] ) {
73
  $this->redirect_to_https();
74
  }
75
  }
87
  */
88
  public function do_conditional_ssl_redirect() {
89
 
90
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
91
  return;
92
  }
93
 
core/modules/strong-passwords/class-itsec-strong-passwords.php CHANGED
@@ -1,218 +1,247 @@
1
  <?php
2
 
3
  final class ITSEC_Strong_Passwords {
4
-
5
- const STRENGTH_KEY = 'itsec-password-strength';
6
-
7
  public function __construct() {
8
 
9
- add_action( 'itsec_register_password_requirements', array( $this, 'register_requirements' ) );
 
 
10
 
11
  add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts' ) );
12
- add_action( 'resetpass_form', array( $this, 'add_scripts_to_wp_login' ) );
13
- add_action( 'itsec_password_requirements_change_form', array( $this, 'add_scripts_to_wp_login' ) );
14
  }
15
 
16
  /**
17
- * Register the Strong Passwords requirement.
 
 
18
  */
19
- public function register_requirements() {
20
- ITSEC_Lib_Password_Requirements::register( 'strength', array(
21
- 'evaluate' => array( $this, 'evaluate' ),
22
- 'validate' => array( $this, 'validate' ),
23
- 'reason' => array( $this, 'reason' ),
24
- 'meta' => self::STRENGTH_KEY,
25
- 'evaluate_if_not_enabled' => true,
26
- 'defaults' => array( 'role' => 'administrator' ),
27
- 'settings_config' => array( $this, 'get_settings_config' ),
28
- ) );
29
  }
30
 
31
  /**
32
- * Enqueue script to hide the acknowledge weak password checkbox.
33
  *
34
- * @return void
35
  */
36
- public function add_scripts() {
37
 
38
- global $pagenow;
39
 
40
- if ( 'profile.php' !== $pagenow ) {
41
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
 
44
- if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( 'strength' ) ) {
45
- return;
46
  }
47
 
48
- $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
49
- $role = isset( $settings['role'] ) ? $settings['role'] : 'administrator';
 
 
 
 
 
 
 
50
 
51
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
52
 
53
- if ( ITSEC_Lib_Canonical_Roles::is_user_at_least( $role ) ) {
54
- wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
 
 
55
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
  /**
59
- * On the reset password and login interstitial form, render the Strong Passwords JS to hide the acknowledge weak password checkbox.
60
- *
61
- * We have to do this in these late actions so we have access to the correct user data.
62
  *
63
- * @param WP_User $user
 
 
 
64
  */
65
- public function add_scripts_to_wp_login( $user ) {
66
 
67
- if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( 'strength' ) ) {
68
- return;
 
 
69
  }
70
 
71
- $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
72
- $role = isset( $settings['role'] ) ? $settings['role'] : 'administrator';
 
 
 
 
 
 
73
 
74
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
 
 
75
 
76
- if ( ITSEC_Lib_Canonical_Roles::is_user_at_least( $role, $user ) ) {
77
- wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
78
  }
79
- }
80
 
81
- /**
82
- * Provide the reason string displayed to users on the change password form.
83
- *
84
- * @param $evaluation
85
- *
86
- * @return string
87
- */
88
- public function reason( $evaluation ) {
89
- return esc_html__( 'Due to site rules, a strong password is required for your account. Please choose a new password that rates as strong on the meter.', 'better-wp-security' );
90
- }
91
 
92
- /**
93
- * Evaluate the strength of a password.
94
- *
95
- * @param string $password
96
- * @param WP_User $user
97
- *
98
- * @return int
99
- */
100
- public function evaluate( $password, $user ) {
101
- return $this->get_password_strength( $user, $password );
102
  }
103
 
104
  /**
105
- * Validate whether a password strength is acceptable for a given user.
106
  *
107
- * @param int $strength
108
- * @param WP_User|stdClass $user
109
- * @param array $settings
110
- * @param array $args
111
  *
112
- * @return bool
113
  */
114
- public function validate( $strength, $user, $settings, $args ) {
115
-
116
- if ( (int) $strength === 4 ) {
117
- return true;
 
118
  }
119
 
120
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
121
 
122
- $role = isset( $args['canonical'] ) ? $args['canonical'] : ITSEC_Lib_Canonical_Roles::get_user_role( $user );
 
123
 
124
- if ( ! ITSEC_Lib_Canonical_Roles::is_canonical_role_at_least( $settings['role'], $role ) ) {
125
- return true;
 
 
 
126
  }
127
 
128
- return $this->make_error_message();
129
- }
130
 
131
- public function get_settings_config() {
132
- return array(
133
- 'label' => esc_html__( 'Strong Passwords', 'better-wp-security' ),
134
- 'description' => esc_html__( 'Force users to use strong passwords as rated by the WordPress password meter.', 'better-wp-security' ),
135
- 'render' => array( $this, 'render_settings' ),
136
- 'sanitize' => array( $this, 'sanitize_settings' ),
137
- );
138
  }
139
 
140
  /**
141
- * Render the Settings Page.
 
 
142
  *
143
- * @param ITSEC_Form $form
144
  */
145
- public function render_settings( $form ) {
146
-
147
- $href = 'http://codex.wordpress.org/Roles_and_Capabilities';
148
- $link = '<a href="' . $href . '" target="_blank" rel="noopener noreferrer">' . $href . '</a>';
149
- ?>
150
- <tr>
151
- <th scope="row">
152
- <label for="itsec-password-requirements-requirement_settings-strength-role">
153
- <?php esc_html_e( 'Minimum Role', 'better-wp-security' ); ?>
154
- </label>
155
- </th>
156
- <td>
157
- <?php $form->add_canonical_roles( 'role' ); ?>
158
- <br/>
159
- <label for="itsec-password-requirements-requirement_settings-strength-role"><?php _e( 'Minimum role at which a user must choose a strong password.', 'better-wp-security' ); ?></label>
160
- <p class="description"><?php printf( __( 'For more information on WordPress roles and capabilities please see %s.', 'better-wp-security' ), $link ); ?></p>
161
- <p class="warningtext description"><?php _e( 'Warning: If your site invites public registrations setting the role too low may annoy your members.', 'better-wp-security' ); ?></p>
162
- </td>
163
- </tr>
164
- <?php
165
  }
166
 
167
  /**
168
- * Get a list of the sanitizer rules to apply.
169
  *
170
- * @param array $settings
171
  *
172
- * @return array
173
  */
174
- public function sanitize_settings( $settings ) {
175
- return array(
176
- array( 'string', 'role', esc_html__( 'Minimum Role for Strong Passwords', 'better-wp-security' ) ),
177
- array( 'canonical-roles', 'role', esc_html__( 'Minimum Role for Strong Passwords', 'better-wp-security' ) ),
178
- );
179
- }
180
 
181
- /**
182
- * Get the strong password error message according to the given context.
183
- *
184
- * @return string
185
- */
186
- private function make_error_message() {
187
- $message = __( '<strong>Error</strong>: Due to site rules, a strong password is required. Please choose a new password that rates as <strong>Strong</strong> on the meter.', 'better-wp-security' );
188
 
189
- return wp_kses( $message, array( 'strong' => array() ) );
190
  }
191
 
192
  /**
193
- * Calculate the strength of a password.
194
  *
195
- * @param WP_User $user
196
- * @param string $password
 
 
 
 
197
  *
198
- * @return int
199
  */
200
- private function get_password_strength( $user, $password ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
- $penalty_strings = array(
203
- get_site_option( 'admin_email' )
204
- );
205
- $user_properties = array( 'user_login', 'first_name', 'last_name', 'nickname', 'display_name', 'user_email', 'user_url', 'description' );
206
 
207
- foreach ( $user_properties as $user_property ) {
208
- if ( isset( $user->$user_property ) ) {
209
- $penalty_strings[] = $user->$user_property;
 
210
  }
211
- }
212
 
213
- $results = ITSEC_Lib::get_password_strength_results( $password, $penalty_strings );
 
 
214
 
215
- return $results->score;
216
  }
217
  }
218
 
1
  <?php
2
 
3
  final class ITSEC_Strong_Passwords {
 
 
 
4
  public function __construct() {
5
 
6
+ add_filter( 'itsec_password_change_requirement_description_for_strength', array( $this, 'strength_reason' ) );
7
+ add_action( 'user_profile_update_errors', array( $this, 'filter_user_profile_update_errors' ), 0, 3 );
8
+ add_action( 'itsec_validate_password', array( $this, 'validate_password' ), 10, 4 );
9
 
10
  add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts' ) );
11
+ add_action( 'login_enqueue_scripts', array( $this, 'add_scripts' ) );
 
12
  }
13
 
14
  /**
15
+ * Enqueue script to add measured password strength to the form submission data.
16
+ *
17
+ * @return void
18
  */
19
+ public function add_scripts() {
20
+ wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
 
 
 
 
 
 
 
 
21
  }
22
 
23
  /**
24
+ * Get the reason description for why a password change was set to 'strength'.
25
  *
26
+ * @return string
27
  */
28
+ public function strength_reason() {
29
 
30
+ $message = __( 'Due to site rules, a strong password is required for your account. Please choose a new password that rates as <strong>Strong</strong> on the meter.', 'better-wp-security' );
31
 
32
+ return wp_kses( $message, array( 'strong' => '' ) );
33
+ }
34
+
35
+ /**
36
+ * Handle submission of a form to create or edit a user.
37
+ *
38
+ * @param WP_Error $errors WP_Error object.
39
+ * @param bool $update Whether this is a user update.
40
+ * @param stdClass $user User object.
41
+ *
42
+ * @return WP_Error
43
+ */
44
+ public function filter_user_profile_update_errors( $errors, $update, $user ) {
45
+
46
+ // An error regarding the password was already found.
47
+ if ( $errors->get_error_data( 'pass' ) ) {
48
+ return $errors;
49
  }
50
 
51
+ if ( isset( $user->user_pass ) || ! $update ) {
52
+ return $errors;
53
  }
54
 
55
+ // The password was not changed, but an update is occurring. Test to see if we need to prompt for a password change.
56
+ // This also handles the case where a user's role is being changed to one that requires strong password enforcement.
57
+
58
+ $strength = get_user_meta( $user->ID, 'itsec-password-strength', true );
59
+
60
+ if ( ! is_numeric( $strength ) || $strength < 0 || $strength > 4 ) {
61
+ // Not enough data to determine whether a change of password is required.
62
+ return $errors;
63
+ }
64
 
65
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
66
 
67
+ if ( isset( $user->role ) ) {
68
+ $role = $this->get_canonical_role_from_role_and_user( $user->role, $user );
69
+ } else {
70
+ $role = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
71
  }
72
+
73
+ if ( ! $this->role_requires_strong_password( $role ) ) {
74
+ return $errors;
75
+ }
76
+
77
+ if ( 4 === (int) $strength ) {
78
+ return $errors;
79
+ }
80
+
81
+ if ( ! $update ) {
82
+ $context = 'admin-user-create';
83
+ } elseif ( $user->ID === get_current_user_id() ) {
84
+ $context = 'profile-update';
85
+ } else {
86
+ $context = 'admin-profile-update';
87
+ }
88
+
89
+ $errors->add( 'pass', $this->make_error_message( $context ) );
90
+
91
+ return $errors;
92
  }
93
 
94
  /**
95
+ * Validate a new password according to the configured strength rules.
 
 
96
  *
97
+ * @param WP_Error $error
98
+ * @param WP_User $user
99
+ * @param string $new_password
100
+ * @param array $args
101
  */
102
+ public function validate_password( $error, $user, $new_password, $args = array() ) {
103
 
104
+ if ( isset( $args['strength'] ) ) {
105
+ $reported_strength = $args['strength'];
106
+ } else {
107
+ $reported_strength = false;
108
  }
109
 
110
+ if ( empty( $user->ID ) || ! is_numeric( $user->ID ) ) {
111
+ $role = isset( $args['role'] ) ? $args['role'] : get_option( 'default_role', 'subscriber' );
112
+ } elseif ( isset( $args['role'] ) ) {
113
+ $role = $this->get_canonical_role_from_role_and_user( $args['role'], $user );
114
+ } else {
115
+ require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
116
+ $role = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
117
+ }
118
 
119
+ if ( ! $this->role_requires_strong_password( $role ) ) {
120
+ return;
121
+ }
122
 
123
+ if ( ! $this->fails_enforcement( $user, $new_password, $reported_strength ) ) {
124
+ return;
125
  }
 
126
 
127
+ $message = $this->make_error_message( $args['context'] );
 
 
 
 
 
 
 
 
 
128
 
129
+ $error->add( 'pass', $message );
 
 
 
 
 
 
 
 
 
130
  }
131
 
132
  /**
133
+ * Retrieve a canonical role for a user and a role.
134
  *
135
+ * @param string $role
136
+ * @param WP_User $user
 
 
137
  *
138
+ * @return string
139
  */
140
+ private function get_canonical_role_from_role_and_user( $role, $user ) {
141
+ if ( empty( $role ) ) {
142
+ $role_caps = array();
143
+ } else {
144
+ $role_caps = array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) );
145
  }
146
 
147
+ $user_caps = array();
148
 
149
+ if ( isset( $user->caps ) ) {
150
+ $wp_roles = wp_roles();
151
 
152
+ foreach ( $user->caps as $cap => $has ) {
153
+ if ( $has && ! $wp_roles->is_role( $cap ) ) {
154
+ $user_caps[] = $has;
155
+ }
156
+ }
157
  }
158
 
159
+ require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
 
160
 
161
+ return ITSEC_Lib_Canonical_Roles::get_role_from_caps( array_merge( $role_caps, $user_caps ) );
 
 
 
 
 
 
162
  }
163
 
164
  /**
165
+ * Get the strong password error message according to the given context.
166
+ *
167
+ * @param string $context
168
  *
169
+ * @return string
170
  */
171
+ private function make_error_message( $context ) {
172
+ $message = __( '<strong>Error</strong>: Due to site rules, a strong password is required. Please choose a new password that rates as <strong>Strong</strong> on the meter.', 'better-wp-security' );
173
+
174
+ if ( 'admin-user-create' === $context ) {
175
+ $message .= ' ' . __( 'The user has not been created.', 'better-wp-security' );
176
+ } elseif ( 'admin-profile-update' === $context ) {
177
+ $message .= ' ' . __( 'The user changes have not been saved.', 'better-wp-security' );
178
+ } elseif ( 'profile-update' === $context ) {
179
+ $message .= ' ' . __( 'Your profile has not been updated.', 'better-wp-security' );
180
+ } elseif ( 'reset-password' === $context ) {
181
+ $message .= ' ' . __( 'The password has not been updated.', 'better-wp-security' );
182
+ }
183
+
184
+ return wp_kses( $message, array( 'strong' => array() ) );
 
 
 
 
 
 
185
  }
186
 
187
  /**
188
+ * Does the given role require a strong password.
189
  *
190
+ * @param string $role The user's canonical role.
191
  *
192
+ * @return bool
193
  */
194
+ private function role_requires_strong_password( $role ) {
195
+ require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
 
 
 
 
196
 
197
+ $min_role = ITSEC_Modules::get_setting( 'strong-passwords', 'role' );
 
 
 
 
 
 
198
 
199
+ return ITSEC_Lib_Canonical_Roles::is_canonical_role_at_least( $min_role, $role );
200
  }
201
 
202
  /**
203
+ * Determine if the user requires enforcement and if it fails that enforcement.
204
  *
205
+ * @param WP_User|stdClass $user Requires either a valid WP_User object or an object that has the following members:
206
+ * user_login, first_name, last_name, nickname, display_name, user_email, user_url, and
207
+ * description. A member of user_pass is required if $password_strength is false.
208
+ * @param string $new_password The user's new password.
209
+ * @param int|boolean $password_strength [optional] An integer value representing the password strength, if known, or false.
210
+ * Defaults to false.
211
  *
212
+ * @return boolean True if the user requires enforcement and has a password weaker than strong. False otherwise.
213
  */
214
+ private function fails_enforcement( $user, $new_password, $password_strength = false ) {
215
+
216
+ if ( false !== $password_strength ) {
217
+ return $password_strength < 4;
218
+ }
219
+
220
+ if ( ! empty( $_POST['password_strength'] ) && 'strong' !== $_POST['password_strength'] ) {
221
+ // We want to validate the password strength if the form data says that the password is strong since we want
222
+ // to protect against spoofing. If the form data says that the password isn't strong, believe it.
223
+
224
+ $password_strength = 1;
225
+ } else {
226
+ // The form data does not indicate a password strength or the data claimed that the password is strong,
227
+ // which is a claim that must be validated. Use the zxcvbn library to find the password strength score.
228
 
229
+ $penalty_strings = array(
230
+ get_site_option( 'admin_email' )
231
+ );
232
+ $user_properties = array( 'user_login', 'first_name', 'last_name', 'nickname', 'display_name', 'user_email', 'user_url', 'description' );
233
 
234
+ foreach ( $user_properties as $user_property ) {
235
+ if ( isset( $user->$user_property ) ) {
236
+ $penalty_strings[] = $user->$user_property;
237
+ }
238
  }
 
239
 
240
+ $results = ITSEC_Lib::get_password_strength_results( $new_password, $penalty_strings );
241
+ $password_strength = $results->score;
242
+ }
243
 
244
+ return $password_strength < 4;
245
  }
246
  }
247
 
core/modules/strong-passwords/js/script.js CHANGED
@@ -1,3 +1,10 @@
1
  jQuery( document ).ready( function () {
 
 
 
 
 
 
 
2
  jQuery( '.pw-weak' ).remove();
3
- } );
1
  jQuery( document ).ready( function () {
2
+
3
+ jQuery( '#resetpassform, #your-profile, #createuser' ).submit( function () {
4
+
5
+ jQuery( '#submit, #createusersub, #resetpassform' ).append( '<input type="hidden" name="password_strength" id="password_strength" value="' + jQuery( '#pass-strength-result' ).attr( 'class' ).replace( /hide-if-no-js /, '' ) + '">' );
6
+
7
+ } );
8
+
9
  jQuery( '.pw-weak' ).remove();
10
+ } );
core/modules/strong-passwords/settings-page.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Strong_Passwords_Settings_Page extends ITSEC_Module_Settings_Page {
4
+ private $script_version = 1;
5
+
6
+
7
+ public function __construct() {
8
+ $this->id = 'strong-passwords';
9
+ $this->title = __( 'Strong Password Enforcement', 'better-wp-security' );
10
+ $this->description = __( 'Force users to use strong passwords as rated by the WordPress password meter.', 'better-wp-security' );
11
+ $this->type = 'recommended';
12
+
13
+ parent::__construct();
14
+ }
15
+
16
+ protected function render_description( $form ) {
17
+
18
+ ?>
19
+ <p><?php _e( 'Force users to use strong passwords as rated by the WordPress password meter.', 'better-wp-security' ); ?></p>
20
+ <?php
21
+
22
+ }
23
+
24
+ protected function render_settings( $form ) {
25
+ $roles = array(
26
+ 'administrator' => translate_user_role( 'Administrator' ),
27
+ 'editor' => translate_user_role( 'Editor' ),
28
+ 'author' => translate_user_role( 'Author' ),
29
+ 'contributor' => translate_user_role( 'Contributor' ),
30
+ 'subscriber' => translate_user_role( 'Subscriber' ),
31
+ );
32
+
33
+ ?>
34
+ <table class="form-table itsec-settings-section">
35
+ <tr>
36
+ <th scope="row"><label for="itsec-strong-passwords-role"><?php _e( 'Select Role for Strong Passwords', 'better-wp-security' ); ?></label></th>
37
+ <td>
38
+ <?php $form->add_select( 'role', $roles ); ?>
39
+ <br />
40
+ <label for="itsec-strong-passwords-role"><?php _e( 'Minimum role at which a user must choose a strong password.', 'better-wp-security' ); ?></label>
41
+ <p class="description"><?php printf( __( 'For more information on WordPress roles and capabilities please see <a href="%1$s" target="_blank" rel="noopener noreferrer">%1$s</a>.', 'better-wp-security' ), 'http://codex.wordpress.org/Roles_and_Capabilities' ); ?></p>
42
+ <p class="warningtext description"><?php _e( 'Warning: If your site invites public registrations setting the role too low may annoy your members.', 'better-wp-security' ); ?></p>
43
+ </td>
44
+ </tr>
45
+ </table>
46
+ <?php
47
+
48
+ }
49
+ }
50
+
51
+ new ITSEC_Strong_Passwords_Settings_Page();
core/modules/strong-passwords/setup.php CHANGED
@@ -6,10 +6,10 @@ if ( ! class_exists( 'ITSEC_Strong_Passwords_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
 
@@ -86,28 +86,6 @@ if ( ! class_exists( 'ITSEC_Strong_Passwords_Setup' ) ) {
86
  }
87
  }
88
 
89
- if ( $itsec_old_version < 4096 ) {
90
- $active = get_site_option( 'itsec_active_modules', array() );
91
-
92
- if ( ! empty( $active['strong-passwords'] ) ) {
93
- $active_requirements = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
94
- $active_requirements['strength'] = true;
95
- ITSEC_Modules::set_setting( 'password-requirements', 'enabled_requirements', $active_requirements );
96
- }
97
-
98
- $requirement_settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
99
- $requirement_settings['strength']['role'] = ITSEC_Modules::get_setting( 'strong-passwords', 'role', 'administrator' );
100
- ITSEC_Modules::set_setting( 'password-requirements', 'requirement_settings', $requirement_settings );
101
-
102
- unset( $active['strong-passwords'] );
103
-
104
- // Need to do this directly to be able to remove a module from the list entirely.
105
- if ( is_multisite() ) {
106
- update_site_option( 'itsec_active_modules', $active );
107
- } else {
108
- update_option( 'itsec_active_modules', $active );
109
- }
110
- }
111
  }
112
 
113
  }
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
 
86
  }
87
  }
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
  }
core/modules/wordpress-tweaks/class-itsec-wordpress-tweaks.php CHANGED
@@ -128,68 +128,6 @@ final class ITSEC_WordPress_Tweaks {
128
  }
129
  }
130
 
131
- public function deinit() {
132
- $this->remove_config_hooks();
133
-
134
- // Functional code for the valid_user_login_type setting.
135
- if ( 'email' === $this->settings['valid_user_login_type'] ) {
136
- remove_action( 'login_init', array( $this, 'add_gettext_filter' ) );
137
- remove_filter( 'authenticate', array( $this, 'add_gettext_filter' ), 0 );
138
- add_filter( 'authenticate', 'wp_authenticate_username_password', 20 );
139
- } else if ( 'username' === $this->settings['valid_user_login_type'] ) {
140
- remove_action( 'login_init', array( $this, 'add_gettext_filter' ) );
141
- remove_filter( 'authenticate', array( $this, 'add_gettext_filter' ), 0 );
142
- add_filter( 'authenticate', 'wp_authenticate_email_password', 20 );
143
- }
144
-
145
- // Functional code for the allow_xmlrpc_multiauth setting.
146
- if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && ! $this->settings['allow_xmlrpc_multiauth'] ) {
147
- remove_filter( 'authenticate', array( $this, 'block_multiauth_attempts' ), 0 );
148
- }
149
-
150
- //remove wlmanifest link if turned on
151
- if ( $this->settings['wlwmanifest_header'] ) {
152
- add_action( 'wp_head', 'wlwmanifest_link' );
153
- }
154
-
155
- //remove rsd link from header if turned on
156
- if ( $this->settings['edituri_header'] ) {
157
- add_action( 'wp_head', 'rsd_link' );
158
- }
159
-
160
- //Disable XML-RPC
161
- if ( 2 == $this->settings['disable_xmlrpc'] ) {
162
- remove_filter( 'xmlrpc_enabled', '__return_null' );
163
- remove_filter( 'bloginfo_url', array( $this, 'remove_pingback_url' ), 10 );
164
- } else if ( 1 == $this->settings['disable_xmlrpc'] ) { // Disable pingbacks
165
- remove_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
166
- }
167
-
168
- remove_filter( 'rest_dispatch_request', array( $this, 'filter_rest_dispatch_request' ), 10 );
169
-
170
- //Process remove login errors
171
- if ( $this->settings['login_errors'] ) {
172
- remove_filter( 'login_errors', '__return_null' );
173
- }
174
-
175
- //Process require unique nicename
176
- if ( $this->settings['force_unique_nicename'] ) {
177
- remove_action( 'user_profile_update_errors', array( $this, 'force_unique_nicename' ), 10 );
178
- }
179
-
180
- //Process remove extra author archives
181
- if ( $this->settings['disable_unused_author_pages'] ) {
182
- remove_action( 'template_redirect', array( $this, 'disable_unused_author_pages' ) );
183
- }
184
-
185
- if ( $this->settings['block_tabnapping'] ) {
186
- remove_action( 'wp_enqueue_scripts', array( $this, 'add_block_tabnapping_script' ) );
187
- remove_action( 'admin_enqueue_scripts', array( $this, 'add_block_tabnapping_script' ) );
188
- }
189
-
190
- remove_filter( 'rest_request_after_callbacks', array( $this, 'filter_taxonomies_response' ), 10, 3 );
191
- }
192
-
193
  /**
194
  * Add filter for gettext to change text for the valid_user_login_type setting changes.
195
  *
@@ -265,44 +203,24 @@ final class ITSEC_WordPress_Tweaks {
265
  return $result;
266
  }
267
 
268
- if ( function_exists( 'rest_authorization_required_code' ) ) {
269
- $code = rest_authorization_required_code();
270
- } else {
271
- $code = is_user_logged_in() ? 403 : 401;
272
- }
273
-
274
- $error = new WP_Error( 'itsec_rest_api_access_restricted', __( 'You do not have sufficient permission to access this endpoint. Access to REST API requests is restricted by iThemes Security settings.', 'better-wp-security' ), array(
275
- 'status' => $code,
276
- ) );
277
-
278
  // Each of the following endpoints can be restricted based on a simple capability check.
279
  $endpoint_caps = array(
280
  'comments' => 'moderate_comments',
281
  'statuses' => 'edit_posts',
 
282
  'types' => 'edit_posts',
283
  );
284
 
285
- if ( version_compare( $GLOBALS['wp_version'], '4.7.0', '<' ) ) {
286
- // We need the request_after_callbacks filter to perform this blocking. So fallback to a more general edit_posts capability when this hook isn't available.
287
- $endpoint_caps['taxonomies'] = 'edit_posts';
288
- }
289
-
290
  foreach ( $endpoint_caps as $endpoint => $cap ) {
291
  if ( $endpoint === $route_parts[2] ) {
292
  if ( current_user_can( $cap ) ) {
293
  return $result;
294
  }
295
 
296
- return $error;
297
  }
298
  }
299
 
300
- if ( 'taxonomies' === $route_parts[2] ) {
301
- add_filter( 'rest_request_after_callbacks', array( $this, 'filter_taxonomies_response' ), 10, 3 );
302
-
303
- return $result;
304
- }
305
-
306
  if ( 'users' === $route_parts[2] ) {
307
  if ( isset( $route_parts[3] ) && 'me' === $route_parts[3] ) {
308
  // The users/me endpoint has its own permissions checks.
@@ -314,7 +232,7 @@ final class ITSEC_WordPress_Tweaks {
314
  return $result;
315
  }
316
 
317
- return $error;
318
  }
319
 
320
 
@@ -341,7 +259,7 @@ final class ITSEC_WordPress_Tweaks {
341
  if ( current_user_can( $taxonomy->cap->edit_terms ) ) {
342
  return $result;
343
  } else {
344
- return $error;
345
  }
346
  }
347
  }
@@ -363,7 +281,7 @@ final class ITSEC_WordPress_Tweaks {
363
  if ( current_user_can( $post_type->cap->edit_posts ) ) {
364
  return $result;
365
  } else {
366
- return $error;
367
  }
368
  }
369
  }
@@ -376,74 +294,6 @@ final class ITSEC_WordPress_Tweaks {
376
  return $result;
377
  }
378
 
379
- /**
380
- * Filter the taxonomies response to exclude taxonomies the user does not have edit permission for.
381
- *
382
- * @param WP_REST_Response|WP_Error $response
383
- * @param array $handler
384
- * @param WP_REST_Request $request
385
- *
386
- * @return WP_REST_Response|WP_Error
387
- */
388
- public function filter_taxonomies_response( $response, $handler, $request ) {
389
-
390
- if ( is_wp_error( $response ) ) {
391
- return $response;
392
- }
393
-
394
- $route = strtolower( $request->get_route() );
395
- $route_parts = explode( '/', trim( $route, '/' ) );
396
-
397
- if ( 'wp' !== $route_parts[0] || ! isset( $route_parts[2] ) || 'taxonomies' !== $route_parts[2] ) {
398
- return $response;
399
- }
400
-
401
- if ( function_exists( 'rest_authorization_required_code' ) ) {
402
- $code = rest_authorization_required_code();
403
- } else {
404
- $code = is_user_logged_in() ? 403 : 401;
405
- }
406
-
407
- $error = new WP_Error( 'itsec_rest_api_access_restricted', __( 'You do not have sufficient permission to access this endpoint. Access to REST API requests is restricted by iThemes Security settings.', 'better-wp-security' ), array(
408
- 'status' => $code,
409
- ) );
410
-
411
- $data = $response->get_data();
412
-
413
- if ( isset( $route_parts[3] ) ) {
414
- if ( ! $taxonomy = get_taxonomy( $route_parts[3] ) ) {
415
- return $response;
416
- }
417
-
418
- if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) {
419
- return $error;
420
- }
421
-
422
- return $response;
423
- }
424
-
425
- foreach ( $data as $i => $taxonomy_data ) {
426
- if ( ! isset( $taxonomy_data['slug'] ) ) {
427
- continue;
428
- }
429
-
430
- if ( ! $taxonomy = get_taxonomy( $taxonomy_data['slug'] ) ) {
431
- continue;
432
- }
433
-
434
- if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) {
435
- unset( $data[ $i ] );
436
- }
437
- }
438
-
439
- if ( ! $data ) {
440
- return $error;
441
- }
442
-
443
- $response->set_data( $data );
444
-
445
- return $response;
446
- }
447
 
448
  public function add_block_tabnapping_script() {
449
  wp_enqueue_script( 'blankshield', plugins_url( 'js/blankshield/blankshield.min.js', __FILE__ ), array(), ITSEC_Core::get_plugin_build(), true );
128
  }
129
  }
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  /**
132
  * Add filter for gettext to change text for the valid_user_login_type setting changes.
133
  *
203
  return $result;
204
  }
205
 
 
 
 
 
 
 
 
 
 
 
206
  // Each of the following endpoints can be restricted based on a simple capability check.
207
  $endpoint_caps = array(
208
  'comments' => 'moderate_comments',
209
  'statuses' => 'edit_posts',
210
+ 'taxonomies' => 'edit_terms',
211
  'types' => 'edit_posts',
212
  );
213
 
 
 
 
 
 
214
  foreach ( $endpoint_caps as $endpoint => $cap ) {
215
  if ( $endpoint === $route_parts[2] ) {
216
  if ( current_user_can( $cap ) ) {
217
  return $result;
218
  }
219
 
220
+ return new WP_Error( 'itsec_rest_api_access_restricted', __( 'You do not have sufficient permission to access this endpoint. Access to REST API requests is restricted by iThemes Security settings.', 'better-wp-security' ) );
221
  }
222
  }
223
 
 
 
 
 
 
 
224
  if ( 'users' === $route_parts[2] ) {
225
  if ( isset( $route_parts[3] ) && 'me' === $route_parts[3] ) {
226
  // The users/me endpoint has its own permissions checks.
232
  return $result;
233
  }
234
 
235
+ return new WP_Error( 'itsec_rest_api_access_restricted', __( 'You do not have sufficient permission to access this endpoint. Access to REST API requests is restricted by iThemes Security settings.', 'better-wp-security' ) );
236
  }
237
 
238
 
259
  if ( current_user_can( $taxonomy->cap->edit_terms ) ) {
260
  return $result;
261
  } else {
262
+ return new WP_Error( 'itsec_rest_api_access_restricted', __( 'You do not have sufficient permission to access this endpoint. Access to REST API requests is restricted by iThemes Security settings.', 'better-wp-security' ) );
263
  }
264
  }
265
  }
281
  if ( current_user_can( $post_type->cap->edit_posts ) ) {
282
  return $result;
283
  } else {
284
+ return new WP_Error( 'itsec_rest_api_access_restricted', __( 'You do not have sufficient permission to access this endpoint. Access to REST API requests is restricted by iThemes Security settings.', 'better-wp-security' ) );
285
  }
286
  }
287
  }
294
  return $result;
295
  }
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
  public function add_block_tabnapping_script() {
299
  wp_enqueue_script( 'blankshield', plugins_url( 'js/blankshield/blankshield.min.js', __FILE__ ), array(), ITSEC_Core::get_plugin_build(), true );
core/modules/wordpress-tweaks/settings-page.php CHANGED
@@ -158,14 +158,6 @@ final class ITSEC_WordPress_Tweaks_Settings_Page extends ITSEC_Module_Settings_P
158
  </ul>
159
  </td>
160
  </tr>
161
- <tr>
162
- <th scope="row"><label for="itsec-wordpress-tweaks-patch_thumb_file_traversal"><?php esc_html_e( 'Mitigate Attachment File Traversal Attack', 'better-wp-security' ); ?></label></th>
163
- <td>
164
- <?php $form->add_checkbox( 'patch_thumb_file_traversal' ); ?>
165
- <label for="itsec-wordpress-tweaks-patch_thumb_file_traversal"><?php esc_html_e( 'Prevent attachment thumbnails from traversing to other files.', 'better-wp-security' ); ?></label>
166
- <p class="description"><?php esc_html_e( 'Disabling this feature is not recommended. This helps mitigate an attack where users with the "author" role or higher could delete any file in your WordPress installation including sensitive files like wp-config.php.', 'better-wp-security' ); ?></p>
167
- </td>
168
- </tr>
169
  </table>
170
  <?php
171
 
158
  </ul>
159
  </td>
160
  </tr>
 
 
 
 
 
 
 
 
161
  </table>
162
  <?php
163
 
core/modules/wordpress-tweaks/settings.php CHANGED
@@ -19,7 +19,6 @@ final class ITSEC_Wordpress_Tweaks_Settings extends ITSEC_Settings {
19
  'disable_unused_author_pages' => false,
20
  'block_tabnapping' => false,
21
  'valid_user_login_type' => 'both',
22
- 'patch_thumb_file_traversal' => true,
23
  );
24
  }
25
  }
19
  'disable_unused_author_pages' => false,
20
  'block_tabnapping' => false,
21
  'valid_user_login_type' => 'both',
 
22
  );
23
  }
24
  }
core/modules/wordpress-tweaks/validator.php CHANGED
@@ -21,7 +21,6 @@ class ITSEC_WordPress_Tweaks_Validator extends ITSEC_Validator {
21
  $this->sanitize_setting( 'bool', 'disable_unused_author_pages', __( 'Disable Extra User Archives', 'better-wp-security' ) );
22
  $this->sanitize_setting( 'bool', 'block_tabnapping', __( 'Protect Against Tabnapping', 'better-wp-security' ) );
23
  $this->sanitize_setting( array( 'both', 'email', 'username' ), 'valid_user_login_type', __( 'Login with Email Address or Username', 'better-wp-security' ) );
24
- $this->sanitize_setting( 'bool', 'patch_thumb_file_traversal', __( 'Mitigate Attachment File Traversal Attack', 'better-wp-security' ) );
25
  }
26
 
27
  protected function validate_settings() {
21
  $this->sanitize_setting( 'bool', 'disable_unused_author_pages', __( 'Disable Extra User Archives', 'better-wp-security' ) );
22
  $this->sanitize_setting( 'bool', 'block_tabnapping', __( 'Protect Against Tabnapping', 'better-wp-security' ) );
23
  $this->sanitize_setting( array( 'both', 'email', 'username' ), 'valid_user_login_type', __( 'Login with Email Address or Username', 'better-wp-security' ) );
 
24
  }
25
 
26
  protected function validate_settings() {
core/notify.php CHANGED
@@ -124,11 +124,9 @@ class ITSEC_Notify {
124
 
125
  $data_proxy = new ITSEC_Notify_Data_Proxy( $data );
126
 
127
- $mail = $nc->mail( 'digest' );
128
  $mail->add_header( $title, $banner_title );
129
- $mail->start_group( 'intro' );
130
  $mail->add_info_box( sprintf( esc_html__( 'The following is a summary of security related activity on your site: %s', 'better-wp-security' ), '<b>' . $mail->get_display_url() . '</b>' ) );
131
- $mail->end_group();
132
 
133
  $content = $mail->get_content();
134
 
@@ -205,13 +203,10 @@ class ITSEC_Notify {
205
  '<a href="' . ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_logs_page_url() ) . '"><b>',
206
  '</b></a>'
207
  ) );
208
-
209
- if ( apply_filters( 'itsec_security_digest_include_security_check', true ) ) {
210
- $mail->add_divider();
211
- $mail->add_large_text( esc_html__( 'Is your site as secure as it could be?', 'better-wp-security' ) );
212
- $mail->add_text( esc_html__( 'Ensure your site is using recommended settings and features with a security check.', 'better-wp-security' ) );
213
- $mail->add_button( esc_html__( 'Run a Security Check ✓', 'better-wp-security' ), ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_security_check_page_url() ) );
214
- }
215
 
216
  $mail->add_footer();
217
 
124
 
125
  $data_proxy = new ITSEC_Notify_Data_Proxy( $data );
126
 
127
+ $mail = $nc->mail();
128
  $mail->add_header( $title, $banner_title );
 
129
  $mail->add_info_box( sprintf( esc_html__( 'The following is a summary of security related activity on your site: %s', 'better-wp-security' ), '<b>' . $mail->get_display_url() . '</b>' ) );
 
130
 
131
  $content = $mail->get_content();
132
 
203
  '<a href="' . ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_logs_page_url() ) . '"><b>',
204
  '</b></a>'
205
  ) );
206
+ $mail->add_divider();
207
+ $mail->add_large_text( esc_html__( 'Is your site as secure as it could be?', 'better-wp-security' ) );
208
+ $mail->add_text( esc_html__( 'Ensure your site is using recommended settings and features with a security check.', 'better-wp-security' ) );
209
+ $mail->add_button( esc_html__( 'Run a Security Check ', 'better-wp-security' ), ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_security_check_page_url() ) );
 
 
 
210
 
211
  $mail->add_footer();
212
 
core/response.php CHANGED
@@ -72,7 +72,7 @@ final class ITSEC_Response {
72
 
73
  return $self->errors;
74
  }
75
-
76
  public static function add_warnings( $warnings ) {
77
  foreach ( $warnings as $warning ) {
78
  self::add_warning( $warning );
@@ -199,10 +199,6 @@ final class ITSEC_Response {
199
  self::get_instance()->add_js_function_call( 'reloadAllModules' );
200
  }
201
 
202
- public static function refresh_page() {
203
- self::get_instance()->add_js_function_call( 'refreshPage' );
204
- }
205
-
206
  public static function regenerate_wp_config() {
207
  $self = self::get_instance();
208
 
@@ -283,26 +279,16 @@ final class ITSEC_Response {
283
  $added = array_diff( $new, $current );
284
 
285
  if ( $added ) {
286
- self::flag_new_notifications_available();
 
 
 
 
 
 
287
  }
288
  }
289
 
290
- public static function flag_new_notifications_available() {
291
- static $run_count = 0;
292
-
293
- if ( $run_count++ > 0 ) {
294
- return;
295
- }
296
-
297
- self::reload_module( 'notification-center' );
298
- self::get_instance()->has_new_notifications = true;
299
- self::get_instance()->add_info( sprintf(
300
- esc_html__( 'New notifications available in the %1$sNotification Center%2$s.', 'better-wp-security' ),
301
- '<a href="#" data-module-link="notification-center">',
302
- '</a>'
303
- ) );
304
- }
305
-
306
  public static function get_raw_data() {
307
  $self = self::get_instance();
308
 
72
 
73
  return $self->errors;
74
  }
75
+
76
  public static function add_warnings( $warnings ) {
77
  foreach ( $warnings as $warning ) {
78
  self::add_warning( $warning );
199
  self::get_instance()->add_js_function_call( 'reloadAllModules' );
200
  }
201
 
 
 
 
 
202
  public static function regenerate_wp_config() {
203
  $self = self::get_instance();
204
 
279
  $added = array_diff( $new, $current );
280
 
281
  if ( $added ) {
282
+ self::reload_module( 'notification-center' );
283
+ self::get_instance()->has_new_notifications = true;
284
+ self::get_instance()->add_info( sprintf(
285
+ esc_html__( 'New notifications available in the %1$sNotification Center%2$s.', 'better-wp-security' ),
286
+ '<a href="#" data-module-link="notification-center">',
287
+ '</a>'
288
+ ) );
289
  }
290
  }
291
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  public static function get_raw_data() {
293
  $self = self::get_instance();
294
 
core/setup.php CHANGED
@@ -9,6 +9,7 @@
9
  final class ITSEC_Setup {
10
  public static function handle_activation() {
11
  self::setup_plugin_data();
 
12
  }
13
 
14
  public static function handle_deactivation() {
@@ -142,11 +143,13 @@ final class ITSEC_Setup {
142
  ITSEC_Lib::schedule_cron_test();
143
  }
144
 
145
- if ( null === get_site_option( 'itsec-enable-grade-report', null ) ) {
146
- update_site_option( 'itsec-enable-grade-report', ITSEC_Modules::get_setting( 'global', 'enable_grade_report' ) );
147
  }
148
 
149
- ITSEC_Core::get_scheduler()->register_events();
 
 
150
 
151
  // Update stored build number.
152
  ITSEC_Modules::set_setting( 'global', 'build', ITSEC_Core::get_plugin_build() );
@@ -199,7 +202,6 @@ final class ITSEC_Setup {
199
 
200
  delete_site_option( 'itsec-storage' );
201
  delete_site_option( 'itsec_active_modules' );
202
- delete_site_option( 'itsec-enable-grade-report' );
203
 
204
  ITSEC_Schema::remove_database_tables();
205
  ITSEC_Lib_Directory::remove( ITSEC_Core::get_storage_dir() );
9
  final class ITSEC_Setup {
10
  public static function handle_activation() {
11
  self::setup_plugin_data();
12
+ ITSEC_Core::get_scheduler()->register_events();
13
  }
14
 
15
  public static function handle_deactivation() {
143
  ITSEC_Lib::schedule_cron_test();
144
  }
145
 
146
+ if ( $build < 4087 ) {
147
+ ITSEC_Core::get_scheduler()->register_events();
148
  }
149
 
150
+ if ( $build < 4094 ) {
151
+ ITSEC_Core::get_scheduler()->register_events();
152
+ }
153
 
154
  // Update stored build number.
155
  ITSEC_Modules::set_setting( 'global', 'build', ITSEC_Core::get_plugin_build() );
202
 
203
  delete_site_option( 'itsec-storage' );
204
  delete_site_option( 'itsec_active_modules' );
 
205
 
206
  ITSEC_Schema::remove_database_tables();
207
  ITSEC_Lib_Directory::remove( ITSEC_Core::get_storage_dir() );
history.txt CHANGED
@@ -765,41 +765,3 @@
765
  7.0.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
766
  Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
767
  Bug Fix: Skip recovery if File Change storage is empty.
768
- 7.0.2 - 2018-06-14 - Chris Jean & Timothy Jacobs
769
- Enhancement: Add UI to cancel in progress File Scan.
770
- Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
771
- Enhancement: Add debug settings JSON editor.
772
- Enhancement: Continually evaluate password strength for users instead of only during registration.
773
- Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
774
- Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
775
- Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
776
- Bug Fix: Enforce the Strong Passwords requirement during Security Check.
777
- Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
778
- Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
779
- Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
780
- Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
781
- Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
782
- Bug Fix: Remove distributed storage table on uninstall.
783
- Tweak: Don't write to the tracked files setting if the file hash has not changed.
784
- Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
785
- 7.0.3 - 2018-06-18 - Chris Jean & Timothy Jacobs
786
- Security Fix: Fixed SQL injection vulnerability in the logs page. Note: Admin privileges are required to exploit this vulnerability. Thanks to Çlirim Emini, Penetration Tester at sentry.co.com, for reporting this vulnerability.
787
- Bug Fix: Provide default values for enabled requirements.
788
- 7.0.4 - 2018-06-27 - Chris Jean & Timothy Jacobs
789
- Enhancement: Add mitigation for the WordPress Attachment File Traversal and Deletion vulnerability.
790
- Tweak: Fire a WordPress action whenever settings are updated.
791
- Bug Fix: Improved input sanitization on the logs page to prevent triggering warnings.
792
- 7.1.0 - 2018-08-15 - Chris Jean & Timothy Jacobs
793
- New Feature: Allow for globally setting recipients for admin-targeted notifications. All new notifications will default to the recipients in this list. Notifications can be set to use the default list or switch to a custom list.
794
- Enhancement: Added a setting to enable/disable the Grade Report feature of Pro.
795
- Tweak: Check if an IP is blacklisted on page load for compatibility with servers that cannot process server configuration level bans immediately.
796
- Tweak: Display a time diff until the next event on the Debug page.
797
- Tweak: Use Logging API for tracking Notification Center errors.
798
- Tweak: Register Scheduler Events whenever the plugin build changes.
799
- Tweak: Allow for filtering logs by any module recorded.
800
- Tweak: Account for 3rd-party Backup Plugin in Security Check.
801
- Bug Fix: 404 detection for plugins that mark is_404 later in the hook sequence.
802
- Bug Fix: REST API Protection blocked the Taxonomies route for all users.
803
- Bug Fix: Account for any CLI PHP SAPI instead of just WP-CLI in the SSL Module.
804
- Bug Fix: Fixed how the Grade Report enable/disable status is stored to fix admin page loading issues on some sites.
805
- Bug Fix: Fix serialization of closure error when a plugin registering a hook with a closure is in the boot-up stack and the notification center is triggered too early in the cycle.
765
  7.0.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
766
  Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
767
  Bug Fix: Skip recovery if File Change storage is empty.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: ithemes, chrisjean, gerroald, 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: 4.9.8
6
- Stable tag: 7.1.0
7
  Requires PHP: 5.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -189,48 +189,6 @@ Free support may be available with the help of the community in the <a href="htt
189
 
190
  == Changelog ==
191
 
192
- = 7.1.0 =
193
- * New Feature: Allow for globally setting recipients for admin-targeted notifications. All new notifications will default to the recipients in this list. Notifications can be set to use the default list or switch to a custom list.
194
- * Enhancement: Added a setting to enable/disable the Grade Report feature of Pro.
195
- * Tweak: Check if an IP is blacklisted on page load for compatibility with servers that cannot process server configuration level bans immediately.
196
- * Tweak: Display a time diff until the next event on the Debug page.
197
- * Tweak: Use Logging API for tracking Notification Center errors.
198
- * Tweak: Register Scheduler Events whenever the plugin build changes.
199
- * Tweak: Allow for filtering logs by any module recorded.
200
- * Tweak: Account for 3rd-party Backup Plugin in Security Check.
201
- * Bug Fix: 404 detection for plugins that mark is_404 later in the hook sequence.
202
- * Bug Fix: REST API Protection blocked the Taxonomies route for all users.
203
- * Bug Fix: Account for any CLI PHP SAPI instead of just WP-CLI in the SSL Module.
204
- * Bug Fix: Fixed how the Grade Report enable/disable status is stored to fix admin page loading issues on some sites.
205
- * Bug Fix: Fix serialization of closure error when a plugin registering a hook with a closure is in the boot-up stack and the notification center is triggered too early in the cycle.
206
-
207
- = 7.0.4 =
208
- * Enhancement: Add mitigation for the WordPress Attachment File Traversal and Deletion vulnerability.
209
- * Tweak: Fire a WordPress action whenever settings are updated.
210
- * Bug Fix: Improved input sanitization on the logs page to prevent triggering warnings.
211
-
212
- = 7.0.3 =
213
- * Security Fix: Fixed SQL injection vulnerability in the logs page. Note: Admin privileges are required to exploit this vulnerability. Thanks to Çlirim Emini, Penetration Tester at sentry.co.com, for reporting this vulnerability.
214
- * Bug Fix: Provide default values for enabled requirements.
215
-
216
- = 7.0.2 =
217
- * Enhancement: Add UI to cancel in progress File Scan.
218
- * Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
219
- * Enhancement: Add debug settings JSON editor.
220
- * Enhancement: Continually evaluate password strength for users instead of only during registration.
221
- * Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
222
- * Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
223
- * Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
224
- * Bug Fix: Enforce the Strong Passwords requirement during Security Check.
225
- * Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
226
- * Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
227
- * Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
228
- * Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
229
- * Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
230
- * Bug Fix: Remove distributed storage table on uninstall.
231
- * Tweak: Don't write to the tracked files setting if the file hash has not changed.
232
- * Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
233
-
234
  = 7.0.1 =
235
  * Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
236
  * Bug Fix: Skip recovery if File Change storage is empty.
@@ -496,5 +454,5 @@ Free support may be available with the help of the community in the <a href="htt
496
 
497
  == Upgrade Notice ==
498
 
499
- = 7.1.0 =
500
- Version 7.1.0 contains important bug fixes and improvements. It is recommended for all users.
2
  Contributors: ithemes, chrisjean, gerroald, 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: 4.9.6
6
+ Stable tag: 7.0.1
7
  Requires PHP: 5.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
189
 
190
  == Changelog ==
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  = 7.0.1 =
193
  * Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
194
  * Bug Fix: Skip recovery if File Change storage is empty.
454
 
455
  == Upgrade Notice ==
456
 
457
+ = 7.0.1 =
458
+ Version 7.0.0 contains important additions to support privacy controls, bug fixes, and various enhancements. It is recommended for all users.