WP Security Audit Log - Version 3.1.2

Version Description

Download this release

Release Info

Developer WPWhiteSecurity
Plugin Icon 128x128 WP Security Audit Log
Version 3.1.2
Comparing to
See all releases

Code changes from version 3.1.1 to 3.1.2

classes/Adapters/MySQL/OccurrenceAdapter.php CHANGED
@@ -239,6 +239,23 @@ class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord im
239
  );
240
  }
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  /**
243
  * Add conditions to the Query
244
  *
239
  );
240
  }
241
 
242
+ /**
243
+ * Gets occurences of the alert 1003.
244
+ *
245
+ * @param array $args - User arguments.
246
+ * @return WSAL_Occurrence[]
247
+ */
248
+ public function check_alert_1003( $args = array() ) {
249
+ return self::LoadMultiQuery(
250
+ 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
251
+ WHERE (occurrence.alert_id = %d)
252
+ AND (occurrence.site_id = %d)
253
+ AND (occurrence.created_on BETWEEN %d AND %d)
254
+ GROUP BY occurrence.id',
255
+ $args
256
+ );
257
+ }
258
+
259
  /**
260
  * Add conditions to the Query
261
  *
classes/AuditLogListView.php CHANGED
@@ -90,12 +90,7 @@ class WSAL_AuditLogListView extends WP_List_Table {
90
  <?php foreach ( $items as $item ) { ?>
91
  <option
92
  value="<?php echo is_string( $item ) ? '' : esc_attr( $item ); ?>"
93
- <?php
94
- if ( $item == $p ) {
95
- echo 'selected="selected"';
96
- }
97
- ?>
98
- >
99
  <?php echo esc_html( $item ); ?>
100
  </option>
101
  <?php } ?>
@@ -118,12 +113,7 @@ class WSAL_AuditLogListView extends WP_List_Table {
118
  <option value="0"><?php esc_html_e( 'All Sites', 'wp-security-audit-log' ); ?></option>
119
  <?php foreach ( $this->get_sites() as $info ) { ?>
120
  <option value="<?php echo esc_attr( $info->blog_id ); ?>"
121
- <?php
122
- if ( $info->blog_id == $curr ) {
123
- echo 'selected="selected"';
124
- }
125
- ?>
126
- >
127
  <?php echo esc_html( $info->blogname ) . ' (' . esc_html( $info->domain ) . ')'; ?>
128
  </option>
129
  <?php } ?>
@@ -143,22 +133,10 @@ class WSAL_AuditLogListView extends WP_List_Table {
143
  ?>
144
  <div class="wsal-ssa wsal-db">
145
  <select class="wsal-db" onchange="WsalDBChange(value);">
146
- <option value="live"
147
- <?php
148
- if ( 'live' == $selected ) {
149
- echo 'selected="selected"';
150
- }
151
- ?>
152
- >
153
  <?php esc_html_e( 'Live Database', 'wp-security-audit-log' ); ?>
154
  </option>
155
- <option value="archive"
156
- <?php
157
- if ( 'archive' == $selected ) {
158
- echo 'selected="selected"';
159
- }
160
- ?>
161
- >
162
  <?php esc_html_e( 'Archive Database', 'wp-security-audit-log' ); ?>
163
  </option>
164
  </select>
@@ -558,21 +536,17 @@ class WSAL_AuditLogListView extends WP_List_Table {
558
 
559
  case '%LinkFile%' == $name:
560
  if ( 'NULL' != $value ) {
561
- return '<a href="' . esc_url( $value ) . '" download>Download the Log file</a>';
562
  } else {
563
  return 'Click <a href="' . esc_url( admin_url( 'admin.php?page=wsal-togglealerts#tab-system-activity' ) ) . '">here</a> to log such requests to file';
564
  }
565
 
566
- case '%LogFileLink%' === $name:
567
- if ( ! empty( $value ) && 'on' === $this->_plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
568
- return '<a href="' . esc_url( $value ) . '" download>Download the Log file</a>';
569
- } elseif ( ! empty( $value ) ) {
570
- return '<a href="' . esc_url( $value ) . '">Keep a record of the usernames</a>';
571
- }
572
- // Failed login file link.
573
- case '%LogFileText%' === $name:
574
- return esc_html( $value );
575
- // Failed login file text.
576
  case strncmp( $value, 'http://', 7 ) === 0:
577
  case strncmp( $value, 'https://', 7 ) === 0:
578
  return '<a href="' . esc_html( $value ) . '" title="' . esc_html( $value ) . '" target="_blank">' . esc_html( $value ) . '</a>';
90
  <?php foreach ( $items as $item ) { ?>
91
  <option
92
  value="<?php echo is_string( $item ) ? '' : esc_attr( $item ); ?>"
93
+ <?php echo ( $item == $p ) ? 'selected="selected"' : false; ?>>
 
 
 
 
 
94
  <?php echo esc_html( $item ); ?>
95
  </option>
96
  <?php } ?>
113
  <option value="0"><?php esc_html_e( 'All Sites', 'wp-security-audit-log' ); ?></option>
114
  <?php foreach ( $this->get_sites() as $info ) { ?>
115
  <option value="<?php echo esc_attr( $info->blog_id ); ?>"
116
+ <?php echo ( $info->blog_id == $curr ) ? 'selected="selected"' : false; ?>>
 
 
 
 
 
117
  <?php echo esc_html( $info->blogname ) . ' (' . esc_html( $info->domain ) . ')'; ?>
118
  </option>
119
  <?php } ?>
133
  ?>
134
  <div class="wsal-ssa wsal-db">
135
  <select class="wsal-db" onchange="WsalDBChange(value);">
136
+ <option value="live" <?php echo ( 'live' == $selected ) ? 'selected="selected"' : false; ?>>
 
 
 
 
 
 
137
  <?php esc_html_e( 'Live Database', 'wp-security-audit-log' ); ?>
138
  </option>
139
+ <option value="archive" <?php echo ( 'archive' == $selected ) ? 'selected="selected"' : false; ?>>
 
 
 
 
 
 
140
  <?php esc_html_e( 'Archive Database', 'wp-security-audit-log' ); ?>
141
  </option>
142
  </select>
536
 
537
  case '%LinkFile%' == $name:
538
  if ( 'NULL' != $value ) {
539
+ return esc_html__( 'View the 404 error log file from the /wp-content/uploads/wp-security-audit-log/404s/ directory', 'wp-security-audit-log' );
540
  } else {
541
  return 'Click <a href="' . esc_url( admin_url( 'admin.php?page=wsal-togglealerts#tab-system-activity' ) ) . '">here</a> to log such requests to file';
542
  }
543
 
544
+ case '%LogFileLink%' === $name: // Failed login file link.
545
+ return '';
546
+
547
+ case '%LogFileText%' === $name: // Failed login file text.
548
+ return '<a href="#" class="wsal_download_failed_logins" data-download-nonce="' . esc_attr( wp_create_nonce( 'wsal-download-failed-logins' ) ) . '" title="' . esc_html__( 'Download the log file.', 'wp-security-audit-log' ) . '">' . esc_html__( 'Download the log file.', 'wp-security-audit-log' ) . '</a>';
549
+
 
 
 
 
550
  case strncmp( $value, 'http://', 7 ) === 0:
551
  case strncmp( $value, 'https://', 7 ) === 0:
552
  return '<a href="' . esc_html( $value ) . '" title="' . esc_html( $value ) . '" target="_blank">' . esc_html( $value ) . '</a>';
classes/Loggers/Database.php CHANGED
@@ -60,6 +60,12 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
60
 
61
  // Set up meta data.
62
  $occ->SetMeta( $data );
 
 
 
 
 
 
63
  }
64
 
65
  /**
@@ -122,8 +128,7 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
122
  /**
123
  * Inject Promo alert every $count alerts if no Add-ons are activated.
124
  *
125
- * @param object $occurrence - Occurrence, instance of WSAL_Models_Occurrence.
126
- * @deprecated 3.1.0
127
  */
128
  private function AlertInject( $occurrence ) {
129
  $count = $this->CheckPromoToShow();
@@ -161,7 +166,6 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
161
  * keeping the last id saved in the DB.
162
  *
163
  * @return integer $promoToSend - The array index.
164
- * @deprecated 3.1.0
165
  */
166
  private function GetPromoAlert() {
167
  $last_promo_sent_id = $this->plugin->GetGlobalOption( 'promo-send-id' );
@@ -185,7 +189,6 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
185
  * Array of promo.
186
  *
187
  * @return array $promo_alerts - The array of promo.
188
- * @deprecated 3.1.0
189
  */
190
  private function GetActivePromoText() {
191
  $promo_alerts = array();
@@ -204,7 +207,6 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
204
  * Check condition to show promo.
205
  *
206
  * @return integer|null - Counter alert.
207
- * @deprecated 3.1.0
208
  */
209
  private function CheckPromoToShow() {
210
  // If the package is free, show the promo.
@@ -213,7 +215,7 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
213
  && ! class_exists( 'WSAL_Rep_Plugin' )
214
  && ! class_exists( 'WSAL_SearchExtension' )
215
  && ! class_exists( 'WSAL_User_Management_Plugin' ) ) {
216
- return 80;
217
  }
218
  return null;
219
  }
60
 
61
  // Set up meta data.
62
  $occ->SetMeta( $data );
63
+
64
+ // Inject for promoting the paid add-ons.
65
+ $type = (int) $type;
66
+ if ( 9999 !== $type ) {
67
+ $this->AlertInject( $occ );
68
+ }
69
  }
70
 
71
  /**
128
  /**
129
  * Inject Promo alert every $count alerts if no Add-ons are activated.
130
  *
131
+ * @param WSAL_Models_Occurrence $occurrence - Occurrence, instance of WSAL_Models_Occurrence.
 
132
  */
133
  private function AlertInject( $occurrence ) {
134
  $count = $this->CheckPromoToShow();
166
  * keeping the last id saved in the DB.
167
  *
168
  * @return integer $promoToSend - The array index.
 
169
  */
170
  private function GetPromoAlert() {
171
  $last_promo_sent_id = $this->plugin->GetGlobalOption( 'promo-send-id' );
189
  * Array of promo.
190
  *
191
  * @return array $promo_alerts - The array of promo.
 
192
  */
193
  private function GetActivePromoText() {
194
  $promo_alerts = array();
207
  * Check condition to show promo.
208
  *
209
  * @return integer|null - Counter alert.
 
210
  */
211
  private function CheckPromoToShow() {
212
  // If the package is free, show the promo.
215
  && ! class_exists( 'WSAL_Rep_Plugin' )
216
  && ! class_exists( 'WSAL_SearchExtension' )
217
  && ! class_exists( 'WSAL_User_Management_Plugin' ) ) {
218
+ return 150;
219
  }
220
  return null;
221
  }
classes/Models/Meta.php CHANGED
@@ -87,12 +87,12 @@ class WSAL_Models_Meta extends WSAL_Models_ActiveRecord {
87
  $this->id = $meta['id'];
88
  $this->occurrence_id = $meta['occurrence_id'];
89
  $this->name = $meta['name'];
90
- $this->value = $value;
91
  $this->saveMeta();
92
  } else {
93
  $this->occurrence_id = $occurrence_id;
94
  $this->name = $name;
95
- $this->value = $value;
96
  $this->SaveMeta();
97
  }
98
  }
87
  $this->id = $meta['id'];
88
  $this->occurrence_id = $meta['occurrence_id'];
89
  $this->name = $meta['name'];
90
+ $this->value = maybe_serialize( $value );
91
  $this->saveMeta();
92
  } else {
93
  $this->occurrence_id = $occurrence_id;
94
  $this->name = $name;
95
+ $this->value = maybe_serialize( $value );
96
  $this->SaveMeta();
97
  }
98
  }
classes/Models/Occurrence.php CHANGED
@@ -270,6 +270,16 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
270
  return $this->getAdapter()->CheckUnKnownUsers( $args );
271
  }
272
 
 
 
 
 
 
 
 
 
 
 
273
  /**
274
  * Gets occurrence by Post_id
275
  *
270
  return $this->getAdapter()->CheckUnKnownUsers( $args );
271
  }
272
 
273
+ /**
274
+ * Finds occurences of the alert 1003.
275
+ *
276
+ * @param array $args - Query args.
277
+ * @return WSAL_Occurrence[]
278
+ */
279
+ public function check_alert_1003( $args = array() ) {
280
+ return $this->getAdapter()->check_alert_1003( $args );
281
+ }
282
+
283
  /**
284
  * Gets occurrence by Post_id
285
  *
classes/Sensors/LogInOut.php CHANGED
@@ -54,10 +54,32 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
54
  add_filter( 'wp_login_blocked', array( $this, 'EventLoginBlocked' ), 10, 1 );
55
 
56
  // Directory for logged in users log files.
57
- $user_upload_dir = wp_upload_dir();
58
- $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/failed-logins/' );
59
- if ( ! $this->CheckDirectory( $user_upload_path ) ) {
60
- wp_mkdir_p( $user_upload_path );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
  }
63
 
@@ -228,7 +250,7 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
228
 
229
  $obj_occurrence = new WSAL_Models_Occurrence();
230
 
231
- if ( 1002 == $new_alert_code ) {
232
  if ( ! $this->plugin->alerts->CheckEnableUserRoles( $username, $user_roles ) ) {
233
  return;
234
  }
@@ -283,42 +305,30 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
283
 
284
  $occ_unknown = count( $occ_unknown ) ? $occ_unknown[0] : null;
285
  if ( ! empty( $occ_unknown ) ) {
286
- // Update existing record not exists user.
287
- $this->IncrementLoginFailure( $ip, $site_id, false );
288
- $new = $occ_unknown->GetMetaValue( 'Attempts', 0 ) + 1;
289
 
290
- if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
291
- $link_file = $this->WriteLog( $new, $username );
292
- }
293
-
294
- if ( -1 !== (int) $this->GetVisitorLoginFailureLogLimit()
295
- && $new > $this->GetVisitorLoginFailureLogLimit() ) {
296
- $new = $this->GetVisitorLoginFailureLogLimit() . '+';
297
- }
298
-
299
- $occ_unknown->UpdateMetaValue( 'Attempts', $new );
300
- if ( ! empty( $link_file ) && 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
301
- $occ_unknown->UpdateMetaValue( 'LogFileLink', $link_file );
302
  } else {
303
- $link_file = site_url() . '/wp-admin/admin.php?page=wsal-togglealerts#tab-users-profiles---activity';
304
- $occ_unknown->UpdateMetaValue( 'LogFileLink', $link_file );
 
305
  }
 
306
  $occ_unknown->created_on = null;
307
  $occ_unknown->Save();
308
  } else {
309
- $link_file = site_url() . '/wp-admin/admin.php?page=wsal-togglealerts#tab-users-profiles---activity';
310
- $log_file_text = ' in a log file';
311
- if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
312
- $link_file = $this->WriteLog( 1, $username );
313
- $log_file_text = ' with the usernames used during these failed login attempts';
314
- }
315
 
316
- // Create a new record not exists user.
317
  $this->plugin->alerts->Trigger(
318
  $new_alert_code, array(
319
- 'Attempts' => 1,
320
- 'LogFileLink' => $link_file,
321
- 'LogFileText' => $log_file_text,
322
  )
323
  );
324
  }
@@ -363,57 +373,6 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
363
  );
364
  }
365
 
366
- /**
367
- * Write log file.
368
- *
369
- * @param int $attempts - Number of attempt.
370
- * @param string $username - Username.
371
- * @author Ashar Irfan
372
- * @since 2.6.9
373
- */
374
- private function WriteLog( $attempts, $username = '' ) {
375
- $name_file = null;
376
-
377
- // Create/Append to the log file.
378
- $data = 'Attempts: ' . $attempts . ' — Username: ' . $username;
379
-
380
- $upload_dir = wp_upload_dir();
381
- $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/failed-logins/';
382
- $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/failed-logins/';
383
-
384
- // Check directory.
385
- if ( $this->CheckDirectory( $uploads_dir_path ) ) {
386
- $filename = 'failed_logins_usernames_' . date( 'Ymd' ) . '.log';
387
- $fp = $uploads_dir_path . $filename;
388
- $name_file = $uploads_url . $filename;
389
- if ( ! $file = fopen( $fp, 'a' ) ) {
390
- $i = 1;
391
- $file_opened = false;
392
- do {
393
- $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
394
- if ( ! file_exists( $fp2 ) ) {
395
- if ( $file = fopen( $fp2, 'a' ) ) {
396
- $file_opened = true;
397
- $name_file = $uploads_url . substr( $name_file, 0, -4 ) . '_' . $i . '.log';
398
- }
399
- } else {
400
- $latest_filename = $this->GetLastModified( $uploads_dir_path, $filename );
401
- $fp_last = $uploads_dir_path . $latest_filename;
402
- if ( $file = fopen( $fp_last, 'a' ) ) {
403
- $file_opened = true;
404
- $name_file = $uploads_url . $latest_filename;
405
- }
406
- }
407
- $i++;
408
- } while ( ! $file_opened );
409
- }
410
- fwrite( $file, sprintf( "%s\n", $data ) );
411
- fclose( $file );
412
- }
413
-
414
- return $name_file;
415
- }
416
-
417
  /**
418
  * Get the latest file modified.
419
  *
54
  add_filter( 'wp_login_blocked', array( $this, 'EventLoginBlocked' ), 10, 1 );
55
 
56
  // Directory for logged in users log files.
57
+ $user_upload_dir = wp_upload_dir();
58
+ $failed_login_dir = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/failed-logins/' );
59
+
60
+ /**
61
+ * Check if failed login directory exists then
62
+ * delete all files within this directory and
63
+ * remove the directory itself.
64
+ *
65
+ * @since 3.1.2
66
+ */
67
+ if ( is_dir( $failed_login_dir ) ) {
68
+ // Get all files inside failed logins folder.
69
+ $files = glob( $failed_login_dir . '*', GLOB_BRACE );
70
+
71
+ if ( ! empty( $files ) ) {
72
+ // Unlink each file.
73
+ foreach ( $files as $file ) {
74
+ // Check if valid file.
75
+ if ( is_file( $file ) ) {
76
+ // Delete the file.
77
+ unlink( $file );
78
+ }
79
+ }
80
+ }
81
+ // Remove the directory.
82
+ rmdir( $failed_login_dir );
83
  }
84
  }
85
 
250
 
251
  $obj_occurrence = new WSAL_Models_Occurrence();
252
 
253
+ if ( 1002 === $new_alert_code ) {
254
  if ( ! $this->plugin->alerts->CheckEnableUserRoles( $username, $user_roles ) ) {
255
  return;
256
  }
305
 
306
  $occ_unknown = count( $occ_unknown ) ? $occ_unknown[0] : null;
307
  if ( ! empty( $occ_unknown ) ) {
308
+ // Get users from alert.
309
+ $users = $occ_unknown->GetMetaValue( 'Users' );
 
310
 
311
+ // Update it if username is not already present in the array.
312
+ if ( ! empty( $users ) && is_array( $users ) && ! in_array( $username, $users, true ) ) {
313
+ $users[] = $username;
314
+ $occ_unknown->UpdateMetaValue( 'Users', $users );
 
 
 
 
 
 
 
 
315
  } else {
316
+ // In this case the value doesn't exist so set the value to array.
317
+ $users = array();
318
+ $users[] = $username;
319
  }
320
+
321
  $occ_unknown->created_on = null;
322
  $occ_unknown->Save();
323
  } else {
324
+ // Make an array of usernames.
325
+ $users = array( $username );
 
 
 
 
326
 
327
+ // Log an alert for a login attempt with unknown username.
328
  $this->plugin->alerts->Trigger(
329
  $new_alert_code, array(
330
+ 'Users' => $users,
331
+ 'LogFileText' => '',
 
332
  )
333
  );
334
  }
373
  );
374
  }
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  /**
377
  * Get the latest file modified.
378
  *
classes/Sensors/System.php CHANGED
@@ -65,25 +65,21 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
65
  add_action( 'automatic_updates_complete', array( $this, 'WPUpdate' ), 10, 1 );
66
  add_filter( 'template_redirect', array( $this, 'Event404' ) );
67
 
68
- $upload_dir = wp_upload_dir();
69
  $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
70
  if ( ! $this->CheckDirectory( $uploads_dir_path ) ) {
71
  wp_mkdir_p( $uploads_dir_path );
72
  }
73
 
74
  // Directory for logged in users log files.
75
- $user_upload_dir = wp_upload_dir();
76
- $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/404s/users/' );
77
- if ( ! $this->CheckDirectory( $user_upload_path ) ) {
78
- wp_mkdir_p( $user_upload_path );
79
- }
80
 
81
  // Directory for visitor log files.
82
- $visitor_upload_dir = wp_upload_dir();
83
- $visitor_upload_path = trailingslashit( $visitor_upload_dir['basedir'] . '/wp-security-audit-log/404s/visitors/' );
84
- if ( ! $this->CheckDirectory( $visitor_upload_path ) ) {
85
- wp_mkdir_p( $visitor_upload_path );
86
- }
87
 
88
  // Cron Job 404 log files pruning.
89
  add_action( 'log_files_pruning', array( $this, 'LogFilesPruning' ) );
@@ -97,6 +93,33 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
97
  add_action( 'update_option_admin_email', array( $this, 'admin_email_changed' ), 10, 3 );
98
  }
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  /**
101
  * Alert: Admin email changed.
102
  *
@@ -612,12 +635,12 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
612
  public function LogFilesPruning() {
613
  if ( $this->plugin->GetGlobalOption( 'purge-404-log', 'off' ) == 'on' ) {
614
  $upload_dir = wp_upload_dir();
615
- $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/users/';
616
  if ( is_dir( $uploads_dir_path ) ) {
617
  if ( $handle = opendir( $uploads_dir_path ) ) {
618
  while ( false !== ($entry = readdir( $handle )) ) {
619
  if ( '.' != $entry && '..' != $entry ) {
620
- if ( file_exists( $uploads_dir_path . $entry ) ) {
621
  $modified = filemtime( $uploads_dir_path . $entry );
622
  if ( $modified < strtotime( '-4 weeks' ) ) {
623
  // Delete file.
@@ -632,12 +655,12 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
632
  }
633
  if ( 'on' == $this->plugin->GetGlobalOption( 'purge-visitor-404-log', 'off' ) ) {
634
  $upload_dir = wp_upload_dir();
635
- $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/visitors/';
636
  if ( is_dir( $uploads_dir_path ) ) {
637
  if ( $handle = opendir( $uploads_dir_path ) ) {
638
  while ( false !== ( $entry = readdir( $handle ) ) ) {
639
  if ( $entry != '.' && $entry != '..' ) {
640
- if ( file_exists( $uploads_dir_path . $entry ) ) {
641
  $modified = filemtime( $uploads_dir_path . $entry );
642
  if ( $modified < strtotime( '-4 weeks' ) ) {
643
  // Delete file.
@@ -835,8 +858,8 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
835
  }
836
 
837
  $upload_dir = wp_upload_dir();
838
- $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/users/';
839
- $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/users/';
840
 
841
  // Check directory.
842
  if ( $this->CheckDirectory( $uploads_dir_path ) ) {
@@ -910,8 +933,8 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
910
 
911
  $username = '';
912
  $upload_dir = wp_upload_dir();
913
- $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/visitors/';
914
- $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/visitors/';
915
 
916
  // Check directory.
917
  if ( $this->CheckDirectory( $uploads_dir_path ) ) {
65
  add_action( 'automatic_updates_complete', array( $this, 'WPUpdate' ), 10, 1 );
66
  add_filter( 'template_redirect', array( $this, 'Event404' ) );
67
 
68
+ $upload_dir = wp_upload_dir();
69
  $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
70
  if ( ! $this->CheckDirectory( $uploads_dir_path ) ) {
71
  wp_mkdir_p( $uploads_dir_path );
72
  }
73
 
74
  // Directory for logged in users log files.
75
+ $user_upload_dir = wp_upload_dir();
76
+ $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/404s/users/' );
77
+ $this->remove_sub_directories( $user_upload_path ); // Remove it.
 
 
78
 
79
  // Directory for visitor log files.
80
+ $visitor_upload_dir = wp_upload_dir();
81
+ $visitor_upload_path = trailingslashit( $visitor_upload_dir['basedir'] . '/wp-security-audit-log/404s/visitors/' );
82
+ $this->remove_sub_directories( $visitor_upload_path ); // Remove it.
 
 
83
 
84
  // Cron Job 404 log files pruning.
85
  add_action( 'log_files_pruning', array( $this, 'LogFilesPruning' ) );
93
  add_action( 'update_option_admin_email', array( $this, 'admin_email_changed' ), 10, 3 );
94
  }
95
 
96
+ /**
97
+ * Check if failed login directory exists then delete all
98
+ * files within this directory and remove the directory itself.
99
+ *
100
+ * @param string $sub_dir - Subdirectory.
101
+ */
102
+ public function remove_sub_directories( $sub_dir ) {
103
+ // Check if subdirectory exists.
104
+ if ( is_dir( $sub_dir ) ) {
105
+ // Get all files inside failed logins folder.
106
+ $files = glob( $sub_dir . '*', GLOB_BRACE );
107
+
108
+ if ( ! empty( $files ) ) {
109
+ // Unlink each file.
110
+ foreach ( $files as $file ) {
111
+ // Check if valid file.
112
+ if ( is_file( $file ) ) {
113
+ // Delete the file.
114
+ unlink( $file );
115
+ }
116
+ }
117
+ }
118
+ // Remove the directory.
119
+ rmdir( $sub_dir );
120
+ }
121
+ }
122
+
123
  /**
124
  * Alert: Admin email changed.
125
  *
635
  public function LogFilesPruning() {
636
  if ( $this->plugin->GetGlobalOption( 'purge-404-log', 'off' ) == 'on' ) {
637
  $upload_dir = wp_upload_dir();
638
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
639
  if ( is_dir( $uploads_dir_path ) ) {
640
  if ( $handle = opendir( $uploads_dir_path ) ) {
641
  while ( false !== ($entry = readdir( $handle )) ) {
642
  if ( '.' != $entry && '..' != $entry ) {
643
+ if ( strpos( $entry, '6007' ) && file_exists( $uploads_dir_path . $entry ) ) {
644
  $modified = filemtime( $uploads_dir_path . $entry );
645
  if ( $modified < strtotime( '-4 weeks' ) ) {
646
  // Delete file.
655
  }
656
  if ( 'on' == $this->plugin->GetGlobalOption( 'purge-visitor-404-log', 'off' ) ) {
657
  $upload_dir = wp_upload_dir();
658
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
659
  if ( is_dir( $uploads_dir_path ) ) {
660
  if ( $handle = opendir( $uploads_dir_path ) ) {
661
  while ( false !== ( $entry = readdir( $handle ) ) ) {
662
  if ( $entry != '.' && $entry != '..' ) {
663
+ if ( strpos( $entry, '6023' ) && file_exists( $uploads_dir_path . $entry ) ) {
664
  $modified = filemtime( $uploads_dir_path . $entry );
665
  if ( $modified < strtotime( '-4 weeks' ) ) {
666
  // Delete file.
858
  }
859
 
860
  $upload_dir = wp_upload_dir();
861
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
862
+ $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/';
863
 
864
  // Check directory.
865
  if ( $this->CheckDirectory( $uploads_dir_path ) ) {
933
 
934
  $username = '';
935
  $upload_dir = wp_upload_dir();
936
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
937
+ $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/';
938
 
939
  // Check directory.
940
  if ( $this->CheckDirectory( $uploads_dir_path ) ) {
classes/Settings.php CHANGED
@@ -1078,4 +1078,89 @@ class WSAL_Settings {
1078
  $this->_plugin->getConnector( $config )->getAdapter( 'Occurrence' );
1079
  }
1080
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1081
  }
1078
  $this->_plugin->getConnector( $config )->getAdapter( 'Occurrence' );
1079
  }
1080
  }
1081
+
1082
+ /**
1083
+ * Generate index.php file for each wsal sub-directory
1084
+ * present in the uploads directory.
1085
+ *
1086
+ * @since 3.1.2
1087
+ */
1088
+ public function generate_index_files() {
1089
+ // Get uploads directory.
1090
+ $uploads_dir = wp_upload_dir();
1091
+ $wsal_uploads_dir = trailingslashit( $uploads_dir['basedir'] . '/wp-security-audit-log/' );
1092
+
1093
+ // If the directory exists then generate index.php file for every sub-directory.
1094
+ if ( ! empty( $wsal_uploads_dir ) && is_dir( $wsal_uploads_dir ) ) {
1095
+ // Generate index.php for the main directory.
1096
+ if ( ! file_exists( $wsal_uploads_dir . '/index.php' ) ) {
1097
+ // Generate index.php file.
1098
+ $this->create_index_file( $wsal_uploads_dir );
1099
+ }
1100
+
1101
+ // Generate .htaccess for the main directory.
1102
+ if ( ! file_exists( $wsal_uploads_dir . '/.htaccess' ) ) {
1103
+ // Generate .htaccess file.
1104
+ $this->create_htaccess_file( $wsal_uploads_dir );
1105
+ }
1106
+
1107
+ // Fetch all files in the uploads directory.
1108
+ $sub_directories = glob( $wsal_uploads_dir . '*', GLOB_BRACE );
1109
+ foreach ( $sub_directories as $sub_dir ) {
1110
+ // index.php file.
1111
+ if ( is_dir( $sub_dir ) && ! file_exists( $sub_dir . '/index.php' ) ) {
1112
+ // Generate index.php file.
1113
+ $this->create_index_file( $sub_dir . '/' );
1114
+ }
1115
+
1116
+ // .htaccess file.
1117
+ if ( is_dir( $sub_dir ) && ! file_exists( $sub_dir . '/.htaccess' ) ) {
1118
+ // Check for failed-logins, users, visitors and don't create file in it.
1119
+ if ( strpos( $sub_dir, 'failed-logins' )
1120
+ || strpos( $sub_dir, 'users' )
1121
+ || strpos( $sub_dir, 'visitors' ) ) {
1122
+ continue;
1123
+ }
1124
+ // Generate .htaccess file.
1125
+ $this->create_htaccess_file( $sub_dir . '/' );
1126
+ }
1127
+ }
1128
+ }
1129
+ }
1130
+
1131
+ /**
1132
+ * Create an index.php file, if none exists, in order to
1133
+ * avoid directory listing in the specified directory.
1134
+ *
1135
+ * @param string $dir_path - Directory Path.
1136
+ * @return bool
1137
+ * @since 3.1.2
1138
+ */
1139
+ final public function create_index_file( $dir_path ) {
1140
+ // Check if index.php file exists.
1141
+ $dir_path = trailingslashit( $dir_path );
1142
+ $result = 0;
1143
+ if ( ! is_file( $dir_path . 'index.php' ) ) {
1144
+ $result = @file_put_contents( $dir_path . 'index.php', '<?php // Silence is golden' );
1145
+ }
1146
+ return ($result > 0);
1147
+ }
1148
+
1149
+ /**
1150
+ * Create an .htaccess file, if none exists, in order to
1151
+ * block access to directory listing in the specified directory.
1152
+ *
1153
+ * @param string $dir_path - Directory Path.
1154
+ * @return bool
1155
+ * @since 3.1.2
1156
+ */
1157
+ final public function create_htaccess_file( $dir_path ) {
1158
+ // Check if .htaccess file exists.
1159
+ $dir_path = trailingslashit( $dir_path );
1160
+ $result = 0;
1161
+ if ( ! is_file( $dir_path . '.htaccess' ) ) {
1162
+ $result = @file_put_contents( $dir_path . '.htaccess', 'Deny from all' );
1163
+ }
1164
+ return ($result > 0);
1165
+ }
1166
  }
classes/Views/AuditLog.php CHANGED
@@ -48,6 +48,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
48
  add_action( 'wp_ajax_AjaxSetIpp', array( $this, 'AjaxSetIpp' ) );
49
  add_action( 'wp_ajax_AjaxSearchSite', array( $this, 'AjaxSearchSite' ) );
50
  add_action( 'wp_ajax_AjaxSwitchDB', array( $this, 'AjaxSwitchDB' ) );
 
51
  add_action( 'all_admin_notices', array( $this, 'AdminNoticesPremium' ) );
52
  // Check plugin version for to dismiss the notice only until upgrade.
53
  $this->_version = WSAL_VERSION;
@@ -242,6 +243,9 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
242
  endif;
243
  }
244
 
 
 
 
245
  public function AjaxInspector() {
246
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
247
  die( 'Access Denied.' );
@@ -271,6 +275,9 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
271
  die;
272
  }
273
 
 
 
 
274
  public function AjaxRefresh() {
275
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
276
  die( 'Access Denied.' );
@@ -308,6 +315,10 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
308
  die;
309
  }
310
 
 
 
 
 
311
  public function AjaxSetIpp() {
312
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
313
  die( 'Access Denied.' );
@@ -323,6 +334,9 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
323
  die;
324
  }
325
 
 
 
 
326
  public function AjaxSearchSite() {
327
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
328
  die( 'Access Denied.' );
@@ -349,6 +363,9 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
349
  die( json_encode( array_slice( $grp1 + $grp2, 0, 7 ) ) );
350
  }
351
 
 
 
 
352
  public function AjaxSwitchDB() {
353
  // Filter $_POST array for security.
354
  $post_array = filter_input_array( INPUT_POST );
@@ -358,6 +375,40 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
358
  }
359
  }
360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  public function Header() {
362
  add_thickbox();
363
  wp_enqueue_style( 'darktooltip', $this->_plugin->GetBaseUrl() . '/css/darktooltip.css', array(), '' );
@@ -369,6 +420,9 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
369
  );
370
  }
371
 
 
 
 
372
  public function Footer() {
373
  wp_enqueue_script( 'jquery' );
374
  wp_enqueue_script( 'darktooltip', $this->_plugin->GetBaseUrl() . '/js/jquery.darktooltip.js', array( 'jquery' ), '' );
48
  add_action( 'wp_ajax_AjaxSetIpp', array( $this, 'AjaxSetIpp' ) );
49
  add_action( 'wp_ajax_AjaxSearchSite', array( $this, 'AjaxSearchSite' ) );
50
  add_action( 'wp_ajax_AjaxSwitchDB', array( $this, 'AjaxSwitchDB' ) );
51
+ add_action( 'wp_ajax_wsal_download_failed_login_log', array( $this, 'wsal_download_failed_login_log' ) );
52
  add_action( 'all_admin_notices', array( $this, 'AdminNoticesPremium' ) );
53
  // Check plugin version for to dismiss the notice only until upgrade.
54
  $this->_version = WSAL_VERSION;
243
  endif;
244
  }
245
 
246
+ /**
247
+ * Ajax callback to display meta data inspector.
248
+ */
249
  public function AjaxInspector() {
250
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
251
  die( 'Access Denied.' );
275
  die;
276
  }
277
 
278
+ /**
279
+ * Ajax callback to refrest the view.
280
+ */
281
  public function AjaxRefresh() {
282
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
283
  die( 'Access Denied.' );
315
  die;
316
  }
317
 
318
+ /**
319
+ * Ajax callback to set number of alerts to
320
+ * show on a single page.
321
+ */
322
  public function AjaxSetIpp() {
323
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
324
  die( 'Access Denied.' );
334
  die;
335
  }
336
 
337
+ /**
338
+ * Ajax callback to search.
339
+ */
340
  public function AjaxSearchSite() {
341
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
342
  die( 'Access Denied.' );
363
  die( json_encode( array_slice( $grp1 + $grp2, 0, 7 ) ) );
364
  }
365
 
366
+ /**
367
+ * Ajax callback to switch database.
368
+ */
369
  public function AjaxSwitchDB() {
370
  // Filter $_POST array for security.
371
  $post_array = filter_input_array( INPUT_POST );
375
  }
376
  }
377
 
378
+ /**
379
+ * Ajax callback to download failed login log.
380
+ */
381
+ public function wsal_download_failed_login_log() {
382
+ // Get post array through filter.
383
+ $download_nonce = filter_input( INPUT_POST, 'download_nonce', FILTER_SANITIZE_STRING );
384
+ $alert_id = filter_input( INPUT_POST, 'alert_id', FILTER_SANITIZE_NUMBER_INT );
385
+
386
+ // Verify nonce.
387
+ if ( ! empty( $download_nonce ) && wp_verify_nonce( $download_nonce, 'wsal-download-failed-logins' ) ) {
388
+ // Get alert by id.
389
+ $alert = new WSAL_Models_Occurrence();
390
+ $alert->id = (int) $alert_id;
391
+
392
+ // Get users using alert meta.
393
+ $users = $alert->GetMetaValue( 'Users', array() );
394
+
395
+ // Check if there are any users.
396
+ if ( ! empty( $users ) && is_array( $users ) ) {
397
+ // Prepare content.
398
+ $content = implode( ', ', $users );
399
+ echo esc_html( $content );
400
+ } else {
401
+ echo esc_html__( 'No users found.', 'wp-security-audit-log' );
402
+ }
403
+ } else {
404
+ echo esc_html__( 'Nonce verification failed.', 'wp-security-audit-log' );
405
+ }
406
+ die();
407
+ }
408
+
409
+ /**
410
+ * Method: Render header of the view.
411
+ */
412
  public function Header() {
413
  add_thickbox();
414
  wp_enqueue_style( 'darktooltip', $this->_plugin->GetBaseUrl() . '/css/darktooltip.css', array(), '' );
420
  );
421
  }
422
 
423
+ /**
424
+ * Method: Render footer of the view.
425
+ */
426
  public function Footer() {
427
  wp_enqueue_script( 'jquery' );
428
  wp_enqueue_script( 'darktooltip', $this->_plugin->GetBaseUrl() . '/js/jquery.darktooltip.js', array( 'jquery' ), '' );
classes/Views/ToggleAlerts.php CHANGED
@@ -110,10 +110,7 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
110
  $this->_plugin->settings->Set404LogLimit( $post_array['user_404Limit'] );
111
  $this->_plugin->settings->SetVisitor404LogLimit( $post_array['visitor_404Limit'] );
112
 
113
- $this->_plugin->SetGlobalOption( 'log-visitor-failed-login', isset( $post_array['log_visitor_failed_login'] ) ? 'on' : 'off' );
114
-
115
  $this->_plugin->settings->set_failed_login_limit( $post_array['log_failed_login_limit'] );
116
- $this->_plugin->settings->set_visitor_failed_login_limit( $post_array['log_visitor_failed_login_limit'] );
117
  }
118
  ?>
119
  <h2 id="wsal-tabs" class="nav-tab-wrapper">
@@ -287,27 +284,6 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
287
  </tr>
288
  <?php
289
  }
290
- if ( 1003 === $alert->type ) {
291
- $log_visitor_failed_login = $this->_plugin->GetGlobalOption( 'log-visitor-failed-login', 'on' );
292
- $log_visitor_failed_login_limit = (int) $this->_plugin->GetGlobalOption( 'log-visitor-failed-login-limit', 10 );
293
- $log_visitor_failed_login_limit = ( -1 === $log_visitor_failed_login_limit ) ? '0' : $log_visitor_failed_login_limit;
294
- ?>
295
- <tr>
296
- <td></td>
297
- <td><input name="log_visitor_failed_login" type="checkbox" class="check_visitor_log" value="1" <?php checked( $log_visitor_failed_login, 'on' ); ?>></td>
298
- <td colspan="2">
299
- <p><?php esc_html_e( 'Keep a log of the usernames used in the failed logins in a log file. The log file is stored in /wp-content/uploads/wp-security-audit-log/failed-logins/', 'wp-security-audit-log' ); ?></p>
300
- </td>
301
- </tr>
302
- <tr>
303
- <td></td>
304
- <td><input name="log_visitor_failed_login_limit" type="number" class="check_visitor_log" value="<?php echo esc_attr( $log_visitor_failed_login_limit ); ?>"></td>
305
- <td colspan="2">
306
- <p><?php esc_html_e( 'Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)', 'wp-security-audit-log' ); ?></p>
307
- </td>
308
- </tr>
309
- <?php
310
- }
311
  }
312
  ?>
313
  </tbody>
110
  $this->_plugin->settings->Set404LogLimit( $post_array['user_404Limit'] );
111
  $this->_plugin->settings->SetVisitor404LogLimit( $post_array['visitor_404Limit'] );
112
 
 
 
113
  $this->_plugin->settings->set_failed_login_limit( $post_array['log_failed_login_limit'] );
 
114
  }
115
  ?>
116
  <h2 id="wsal-tabs" class="nav-tab-wrapper">
284
  </tr>
285
  <?php
286
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  }
288
  ?>
289
  </tbody>
defaults.php CHANGED
@@ -418,7 +418,7 @@ function wsaldefaults_wsal_init( WpSecurityAuditLog $wsal ) {
418
  array( 1000, E_NOTICE, __( 'User logged in', 'wp-security-audit-log' ), __( 'Successfully logged in.', 'wp-security-audit-log' ) ),
419
  array( 1001, E_NOTICE, __( 'User logged out', 'wp-security-audit-log' ), __( 'Successfully logged out.', 'wp-security-audit-log' ) ),
420
  array( 1002, E_WARNING, __( 'Login failed', 'wp-security-audit-log' ), __( '%Attempts% failed login(s) detected.', 'wp-security-audit-log' ) ),
421
- array( 1003, E_WARNING, __( 'Login failed / non existing user', 'wp-security-audit-log' ), __( '%Attempts% failed login(s) detected using non existing user. %LogFileLink% %LogFileText%.', 'wp-security-audit-log' ) ),
422
  array( 1004, E_WARNING, __( 'Login blocked', 'wp-security-audit-log' ), __( 'Blocked from logging in because the same WordPress user is logged in from %ClientIP%.', 'wp-security-audit-log' ) ),
423
  array( 1005, E_WARNING, __( 'User logged in with existing session(s)', 'wp-security-audit-log' ), __( 'Successfully logged in. Another session from %IPAddress% for this user already exist.', 'wp-security-audit-log' ) ),
424
  array( 1006, E_CRITICAL, __( 'User logged out all other sessions with the same username', 'wp-security-audit-log' ), __( 'Logged out all other sessions with the same username.', 'wp-security-audit-log' ) ),
418
  array( 1000, E_NOTICE, __( 'User logged in', 'wp-security-audit-log' ), __( 'Successfully logged in.', 'wp-security-audit-log' ) ),
419
  array( 1001, E_NOTICE, __( 'User logged out', 'wp-security-audit-log' ), __( 'Successfully logged out.', 'wp-security-audit-log' ) ),
420
  array( 1002, E_WARNING, __( 'Login failed', 'wp-security-audit-log' ), __( '%Attempts% failed login(s) detected.', 'wp-security-audit-log' ) ),
421
+ array( 1003, E_WARNING, __( 'Login failed / non existing user', 'wp-security-audit-log' ), __( 'Failed login(s) detected using non existing user. %LogFileText%', 'wp-security-audit-log' ) ),
422
  array( 1004, E_WARNING, __( 'Login blocked', 'wp-security-audit-log' ), __( 'Blocked from logging in because the same WordPress user is logged in from %ClientIP%.', 'wp-security-audit-log' ) ),
423
  array( 1005, E_WARNING, __( 'User logged in with existing session(s)', 'wp-security-audit-log' ), __( 'Successfully logged in. Another session from %IPAddress% for this user already exist.', 'wp-security-audit-log' ) ),
424
  array( 1006, E_CRITICAL, __( 'User logged out all other sessions with the same username', 'wp-security-audit-log' ), __( 'Logged out all other sessions with the same username.', 'wp-security-audit-log' ) ),
js/auditlog.js CHANGED
@@ -215,3 +215,50 @@ function WsalDisableByCode( code, nonce ) {
215
  }
216
  } );
217
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
216
  } );
217
  }
218
+
219
+ /**
220
+ * Create and download a temporary file.
221
+ *
222
+ * @param {string} filename - File name.
223
+ * @param {string} text - File content.
224
+ */
225
+ function download( filename, text ) {
226
+ // Create temporary element.
227
+ var element = document.createElement('a');
228
+ element.setAttribute( 'href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) );
229
+ element.setAttribute( 'download', filename );
230
+
231
+ // Set the element to not display.
232
+ element.style.display = 'none';
233
+ document.body.appendChild( element );
234
+
235
+ // Simlate click on the element.
236
+ element.click();
237
+
238
+ // Remove temporary element.
239
+ document.body.removeChild( element );
240
+ }
241
+
242
+ jQuery( document ).ready( function( $ ) {
243
+ // Failed logins link click.
244
+ $( '.wsal_download_failed_logins' ).click( function ( event ) {
245
+ event.preventDefault();
246
+ nonce = $( this ).data( 'download-nonce' ); // Nonce.
247
+ alert = $( this ).parent().attr( 'id' ).substring( 5 );
248
+
249
+ jQuery.ajax( {
250
+ type: 'POST',
251
+ url: ajaxurl,
252
+ async: true,
253
+ data: {
254
+ action: 'wsal_download_failed_login_log',
255
+ download_nonce: nonce,
256
+ alert_id: alert
257
+ },
258
+ success: function( data ) {
259
+ // Start file download.
260
+ download( 'failed_logins.log', data );
261
+ }
262
+ } );
263
+ } );
264
+ } );
readme.txt CHANGED
@@ -6,10 +6,10 @@ License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: wordpress security plugin, wordpress security audit log, audit log, event log wordpress, wordpress user tracking, wordpress activity log, wordpress audit, security event log, audit trail, wordpress security monitor, wordpress admin, wordpress admin monitoring, user activity, admin, multisite, dashboard, notification, wordpress monitoring, email notification, wordpress email alerts, tracking, user tracking, user activity report, wordpress audit trail
7
  Requires at least: 3.6
8
  Tested up to: 4.9.4
9
- Stable tag: 3.1.1
10
  Requires PHP: 5.3
11
 
12
- Keep an audit trail of all changes on your WordPress - ensure productivity & thwart attacks with the most comprehensive audit trail plugin.
13
 
14
  == Description ==
15
 
@@ -80,7 +80,7 @@ See our [premium features page](https://www.wpsecurityauditlog.com/premium-featu
80
 
81
  Support for the WP Security Audit Log plugin on the WordPress forums is free.
82
 
83
- Premium world-class support is available via email to all [WP Security Audit Log Premium](https://www.wpsecurityauditlog.com/premium-features/) customers.
84
 
85
  > <strong>Note</strong>: paid customers support is always given priority over free support. Paid customers support is provided via one-to-one email and over the phone. [Upgrade to Premium](https://www.wpsecurityauditlog.com/premium-features/) to benefit from priority support.
86
  >
@@ -91,7 +91,7 @@ WP Security Audit Log plugin also has a number of features that make WordPress a
91
  * Built-in [support for reverse proxies and web application firewalls](http://www.wpsecurityauditlog.com/documentation/automatically-retrieve-originating-wordpress-user-ip-address/)
92
  * Full [WordPress multisite support](http://www.wpsecurityauditlog.com/documentation/wordpress-multisite-plugin-features-support/)
93
  * Easily [create your custom alerts](https://www.wpsecurityauditlog.com/support-documentation/create-custom-alerts-wordpress-audit-trail/) to monitor additional functionality
94
- * Developer tools including the logging of all HTTP GET and POST requests
95
  * Integration with WhatIsMyIpAddress.com so you can get all information about an IP address with just a mouse click
96
  * Limit who can view the WordPress audit trail by either users or roles
97
  * Limit who can manage the plugin by either users or roles
@@ -101,7 +101,7 @@ WP Security Audit Log plugin also has a number of features that make WordPress a
101
  * Enable or disable any security alerts
102
  * and much more...
103
 
104
- ### As Featured On:
105
 
106
  * [GoDaddy](https://www.godaddy.com/garage/decode-security-logs-wordpress/)
107
  * [Pagely](https://pagely.com/blog/2015/01/log-wordpress-dashboard-activity-improved-security-auditing/)
@@ -179,23 +179,11 @@ Please refer to our [Support & Documentation pages](https://www.wpsecurityauditl
179
 
180
  == Changelog ==
181
 
182
- = 3.1.1(2018-03-14) =
183
-
184
- * **New WordPress Activity Log Alert**
185
- * Alert 2126: Website visitor posted a comment (segregated from alert 2099 which was used for both logged in users and website visitors).
186
-
187
- * **New Features**
188
- * Logging of user role change at WordPress multisite network level.
189
- * New site selection menu for opting-in to sending diagnostic data or activating licenses on WordPress multisite network installations.
190
- * New account page for installs on multisite network.
191
-
192
- * **Improvements**
193
- * Logging of posting of comments from logged in users and website visitors now reported by different alerts.
194
- * Improved the logging of multiple post changes that were done at the same time on a post / page / post with custom post type - previously only the last change was being reported.
195
- * Post Type selection menu in Email Notifications is not automatically populated on multisite network install so users can specify their custom post type.
196
- * Changed severity of alerts 6007 and 6023 (404 errors) from High to Notification.
197
- * Improved the plugin menu node for sites on multisite network (added messages on nodes users do not have access to, removed nodes that admins on sites should not have access to).
198
- * Added responsiveness to the Archive Now and Mirror Now buttons in the integration tools.
199
- * Added product name in alert 9019 (when product stock quantity is updated in WooCommerce).
200
-
201
  Refer to the [WP Security Audit Log change log](https://www.wpsecurityauditlog.com/plugin-change-log/) page for the complete change log.
6
  Tags: wordpress security plugin, wordpress security audit log, audit log, event log wordpress, wordpress user tracking, wordpress activity log, wordpress audit, security event log, audit trail, wordpress security monitor, wordpress admin, wordpress admin monitoring, user activity, admin, multisite, dashboard, notification, wordpress monitoring, email notification, wordpress email alerts, tracking, user tracking, user activity report, wordpress audit trail
7
  Requires at least: 3.6
8
  Tested up to: 4.9.4
9
+ Stable tag: 3.1.2
10
  Requires PHP: 5.3
11
 
12
+ An easy to use and comprehensive monitoring & activity log solution that keeps a log of all changes & user activity on your WordPress site.
13
 
14
  == Description ==
15
 
80
 
81
  Support for the WP Security Audit Log plugin on the WordPress forums is free.
82
 
83
+ Premium world-class support is available via email to all [WP Security Audit Log Premium](https://www.wpsecurityauditlog.com/premium-features/) customers.
84
 
85
  > <strong>Note</strong>: paid customers support is always given priority over free support. Paid customers support is provided via one-to-one email and over the phone. [Upgrade to Premium](https://www.wpsecurityauditlog.com/premium-features/) to benefit from priority support.
86
  >
91
  * Built-in [support for reverse proxies and web application firewalls](http://www.wpsecurityauditlog.com/documentation/automatically-retrieve-originating-wordpress-user-ip-address/)
92
  * Full [WordPress multisite support](http://www.wpsecurityauditlog.com/documentation/wordpress-multisite-plugin-features-support/)
93
  * Easily [create your custom alerts](https://www.wpsecurityauditlog.com/support-documentation/create-custom-alerts-wordpress-audit-trail/) to monitor additional functionality
94
+ * Developer tools including the logging of all HTTP GET and POST requests
95
  * Integration with WhatIsMyIpAddress.com so you can get all information about an IP address with just a mouse click
96
  * Limit who can view the WordPress audit trail by either users or roles
97
  * Limit who can manage the plugin by either users or roles
101
  * Enable or disable any security alerts
102
  * and much more...
103
 
104
+ ### As Featured On:
105
 
106
  * [GoDaddy](https://www.godaddy.com/garage/decode-security-logs-wordpress/)
107
  * [Pagely](https://pagely.com/blog/2015/01/log-wordpress-dashboard-activity-improved-security-auditing/)
179
 
180
  == Changelog ==
181
 
182
+ = 3.1.2(2018-03-16) =
183
+
184
+ * **Security improvement**
185
+ * Move log for failed logins in the database
186
+ * Added index.php and .htaccess files to the plugin upload directory (restricting directory listing)
187
+ * Above fixes applied to address potential information disclosure issue reported by Colette Chamberland, Defiant, Inc.
188
+
 
 
 
 
 
 
 
 
 
 
 
 
189
  Refer to the [WP Security Audit Log change log](https://www.wpsecurityauditlog.com/plugin-change-log/) page for the complete change log.
wp-security-audit-log.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin URI: http://www.wpsecurityauditlog.com/
5
  * Description: Identify WordPress security issues before they become a problem. Keep track of everything happening on your WordPress including WordPress users activity. Similar to Windows Event Log and Linux Syslog, WP Security Audit Log generates a security alert for everything that happens on your WordPress blogs and websites. Use the Audit Log Viewer included in the plugin to see all the security alerts.
6
  * Author: WP White Security
7
- * Version: 3.1.1
8
  * Text Domain: wp-security-audit-log
9
  * Author URI: http://www.wpsecurityauditlog.com/
10
  * License: GPL2
@@ -54,7 +54,7 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
54
  *
55
  * @var string
56
  */
57
- public $version = '3.1.1';
58
 
59
  // Plugin constants.
60
  const PLG_CLS_PRFX = 'WSAL_';
@@ -219,6 +219,12 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
219
  // Render Login Page Notification.
220
  add_filter( 'login_message', array( $this, 'render_login_page_message' ), 10, 1 );
221
 
 
 
 
 
 
 
222
  // Register freemius uninstall event.
223
  wsal_freemius()->add_action( 'after_uninstall', array( $this, 'wsal_freemius_uninstall_cleanup' ) );
224
 
@@ -501,6 +507,9 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
501
  if ( $old_version !== $new_version ) {
502
  $this->Update( $old_version, $new_version );
503
  }
 
 
 
504
  }
505
 
506
  /**
@@ -920,6 +929,36 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
920
  $s->Stop();
921
  }
922
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
923
  /**
924
  * Add callback to be called when a cleanup operation is required.
925
  *
4
  * Plugin URI: http://www.wpsecurityauditlog.com/
5
  * Description: Identify WordPress security issues before they become a problem. Keep track of everything happening on your WordPress including WordPress users activity. Similar to Windows Event Log and Linux Syslog, WP Security Audit Log generates a security alert for everything that happens on your WordPress blogs and websites. Use the Audit Log Viewer included in the plugin to see all the security alerts.
6
  * Author: WP White Security
7
+ * Version: 3.1.2
8
  * Text Domain: wp-security-audit-log
9
  * Author URI: http://www.wpsecurityauditlog.com/
10
  * License: GPL2
54
  *
55
  * @var string
56
  */
57
+ public $version = '3.1.2';
58
 
59
  // Plugin constants.
60
  const PLG_CLS_PRFX = 'WSAL_';
219
  // Render Login Page Notification.
220
  add_filter( 'login_message', array( $this, 'render_login_page_message' ), 10, 1 );
221
 
222
+ // Cron job to delete alert 1003 for the last day.
223
+ add_action( 'wsal_delete_logins', array( $this, 'delete_failed_logins' ) );
224
+ if ( ! wp_next_scheduled( 'wsal_delete_logins' ) ) {
225
+ wp_schedule_event( time(), 'daily', 'wsal_delete_logins' );
226
+ }
227
+
228
  // Register freemius uninstall event.
229
  wsal_freemius()->add_action( 'after_uninstall', array( $this, 'wsal_freemius_uninstall_cleanup' ) );
230
 
507
  if ( $old_version !== $new_version ) {
508
  $this->Update( $old_version, $new_version );
509
  }
510
+
511
+ // Generate index.php for uploads directory.
512
+ $this->settings->generate_index_files();
513
  }
514
 
515
  /**
929
  $s->Stop();
930
  }
931
 
932
+ /**
933
+ * Clear last day's failed login alert.
934
+ */
935
+ public function delete_failed_logins() {
936
+ // Set the dates.
937
+ list( $y, $m, $d ) = explode( '-', date( 'Y-m-d' ) );
938
+
939
+ // Site id.
940
+ $site_id = (function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 0);
941
+
942
+ // New occurrence object.
943
+ $occurrence = new WSAL_Models_Occurrence();
944
+ $alerts = $occurrence->check_alert_1003(
945
+ array(
946
+ 1003,
947
+ $site_id,
948
+ mktime( 0, 0, 0, $m, $d - 1, $y ) + 1,
949
+ mktime( 0, 0, 0, $m, $d, $y ),
950
+ )
951
+ );
952
+
953
+ // Alerts exists then continue.
954
+ if ( ! empty( $alerts ) ) {
955
+ foreach ( $alerts as $alert ) {
956
+ // Flush the usernames meta data.
957
+ $alert->UpdateMetaValue( 'Users', array() );
958
+ }
959
+ }
960
+ }
961
+
962
  /**
963
  * Add callback to be called when a cleanup operation is required.
964
  *