Wordfence Security – Firewall & Malware Scan - Version 5.0.9

Version Description

  • Feature: (Premium) Advanced Comment Spam Filter. Checks comment source IP, author URL and hosts and IP's in body against additional spam lists.
  • Feature: (Premium) Check if your site is being Spamvertised i.e. your domain is being included in spam emails. Usually indicates you've been hacked.
  • Feature: (Premium) Check if your website IP is generating spam. Checks against spam lists if your IP is a known source of spam.
  • Improvement: Cache clearing errors are nown shown with clear explanations.
  • Improvement: Added lightweight stats logging internally in preparation for displaying them on the admin UI in the next release.
  • Fix: If a non-existent user tries to sign in it is not logged in the live logins tab. Fixed.
  • Fix: Removed warning "Trying to get property of non-object" that would occur under certain conditions.
  • Fix: Removed call to is_404() which was not having any effect and would issue a warning if debug mode is enabled.
  • Fix: Check if CURL is installed as part of connectivity test.
Download this release

Release Info

Developer mmaunder
Plugin Icon 128x128 Wordfence Security – Firewall & Malware Scan
Version 5.0.9
Comparing to
See all releases

Code changes from version 5.0.8 to 5.0.9

lib/conntest.php CHANGED
@@ -40,6 +40,10 @@ function doWPostTest($protocol){
40
  }
41
  }
42
  function doCurlTest($protocol){
 
 
 
 
43
  echo "<br /><b>STARTING CURL $protocol CONNECTION TEST....</b><br />\n";
44
  global $curlContent;
45
  $curlContent = "";
40
  }
41
  }
42
  function doCurlTest($protocol){
43
+ if(! function_exists('curl_init')){
44
+ echo "<br /><b style='color: #F00;'>CURL is not installed</b>. Asking your hosting provider to install and enable CURL may improve any connection problems.</b><br />\n";
45
+ return;
46
+ }
47
  echo "<br /><b>STARTING CURL $protocol CONNECTION TEST....</b><br />\n";
48
  global $curlContent;
49
  $curlContent = "";
lib/dashboard.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <table border="0">
3
+ <?php
4
+ $lastAdminLogin = wfConfig::get_ser('lastAdminLogin', false);
5
+ if($lastAdminLogin){
6
+ ?>
7
+ <tr><td style="padding-right: 20px;">Last Admin Username Login:</td><td>
8
+ <?php
9
+ echo '<strong>' . $lastAdminLogin['username'] . '</strong> [' . $lastAdminLogin['firstName'] . ' ' . $lastAdminLogin['lastName'] . ']</td></tr><tr style="padding-right: 20px;"><td>Last Admin Login Time</td><td>' . $lastAdminLogin['time'] . '</td></tr><tr><td style="padding-right: 20px;">Last Admin Login IP:</td><td>' . $lastAdminLogin['IP'] . '</td></tr>';
10
+ ?>
11
+ </td></tr>
12
+ <?php } ?>
13
+ <?php if(wfConfig::get('firewallEnabled')){ ?><tr><td style="padding-right: 20px;">Firewall Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
14
+ <?php if(wfConfig::get('loginSecurityEnabled')){ ?><tr><td style="padding-right: 20px;">Login Security Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
15
+ <?php if(wfConfig::get('other_scanComments')){ ?><tr><td style="padding-right: 20px;">Comment Filter Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
16
+ <?php if(wfConfig::get('other_hideWPVersion')){ ?><tr><td style="padding-right: 20px;">WordPress Version Hiding Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
17
+ <?php if(wfConfig::get('other_pwStrengthOnUpdate')){ ?><tr><td style="padding-right: 20px;">Password Strength Checking Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
18
+ <?php if(wfConfig::get('other_WFNet')){ ?><tr><td style="padding-right: 20px;">Security Network Active:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
19
+ <?php if(wfConfig::get('loginSec_maskLoginErrors')){ ?><tr><td style="padding-right: 20px;">Username Hiding in login form errors:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
20
+ <?php if(wfConfig::get('loginSec_disableAuthorScan')){ ?><tr><td style="padding-right: 20px;">Prevent username discovery by bots:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
21
+ <?php if(wfConfig::get('loginSec_blockAdminReg')){ ?><tr><td style="padding-right: 20px;">Protect 'admin' username from being registered:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
22
+ <?php if(wfConfig::get('loginSec_userBlacklist')){ ?><tr><td style="padding-right: 20px;">Instant blocking of specific username attempts:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
23
+ <?php if(wfConfig::get('other_noAnonMemberComments')){ ?><tr><td style="padding-right: 20px;">Hold anon comments by existing usernames:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
24
+ <?php if(wfConfig::get('loginSec_lockInvalidUsers')){ ?><tr><td style="padding-right: 20px;">Instant lockout of invalid usernames:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
25
+ <?php if(wfConfig::get('loginSec_strongPasswds') == 'all' || wfConfig::get('loginSec_strongPasswds') == 'pubs'){ ?><tr><td style="padding-right: 20px;">Enforce strong passwords:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
26
+
27
+ <?php if(wfConfig::get('scheduledScansEnabled')){ ?>
28
+ <?php if(wfConfig::get('scheduledScansEnabled')){ ?><tr><td style="padding-right: 20px;">Security Scans Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
29
+ <?php if(wfConfig::get('scansEnabled_public')){ ?><tr><td style="padding-right: 20px;">Scan public facing site:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
30
+ <?php if(wfConfig::get('scansEnabled_heartbleed')){ ?><tr><td style="padding-right: 20px;">Scan for HeartBleed Vulnerability:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
31
+ <?php if(wfConfig::get('scansEnabled_core')){ ?><tr><td style="padding-right: 20px;">Scan Core Files:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
32
+ <?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
33
+ <?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
34
+ <?php if(wfConfig::get('scansEnabled_fileContents')){ ?><tr><td style="padding-right: 20px;">Scan Other Files:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
35
+ <?php if(wfConfig::get('scansEnabled_posts')){ ?><tr><td style="padding-right: 20px;">Scan Posts:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
36
+ <?php if(wfConfig::get('scansEnabled_comments')){ ?><tr><td style="padding-right: 20px;">Scan Comments:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
37
+ <?php if(wfConfig::get('scansEnabled_oldVersions')){ ?><tr><td style="padding-right: 20px;">Scan for Old Software:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
38
+ <?php if(wfConfig::get('scansEnabled_options')){ ?><tr><td style="padding-right: 20px;">Scan Options Table:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
39
+ <?php if(wfConfig::get('scansEnabled_highSense')){ ?><tr><td style="padding-right: 20px;">High sensitivity scans enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
40
+ <?php if(wfConfig::get('scansEnabled_scanImages')){ ?><tr><td style="padding-right: 20px;">Scan image files for executable code:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
41
+ <?php if(wfConfig::get('other_scanOutside')){ ?><tr><td style="padding-right: 20px;">Scan files outside WordPress install:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
42
+ <?php if(wfConfig::get('scansEnabled_dns')){ ?><tr><td style="padding-right: 20px;">Scan for DNS changes:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
43
+ <?php if(wfConfig::get('scansEnabled_diskSpace')){ ?><tr><td style="padding-right: 20px;">Monitor disk space:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
44
+ <?php } ?>
45
+ <?php if(wfConfig::get('debugOn')){ ?><tr><td style="padding-right: 20px;">Wordfence DEBUG mode enabled:</td><td style="color: #F00;">DEBUG ENABLED</td></tr> <?php } ?>
46
+
47
+
48
+ </table>
49
+
lib/menu_options.php CHANGED
@@ -23,7 +23,7 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
23
  <?php if(wfConfig::get('isPaid')){ ?>
24
  The currently active API Key is a Premium Key. <span style="font-weight: bold; color: #0A0;">Premium scanning enabled!</span>
25
  <?php } else {?>
26
- The currently active API Key is a <span style="color: #F00; font-weight: bold;">Free Key</a>. <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Upgrade to Premium Scanning now.</a>
27
  <?php } ?>
28
  </td></tr>
29
  <tr><td colspan="2">
@@ -39,6 +39,10 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
39
  <tr><td colspan="2">&nbsp;</td></tr>
40
  <tr><th class="wfConfigEnable">Enable Live Traffic View</th><td><input type="checkbox" id="liveTrafficEnabled" class="wfConfigElem" name="liveTrafficEnabled" value="1" <?php $w->cb('liveTrafficEnabled'); ?> onclick="WFAD.reloadConfigPage = true; return true;" />&nbsp;This option enables live traffic logging.</td></tr>
41
  <tr><td colspan="2">&nbsp;</td></tr>
 
 
 
 
42
  <?php /* <tr><th class="wfConfigEnable">Enable Performance Monitoring</th><td><input type="checkbox" id="perfLoggingEnabled" class="wfConfigElem" name="perfLoggingEnabled" value="1" <?php $w->cb('perfLoggingEnabled'); ?> onclick="WFAD.reloadConfigPage = true; return true;" />&nbsp;This option enables performance monitoring.</td></tr> */ ?>
43
  <tr><td colspan="2">&nbsp;</td></tr>
44
  <tr><th class="wfConfigEnable">Enable automatic scheduled scans</th><td><input type="checkbox" id="scheduledScansEnabled" class="wfConfigElem" name="scheduledScansEnabled" value="1" <?php $w->cb('scheduledScansEnabled'); ?> />&nbsp;Regular scans ensure your site stays secure.</td></tr>
@@ -253,7 +257,7 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
253
  <tr><th colspan="2" style="color: #999;">Whitelisted IP's must be separated by commas. You can specify ranges using the following format: 123.23.34.[1-50]<br />Wordfence automatically whitelists <a href="http://en.wikipedia.org/wiki/Private_network" target="_blank">private networks</a> because these are not routable on the public Internet.<br /><br /></th></tr>
254
  <tr><th>Hide WordPress version</th><td><input type="checkbox" id="other_hideWPVersion" class="wfConfigElem" name="other_hideWPVersion" value="1" <?php $w->cb('other_hideWPVersion'); ?> /></td></tr>
255
  <tr><th>Hold anonymous comments using member emails for moderation</th><td><input type="checkbox" id="other_noAnonMemberComments" class="wfConfigElem" name="other_noAnonMemberComments" value="1" <?php $w->cb('other_noAnonMemberComments'); ?> /></td></tr>
256
- <tr><th>Scan comments for malware and phishing URL's</th><td><input type="checkbox" id="other_scanComments" class="wfConfigElem" name="other_scanComments" value="1" <?php $w->cb('other_scanComments'); ?> /></td></tr>
257
  <tr><th>Check password strength on profile update</th><td><input type="checkbox" id="other_pwStrengthOnUpdate" class="wfConfigElem" name="other_pwStrengthOnUpdate" value="1" <?php $w->cb('other_pwStrengthOnUpdate'); ?> /></td></tr>
258
  <tr><th>Participate in the Real-Time WordPress Security Network</th><td><input type="checkbox" id="other_WFNet" class="wfConfigElem" name="other_WFNet" value="1" <?php $w->cb('other_WFNet'); ?> /></td></tr>
259
  <tr><th>Maximum memory Wordfence can use</th><td><input type="text" id="maxMem" name="maxMem" value="<?php $w->f('maxMem'); ?>" size="4" />Megabytes</td></tr>
23
  <?php if(wfConfig::get('isPaid')){ ?>
24
  The currently active API Key is a Premium Key. <span style="font-weight: bold; color: #0A0;">Premium scanning enabled!</span>
25
  <?php } else {?>
26
+ The currently active API Key is a <span style="color: #F00; font-weight: bold;">Free Key</a>. <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Click Here to Upgrade to Wordfence Premium now.</a>
27
  <?php } ?>
28
  </td></tr>
29
  <tr><td colspan="2">
39
  <tr><td colspan="2">&nbsp;</td></tr>
40
  <tr><th class="wfConfigEnable">Enable Live Traffic View</th><td><input type="checkbox" id="liveTrafficEnabled" class="wfConfigElem" name="liveTrafficEnabled" value="1" <?php $w->cb('liveTrafficEnabled'); ?> onclick="WFAD.reloadConfigPage = true; return true;" />&nbsp;This option enables live traffic logging.</td></tr>
41
  <tr><td colspan="2">&nbsp;</td></tr>
42
+ <tr><th class="wfConfigEnable">Advanced Comment Spam Filter</th><td><input type="checkbox" id="advancedCommentScanning" class="wfConfigElem" name="advancedCommentScanning" value="1" <?php $w->cbp('advancedCommentScanning'); if(! wfConfig::get('isPaid')){ ?>onclick="alert('This is a paid feature because it places significant additional load on our servers.'); jQuery('#advancedCommentScanning').attr('checked', false); return false;" <?php } ?> />&nbsp;<span style="color: #F00;">Premium Feature</span> In addition to free comment filtering (see below) this option filters comments against several additional real-time lists of known spammers and infected hosts.</td></tr>
43
+ <tr><th class="wfConfigEnable">Check if this website is being "Spamvertised"</th><td><input type="checkbox" id="spamvertizeCheck" class="wfConfigElem" name="spamvertizeCheck" value="1" <?php $w->cbp('spamvertizeCheck'); if(! wfConfig::get('isPaid')){ ?>onclick="alert('This is a paid feature because it places significant additional load on our servers.'); jQuery('#spamvertizeCheck').attr('checked', false); return false;" <?php } ?> />&nbsp;<span style="color: #F00;">Premium Feature</span> When doing a scan, Wordfence will check with spam services if your site domain name is appearing as a link in spam emails.</td></tr>
44
+ <tr><th class="wfConfigEnable">Check if this website IP is generating spam</th><td><input type="checkbox" id="checkSpamIP" class="wfConfigElem" name="checkSpamIP" value="1" <?php $w->cbp('checkSpamIP'); if(! wfConfig::get('isPaid')){ ?>onclick="alert('This is a paid feature because it places significant additional load on our servers.'); jQuery('#checkSpamIP').attr('checked', false); return false;" <?php } ?> />&nbsp;<span style="color: #F00;">Premium Feature</span> When doing a scan, Wordfence will check with spam services if your website IP address is listed as a known source of spam email.</td></tr>
45
+ <tr><td colspan="2">&nbsp;</td></tr>
46
  <?php /* <tr><th class="wfConfigEnable">Enable Performance Monitoring</th><td><input type="checkbox" id="perfLoggingEnabled" class="wfConfigElem" name="perfLoggingEnabled" value="1" <?php $w->cb('perfLoggingEnabled'); ?> onclick="WFAD.reloadConfigPage = true; return true;" />&nbsp;This option enables performance monitoring.</td></tr> */ ?>
47
  <tr><td colspan="2">&nbsp;</td></tr>
48
  <tr><th class="wfConfigEnable">Enable automatic scheduled scans</th><td><input type="checkbox" id="scheduledScansEnabled" class="wfConfigElem" name="scheduledScansEnabled" value="1" <?php $w->cb('scheduledScansEnabled'); ?> />&nbsp;Regular scans ensure your site stays secure.</td></tr>
257
  <tr><th colspan="2" style="color: #999;">Whitelisted IP's must be separated by commas. You can specify ranges using the following format: 123.23.34.[1-50]<br />Wordfence automatically whitelists <a href="http://en.wikipedia.org/wiki/Private_network" target="_blank">private networks</a> because these are not routable on the public Internet.<br /><br /></th></tr>
258
  <tr><th>Hide WordPress version</th><td><input type="checkbox" id="other_hideWPVersion" class="wfConfigElem" name="other_hideWPVersion" value="1" <?php $w->cb('other_hideWPVersion'); ?> /></td></tr>
259
  <tr><th>Hold anonymous comments using member emails for moderation</th><td><input type="checkbox" id="other_noAnonMemberComments" class="wfConfigElem" name="other_noAnonMemberComments" value="1" <?php $w->cb('other_noAnonMemberComments'); ?> /></td></tr>
260
+ <tr><th>Filter comments for malware and phishing URL's</th><td><input type="checkbox" id="other_scanComments" class="wfConfigElem" name="other_scanComments" value="1" <?php $w->cb('other_scanComments'); ?> /></td></tr>
261
  <tr><th>Check password strength on profile update</th><td><input type="checkbox" id="other_pwStrengthOnUpdate" class="wfConfigElem" name="other_pwStrengthOnUpdate" value="1" <?php $w->cb('other_pwStrengthOnUpdate'); ?> /></td></tr>
262
  <tr><th>Participate in the Real-Time WordPress Security Network</th><td><input type="checkbox" id="other_WFNet" class="wfConfigElem" name="other_WFNet" value="1" <?php $w->cb('other_WFNet'); ?> /></td></tr>
263
  <tr><th>Maximum memory Wordfence can use</th><td><input type="text" id="maxMem" name="maxMem" value="<?php $w->f('maxMem'); ?>" size="4" />Megabytes</td></tr>
lib/menu_scan.php CHANGED
@@ -651,6 +651,67 @@
651
  </div>
652
  </div>
653
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
 
655
 
656
 
651
  </div>
652
  </div>
653
  </script>
654
+ <script type="text/x-jquery-template" id="issueTmpl_checkSpamIP">
655
+ <div>
656
+ <div class="wfIssue">
657
+ <h2>${shortMsg}</h2>
658
+ <p>
659
+ <table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
660
+ <tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
661
+ <tr><th>Status</th><td>
662
+ {{if status == 'new' }}New{{/if}}
663
+ {{if status == 'ignoreC' }}This redirect will be ignored until it changes.{{/if}}
664
+ {{if status == 'ignoreP' }}This redirect is permanently ignored.{{/if}}
665
+ </td></tr>
666
+ </table>
667
+ </p>
668
+ <p>
669
+ {{html longMsg}}
670
+ </p>
671
+ <div class="wfIssueOptions">
672
+ {{if status == 'new'}}
673
+ <strong>Resolve:</strong>
674
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
675
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreP'); return false;">Ignore this problem</a>
676
+ {{/if}}
677
+ {{if status == 'ignoreP' || status == 'ignoreC'}}
678
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
679
+ {{/if}}
680
+ </div>
681
+ </div>
682
+ </div>
683
+ </script>
684
+
685
+ <script type="text/x-jquery-template" id="issueTmpl_spamvertizeCheck">
686
+ <div>
687
+ <div class="wfIssue">
688
+ <h2>${shortMsg}</h2>
689
+ <p>
690
+ <table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
691
+ <tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
692
+ <tr><th>Status</th><td>
693
+ {{if status == 'new' }}New{{/if}}
694
+ {{if status == 'ignoreC' }}This redirect will be ignored until it changes.{{/if}}
695
+ {{if status == 'ignoreP' }}This redirect is permanently ignored.{{/if}}
696
+ </td></tr>
697
+ </table>
698
+ </p>
699
+ <p>
700
+ {{html longMsg}}
701
+ </p>
702
+ <div class="wfIssueOptions">
703
+ {{if status == 'new'}}
704
+ <strong>Resolve:</strong>
705
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
706
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreP'); return false;">Ignore this problem</a>
707
+ {{/if}}
708
+ {{if status == 'ignoreP' || status == 'ignoreC'}}
709
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
710
+ {{/if}}
711
+ </div>
712
+ </div>
713
+ </div>
714
+ </script>
715
 
716
 
717
 
lib/wfCache.php CHANGED
@@ -5,6 +5,7 @@ class wfCache {
5
  private static $cacheStats = array();
6
  private static $cacheClearedThisRequest = false;
7
  private static $clearScheduledThisRequest = false;
 
8
  public static function setupCaching(){
9
  self::$cacheType = wfConfig::get('cacheType');
10
  if(self::$cacheType != 'php' && self::$cacheType != 'falcon'){
@@ -67,9 +68,6 @@ class wfCache {
67
  return $status;
68
  }
69
  public static function isCachable(){
70
- if(function_exists('is_404') && is_404()){
71
- return false;
72
- }
73
  if(defined('WFDONOTCACHE') || defined('DONOTCACHEPAGE') || defined('DONOTCACHEDB') || defined('DONOTCACHEOBJECT')){ //If you want to tell Wordfence not to cache something in another plugin, simply define one of these.
74
  return false;
75
  }
@@ -320,18 +318,32 @@ class wfCache {
320
  'dirsDeleted' => 0,
321
  'filesDeleted' => 0,
322
  'totalData' => 0,
323
- 'totalErrors' => 0
 
324
  );
325
  $cacheClearLock = WP_CONTENT_DIR . '/wfcache/clear.lock';
326
  if(! is_file($cacheClearLock)){
327
- touch($cacheClearLock);
 
 
 
 
328
  }
329
  $fp = fopen($cacheClearLock, 'w');
330
- if(! $fp){ return; }
 
 
 
 
331
  if(flock($fp, LOCK_EX | LOCK_NB)){ //non blocking exclusive flock attempt. If we get a lock then it continues and returns true. If we don't lock, then return false, don't block and don't clear the cache.
332
  // This logic means that if a cache clear is currently in progress we don't try to clear the cache.
333
  // This prevents web server children from being queued up waiting to be able to also clear the cache.
 
334
  self::recursiveDelete(WP_CONTENT_DIR . '/wfcache/');
 
 
 
 
335
  flock($fp, LOCK_UN);
336
  }
337
  fclose($fp);
@@ -342,7 +354,9 @@ class wfCache {
342
  $files = array_diff(scandir($dir), array('.','..'));
343
  foreach ($files as $file) {
344
  if(is_dir($dir . '/' . $file)){
345
- self::recursiveDelete($dir . '/' . $file);
 
 
346
  } else {
347
  if($file == 'clear.lock'){ continue; } //Don't delete our lock file
348
  $size = filesize($dir . '/' . $file);
@@ -350,29 +364,37 @@ class wfCache {
350
  self::$cacheStats['totalData'] += round($size / 1024);
351
  }
352
  if(strpos($dir, 'wfcache/') === false){
353
- //error_log("Tried to delete file in invalid dir in cache clear: $dir");
354
- return; //Safety check that we're in a subdir of the cache
 
355
  }
356
  if(@unlink($dir . '/' . $file)){
357
  self::$cacheStats['filesDeleted']++;
358
  } else {
 
359
  self::$cacheStats['totalErrors']++;
 
360
  }
361
  }
362
  }
363
  if($dir != WP_CONTENT_DIR . '/wfcache/'){
364
  if(strpos($dir, 'wfcache/') === false){
365
- //error_log("Tried to delete invalid dir in cache clear: $dir");
 
366
  return; //Safety check that we're in a subdir of the cache
367
  }
368
  if(@rmdir($dir)){
369
  self::$cacheStats['dirsDeleted']++;
370
  } else {
 
371
  self::$cacheStats['totalErrors']++;
 
372
  }
 
373
  } else {
374
  return true;
375
  }
 
376
  }
377
  public static function addHtaccessCode($action){
378
  if($action != 'add' && $action != 'remove'){
5
  private static $cacheStats = array();
6
  private static $cacheClearedThisRequest = false;
7
  private static $clearScheduledThisRequest = false;
8
+ private static $lastRecursiveDeleteError = false;
9
  public static function setupCaching(){
10
  self::$cacheType = wfConfig::get('cacheType');
11
  if(self::$cacheType != 'php' && self::$cacheType != 'falcon'){
68
  return $status;
69
  }
70
  public static function isCachable(){
 
 
 
71
  if(defined('WFDONOTCACHE') || defined('DONOTCACHEPAGE') || defined('DONOTCACHEDB') || defined('DONOTCACHEOBJECT')){ //If you want to tell Wordfence not to cache something in another plugin, simply define one of these.
72
  return false;
73
  }
318
  'dirsDeleted' => 0,
319
  'filesDeleted' => 0,
320
  'totalData' => 0,
321
+ 'totalErrors' => 0,
322
+ 'error' => '',
323
  );
324
  $cacheClearLock = WP_CONTENT_DIR . '/wfcache/clear.lock';
325
  if(! is_file($cacheClearLock)){
326
+ if(! touch($cacheClearLock)){
327
+ self::$cacheStats['error'] = "Could not create a lock file $cacheClearLock to clear the cache.";
328
+ self::$cacheStats['totalErrors']++;
329
+ return self::$cacheStats;
330
+ }
331
  }
332
  $fp = fopen($cacheClearLock, 'w');
333
+ if(! $fp){
334
+ self::$cacheStats['error'] = "Could not open the lock file $cacheClearLock to clear the cache. Please make sure the directory is writable by your web server.";
335
+ self::$cacheStats['totalErrors']++;
336
+ return self::$cacheStats;
337
+ }
338
  if(flock($fp, LOCK_EX | LOCK_NB)){ //non blocking exclusive flock attempt. If we get a lock then it continues and returns true. If we don't lock, then return false, don't block and don't clear the cache.
339
  // This logic means that if a cache clear is currently in progress we don't try to clear the cache.
340
  // This prevents web server children from being queued up waiting to be able to also clear the cache.
341
+ self::$lastRecursiveDeleteError = false;
342
  self::recursiveDelete(WP_CONTENT_DIR . '/wfcache/');
343
+ if(self::$lastRecursiveDeleteError){
344
+ self::$cacheStats['error'] = self::$lastRecursiveDeleteError;
345
+ self::$cacheStats['totalErrors']++;
346
+ }
347
  flock($fp, LOCK_UN);
348
  }
349
  fclose($fp);
354
  $files = array_diff(scandir($dir), array('.','..'));
355
  foreach ($files as $file) {
356
  if(is_dir($dir . '/' . $file)){
357
+ if(! self::recursiveDelete($dir . '/' . $file)){
358
+ return false;
359
+ }
360
  } else {
361
  if($file == 'clear.lock'){ continue; } //Don't delete our lock file
362
  $size = filesize($dir . '/' . $file);
364
  self::$cacheStats['totalData'] += round($size / 1024);
365
  }
366
  if(strpos($dir, 'wfcache/') === false){
367
+ self::$lastRecursiveDeleteError = "Not deleting file in directory $dir because it appears to be in the wrong path.";
368
+ self::$cacheStats['totalErrors']++;
369
+ return false; //Safety check that we're in a subdir of the cache
370
  }
371
  if(@unlink($dir . '/' . $file)){
372
  self::$cacheStats['filesDeleted']++;
373
  } else {
374
+ self::$lastRecursiveDeleteError = "Could not delete file " . $dir . "/" . $file . " : " . wfUtils::getLastError();
375
  self::$cacheStats['totalErrors']++;
376
+ return false;
377
  }
378
  }
379
  }
380
  if($dir != WP_CONTENT_DIR . '/wfcache/'){
381
  if(strpos($dir, 'wfcache/') === false){
382
+ self::$lastRecursiveDeleteError = "Not deleting directory $dir because it appears to be in the wrong path.";
383
+ self::$cacheStats['totalErrors']++;
384
  return; //Safety check that we're in a subdir of the cache
385
  }
386
  if(@rmdir($dir)){
387
  self::$cacheStats['dirsDeleted']++;
388
  } else {
389
+ self::$lastRecursiveDeleteError = "Could not delete directory $dir : " . wfUtils::getLastError();
390
  self::$cacheStats['totalErrors']++;
391
+ return false;
392
  }
393
+ return true;
394
  } else {
395
  return true;
396
  }
397
+ return true;
398
  }
399
  public static function addHtaccessCode($action){
400
  if($action != 'add' && $action != 'remove'){
lib/wfConfig.php CHANGED
@@ -19,6 +19,9 @@ class wfConfig {
19
  "alertOn_adminLogin" => false,
20
  "alertOn_nonAdminLogin" => false,
21
  "liveTrafficEnabled" => true,
 
 
 
22
  "liveTraf_ignorePublishers" => true,
23
  //"perfLoggingEnabled" => false,
24
  "scheduledScansEnabled" => false,
@@ -94,6 +97,9 @@ class wfConfig {
94
  "alertOn_adminLogin" => true,
95
  "alertOn_nonAdminLogin" => false,
96
  "liveTrafficEnabled" => true,
 
 
 
97
  "liveTraf_ignorePublishers" => true,
98
  //"perfLoggingEnabled" => false,
99
  "scheduledScansEnabled" => true,
@@ -169,6 +175,9 @@ class wfConfig {
169
  "alertOn_adminLogin" => true,
170
  "alertOn_nonAdminLogin" => false,
171
  "liveTrafficEnabled" => true,
 
 
 
172
  "liveTraf_ignorePublishers" => true,
173
  //"perfLoggingEnabled" => false,
174
  "scheduledScansEnabled" => true,
@@ -244,6 +253,9 @@ class wfConfig {
244
  "alertOn_adminLogin" => true,
245
  "alertOn_nonAdminLogin" => false,
246
  "liveTrafficEnabled" => true,
 
 
 
247
  "liveTraf_ignorePublishers" => true,
248
  //"perfLoggingEnabled" => false,
249
  "scheduledScansEnabled" => true,
@@ -319,6 +331,9 @@ class wfConfig {
319
  "alertOn_adminLogin" => true,
320
  "alertOn_nonAdminLogin" => false,
321
  "liveTrafficEnabled" => true,
 
 
 
322
  "liveTraf_ignorePublishers" => true,
323
  //"perfLoggingEnabled" => false,
324
  "scheduledScansEnabled" => true,
@@ -435,6 +450,13 @@ class wfConfig {
435
  public static function getHTML($key){
436
  return htmlspecialchars(self::get($key));
437
  }
 
 
 
 
 
 
 
438
  public static function set($key, $val){
439
  if(is_array($val)){
440
  $msg = "wfConfig::set() got an array as second param with key: $key and value: " . var_export($val, true);
@@ -621,6 +643,11 @@ class wfConfig {
621
  public static function f($key){
622
  echo esc_attr(self::get($key));
623
  }
 
 
 
 
 
624
  public static function cb($key){
625
  if(self::get($key)){
626
  echo ' checked ';
19
  "alertOn_adminLogin" => false,
20
  "alertOn_nonAdminLogin" => false,
21
  "liveTrafficEnabled" => true,
22
+ "advancedCommentScanning" => false,
23
+ "checkSpamIP" => false,
24
+ "spamvertizeCheck" => false,
25
  "liveTraf_ignorePublishers" => true,
26
  //"perfLoggingEnabled" => false,
27
  "scheduledScansEnabled" => false,
97
  "alertOn_adminLogin" => true,
98
  "alertOn_nonAdminLogin" => false,
99
  "liveTrafficEnabled" => true,
100
+ "advancedCommentScanning" => false,
101
+ "checkSpamIP" => false,
102
+ "spamvertizeCheck" => false,
103
  "liveTraf_ignorePublishers" => true,
104
  //"perfLoggingEnabled" => false,
105
  "scheduledScansEnabled" => true,
175
  "alertOn_adminLogin" => true,
176
  "alertOn_nonAdminLogin" => false,
177
  "liveTrafficEnabled" => true,
178
+ "advancedCommentScanning" => false,
179
+ "checkSpamIP" => false,
180
+ "spamvertizeCheck" => false,
181
  "liveTraf_ignorePublishers" => true,
182
  //"perfLoggingEnabled" => false,
183
  "scheduledScansEnabled" => true,
253
  "alertOn_adminLogin" => true,
254
  "alertOn_nonAdminLogin" => false,
255
  "liveTrafficEnabled" => true,
256
+ "advancedCommentScanning" => false,
257
+ "checkSpamIP" => false,
258
+ "spamvertizeCheck" => false,
259
  "liveTraf_ignorePublishers" => true,
260
  //"perfLoggingEnabled" => false,
261
  "scheduledScansEnabled" => true,
331
  "alertOn_adminLogin" => true,
332
  "alertOn_nonAdminLogin" => false,
333
  "liveTrafficEnabled" => true,
334
+ "advancedCommentScanning" => false,
335
+ "checkSpamIP" => false,
336
+ "spamvertizeCheck" => false,
337
  "liveTraf_ignorePublishers" => true,
338
  //"perfLoggingEnabled" => false,
339
  "scheduledScansEnabled" => true,
450
  public static function getHTML($key){
451
  return htmlspecialchars(self::get($key));
452
  }
453
+ public static function inc($key){
454
+ $val = self::get($key, false);
455
+ if(! $val){
456
+ $val = 0;
457
+ }
458
+ self::set($key, $val + 1);
459
+ }
460
  public static function set($key, $val){
461
  if(is_array($val)){
462
  $msg = "wfConfig::set() got an array as second param with key: $key and value: " . var_export($val, true);
643
  public static function f($key){
644
  echo esc_attr(self::get($key));
645
  }
646
+ public static function cbp($key){
647
+ if(self::get('isPaid') && self::get($key)){
648
+ echo ' checked ';
649
+ }
650
+ }
651
  public static function cb($key){
652
  if(self::get($key)){
653
  echo ' checked ';
lib/wfLog.php CHANGED
@@ -55,9 +55,7 @@ class wfLog {
55
  if(! $userID){
56
  return;
57
  }
58
- } else {
59
- return;
60
- }
61
  $this->getDB()->queryWrite("insert into " . $this->loginsTable . " (ctime, fail, action, username, userID, IP, UA) values (%f, %d, '%s', '%s', %s, %s, '%s')",
62
  sprintf('%.6f', microtime(true)),
63
  $fail,
@@ -281,6 +279,7 @@ class wfLog {
281
  );
282
  }
283
  wfCache::updateBlockedIPs('add');
 
284
  return true;
285
  }
286
  public function lockOutIP($IP, $reason){
@@ -290,6 +289,7 @@ class wfLog {
290
  $reason,
291
  $reason
292
  );
 
293
  return true;
294
  }
295
  public function unlockOutIP($IP){
@@ -713,6 +713,7 @@ class wfLog {
713
  }
714
  } else {
715
  $this->do503(3600, "Access from your area has been temporarily limited for security reasons");
 
716
  }
717
  }
718
  }
@@ -768,6 +769,7 @@ class wfLog {
768
  $IP = wfUtils::getIP();
769
  $this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_aton($IP), $reason, $reason);
770
  wordfence::status(2, 'info', "Throttling IP $IP. $reason");
 
771
  $secsToGo = 60;
772
  }
773
  $this->do503($secsToGo, $reason);
@@ -776,6 +778,7 @@ class wfLog {
776
  }
777
  }
778
  public function do503($secsToGo, $reason){
 
779
  wfUtils::doNotCache();
780
  header('HTTP/1.1 503 Service Temporarily Unavailable');
781
  header('Status: 503 Service Temporarily Unavailable');
55
  if(! $userID){
56
  return;
57
  }
58
+ } //Else userID stays 0 but we do log this even though the user doesn't exist.
 
 
59
  $this->getDB()->queryWrite("insert into " . $this->loginsTable . " (ctime, fail, action, username, userID, IP, UA) values (%f, %d, '%s', '%s', %s, %s, '%s')",
60
  sprintf('%.6f', microtime(true)),
61
  $fail,
279
  );
280
  }
281
  wfCache::updateBlockedIPs('add');
282
+ wfConfig::inc('totalIPsBlocked');
283
  return true;
284
  }
285
  public function lockOutIP($IP, $reason){
289
  $reason,
290
  $reason
291
  );
292
+ wfConfig::inc('totalIPsLocked');
293
  return true;
294
  }
295
  public function unlockOutIP($IP){
713
  }
714
  } else {
715
  $this->do503(3600, "Access from your area has been temporarily limited for security reasons");
716
+ wfConfig::inc('totalCountryBlocked');
717
  }
718
  }
719
  }
769
  $IP = wfUtils::getIP();
770
  $this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_aton($IP), $reason, $reason);
771
  wordfence::status(2, 'info', "Throttling IP $IP. $reason");
772
+ wfConfig::inc('totalIPsThrottled');
773
  $secsToGo = 60;
774
  }
775
  $this->do503($secsToGo, $reason);
778
  }
779
  }
780
  public function do503($secsToGo, $reason){
781
+ wfConfig::inc('total503s');
782
  wfUtils::doNotCache();
783
  header('HTTP/1.1 503 Service Temporarily Unavailable');
784
  header('Status: 503 Service Temporarily Unavailable');
lib/wfScanEngine.php CHANGED
@@ -49,6 +49,8 @@ class wfScanEngine {
49
  include('wfDict.php'); //$dictWords
50
  $this->dictWords = $dictWords;
51
  $this->jobList[] = 'publicSite';
 
 
52
  $this->jobList[] = 'heartbleed';
53
  $this->jobList[] = 'knownFiles_init';
54
  $this->jobList[] = 'knownFiles_main';
@@ -173,6 +175,54 @@ class wfScanEngine {
173
  sleep(2); //enough time to read the message before it scrolls off.
174
  }
175
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  private function scan_knownFiles_init(){
177
  $this->status(1, 'info', "Contacting Wordfence to initiate scan");
178
  $this->api->call('log_scan', array(), array());
@@ -887,6 +937,7 @@ class wfScanEngine {
887
  }
888
  public static function startScan($isFork = false){
889
  if(! $isFork){ //beginning of scan
 
890
  wfConfig::set('wfKillRequested', 0);
891
  wordfence::status(4, 'info', "Entering start scan routine");
892
  if(wfUtils::isScanRunning()){
49
  include('wfDict.php'); //$dictWords
50
  $this->dictWords = $dictWords;
51
  $this->jobList[] = 'publicSite';
52
+ $this->jobList[] = 'checkSpamvertized';
53
+ $this->jobList[] = 'checkSpamIP';
54
  $this->jobList[] = 'heartbleed';
55
  $this->jobList[] = 'knownFiles_init';
56
  $this->jobList[] = 'knownFiles_main';
175
  sleep(2); //enough time to read the message before it scrolls off.
176
  }
177
  }
178
+ private function scan_checkSpamIP(){
179
+ if(wfConfig::get('isPaid')){
180
+ if(wfConfig::get('checkSpamIP')){
181
+ $this->statusIDX['checkSpamIP'] = wordfence::statusStart("Checking if your site IP is generating spam");
182
+ $result = $this->api->call('check_spam_ip', array(), array(
183
+ 'siteURL' => site_url()
184
+ ));
185
+ $haveIssues = false;
186
+ if($result['haveIssues'] && is_array($result['issues']) ){
187
+ foreach($result['issues'] as $issue){
188
+ $this->addIssue($issue['type'], $issue['level'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
189
+ $haveIssues = true;
190
+ }
191
+ }
192
+ wordfence::statusEnd($this->statusIDX['checkSpamIP'], $haveIssues);
193
+ } else {
194
+ wordfence::statusDisabled("Skipping check if your IP is generating spam");
195
+ }
196
+
197
+ } else {
198
+ wordfence::statusPaidOnly("Checking if your IP is generating spam is for paid members only");
199
+ sleep(2);
200
+ }
201
+ }
202
+ private function scan_checkSpamvertized(){
203
+ if(wfConfig::get('isPaid')){
204
+ if(wfConfig::get('spamvertizeCheck')){
205
+ $this->statusIDX['spamvertizeCheck'] = wordfence::statusStart("Checking if your site is being Spamvertised");
206
+ $result = $this->api->call('spamvertize_check', array(), array(
207
+ 'siteURL' => site_url()
208
+ ));
209
+ $haveIssues = false;
210
+ if($result['haveIssues'] && is_array($result['issues']) ){
211
+ foreach($result['issues'] as $issue){
212
+ $this->addIssue($issue['type'], $issue['level'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
213
+ $haveIssues = true;
214
+ }
215
+ }
216
+ wordfence::statusEnd($this->statusIDX['spamvertizeCheck'], $haveIssues);
217
+ } else {
218
+ wordfence::statusDisabled("Skipping check if your site is being spamvertized");
219
+ }
220
+
221
+ } else {
222
+ wordfence::statusPaidOnly("Check if your site is being Spamvertized is for paid members only");
223
+ sleep(2);
224
+ }
225
+ }
226
  private function scan_knownFiles_init(){
227
  $this->status(1, 'info', "Contacting Wordfence to initiate scan");
228
  $this->api->call('log_scan', array(), array());
937
  }
938
  public static function startScan($isFork = false){
939
  if(! $isFork){ //beginning of scan
940
+ wfConfig::inc('totalScansRun');
941
  wfConfig::set('wfKillRequested', 0);
942
  wordfence::status(4, 'info', "Entering start scan routine");
943
  if(wfUtils::isScanRunning()){
lib/wfUtils.php CHANGED
@@ -321,14 +321,26 @@ class wfUtils {
321
  self::iniSet('memory_limit', $maxMem . 'M');
322
  }
323
  }
324
- public static function isAdmin(){
325
- if(is_multisite()){
326
- if(current_user_can('manage_network')){
327
- return true;
 
 
 
 
 
 
328
  }
329
  } else {
330
- if(current_user_can('manage_options')){
331
- return true;
 
 
 
 
 
 
332
  }
333
  }
334
  return false;
@@ -415,6 +427,10 @@ class wfUtils {
415
  $db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)", ($isInt ? $IP : self::inet_aton($IP)) );
416
  $IPLocs[$IP] = false;
417
  } else {
 
 
 
 
418
  $db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
419
  ($isInt ? $IP : self::inet_aton($IP)),
420
  $value[3], //city
@@ -544,6 +560,9 @@ class wfUtils {
544
  public static function localHumanDate(){
545
  return date('l jS \of F Y \a\t h:i:s A', time() + (3600 * get_option('gmt_offset')));
546
  }
 
 
 
547
  public static function funcEnabled($func){
548
  if(! function_exists($func)){ return false; }
549
  $disabled = explode(',', ini_get('disable_functions'));
@@ -600,6 +619,13 @@ class wfUtils {
600
  return true;
601
  }
602
  }
 
 
 
 
 
 
 
603
  }
604
 
605
 
321
  self::iniSet('memory_limit', $maxMem . 'M');
322
  }
323
  }
324
+ public static function isAdmin($user = false){
325
+ if($user){
326
+ if(is_multisite()){
327
+ if(user_can($user, 'manage_network')){
328
+ return true;
329
+ }
330
+ } else {
331
+ if(user_can($user, 'manage_options')){
332
+ return true;
333
+ }
334
  }
335
  } else {
336
+ if(is_multisite()){
337
+ if(current_user_can('manage_network')){
338
+ return true;
339
+ }
340
+ } else {
341
+ if(current_user_can('manage_options')){
342
+ return true;
343
+ }
344
  }
345
  }
346
  return false;
427
  $db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)", ($isInt ? $IP : self::inet_aton($IP)) );
428
  $IPLocs[$IP] = false;
429
  } else {
430
+ for($i = 0; $i <= 5; $i++){
431
+ //Prevent warnings in debug mode about uninitialized values
432
+ if(! isset($value[$i])){ $value[$i] = ''; }
433
+ }
434
  $db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
435
  ($isInt ? $IP : self::inet_aton($IP)),
436
  $value[3], //city
560
  public static function localHumanDate(){
561
  return date('l jS \of F Y \a\t h:i:s A', time() + (3600 * get_option('gmt_offset')));
562
  }
563
+ public static function localHumanDateShort(){
564
+ return date('D jS F \@ h:i:sA', time() + (3600 * get_option('gmt_offset')));
565
+ }
566
  public static function funcEnabled($func){
567
  if(! function_exists($func)){ return false; }
568
  $disabled = explode(',', ini_get('disable_functions'));
619
  return true;
620
  }
621
  }
622
+ public static function getLastError(){
623
+ $err = error_get_last();
624
+ if(is_array($err)){
625
+ return $err['message'];
626
+ }
627
+ return '';
628
+ }
629
  }
630
 
631
 
lib/wordfenceClass.php CHANGED
@@ -28,6 +28,7 @@ class wordfence {
28
  private static $statusStartMsgs = array();
29
  private static $debugOn = null;
30
  private static $runInstallCalled = false;
 
31
  public static function installPlugin(){
32
  self::runInstall();
33
  //Used by MU code below
@@ -125,7 +126,7 @@ class wordfence {
125
  }
126
  }
127
  }
128
- private function keyAlert($msg){
129
  self::alert($msg, $msg . " To ensure uninterrupted Premium Wordfence protection on your site,\nplease renew your API key by visiting http://www.wordfence.com/ Sign in, go to your dashboard,\nselect the key about to expire and click the button to renew that API key.", false);
130
  }
131
  public static function dailyCron(){
@@ -349,6 +350,10 @@ class wordfence {
349
  add_action('wp_ajax_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
350
  add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
351
  add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
 
 
 
 
352
  }
353
 
354
 
@@ -417,6 +422,14 @@ class wordfence {
417
  return $schedules;
418
  }
419
  */
 
 
 
 
 
 
 
 
420
  public static function jetpackMobileSetup(){
421
  define('WFDONOTCACHE', true); //Don't cache jetpack mobile theme pages.
422
  }
@@ -704,9 +717,21 @@ class wordfence {
704
  public static function loginAction($username){
705
  if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
706
  if(! $username){ return; }
 
707
  $user = get_user_by('login', $username);
708
  $userID = $user ? $user->ID : 0;
709
  self::getLog()->logLogin('loginOK', 0, $username);
 
 
 
 
 
 
 
 
 
 
 
710
  if(user_can($userID, 'update_core')){
711
  if(wfConfig::get('alertOn_adminLogin')){
712
  wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
@@ -724,6 +749,7 @@ class wordfence {
724
  return $errors;
725
  }
726
  public static function authenticateFilter($authResult){
 
727
  $IP = wfUtils::getIP();
728
  $secEnabled = wfConfig::get('loginSecurityEnabled');
729
  if($secEnabled && (! self::getLog()->isWhitelisted($IP)) && wfConfig::get('isPaid') ){
@@ -865,7 +891,9 @@ class wordfence {
865
  public static function logoutAction(){
866
  $userID = get_current_user_id();
867
  $userDat = get_user_by('id', $userID);
868
- self::getLog()->logLogin('logout', 0, $userDat->user_login);
 
 
869
  }
870
  public static function loginInitAction(){
871
  if(self::isLockedOut(wfUtils::getIP())){
@@ -1443,6 +1471,10 @@ class wordfence {
1443
  }
1444
  public static function ajax_clearPageCache_callback(){
1445
  $stats = wfCache::clearPageCache();
 
 
 
 
1446
  $body = "A total of " . $stats['filesDeleted'] . ' files were deleted and ' . $stats['dirsDeleted'] . ' directories were removed. We cleared a total of ' . $stats['totalData'] . 'KB of data in the cache.';
1447
  if($stats['totalErrors'] > 0){
1448
  $body .= ' A total of ' . $stats['totalErrors'] . ' errors were encountered. This probably means that we could not remove some of the files or directories in the cache. Please use your CPanel or file manager to remove the rest of the files in the directory: ' . WP_CONTENT_DIR . '/wfcache/';
@@ -2513,10 +2545,19 @@ EOL;
2513
  return $gen;
2514
  }
2515
  }
 
 
 
 
 
 
 
 
2516
  public static function preCommentApprovedFilter($approved, $cData){
2517
  if( $approved == 1 && (! is_user_logged_in()) && wfConfig::get('other_noAnonMemberComments') ){
2518
  $user = get_user_by('email', trim($cData['comment_author_email']));
2519
  if($user){
 
2520
  return 0; //hold for moderation if the user is not signed in but used a members email
2521
  }
2522
  }
@@ -2525,12 +2566,43 @@ EOL;
2525
  $wf = new wfScanEngine();
2526
  try {
2527
  if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
 
2528
  return 'spam';
2529
  }
2530
  } catch(Exception $e){
2531
  //This will most likely be an API exception because we can't contact the API, so we ignore it and let the normal comment mechanisms run.
2532
  }
2533
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2534
  return $approved;
2535
  }
2536
  public static function getMyHomeURL(){
@@ -2541,6 +2613,7 @@ EOL;
2541
  }
2542
 
2543
  public static function alert($subject, $alertMsg, $IP){
 
2544
  $emails = wfConfig::getAlertEmails();
2545
  if(sizeof($emails) < 1){ return; }
2546
 
28
  private static $statusStartMsgs = array();
29
  private static $debugOn = null;
30
  private static $runInstallCalled = false;
31
+ public static $commentSpamItems = array();
32
  public static function installPlugin(){
33
  self::runInstall();
34
  //Used by MU code below
126
  }
127
  }
128
  }
129
+ private static function keyAlert($msg){
130
  self::alert($msg, $msg . " To ensure uninterrupted Premium Wordfence protection on your site,\nplease renew your API key by visiting http://www.wordfence.com/ Sign in, go to your dashboard,\nselect the key about to expire and click the button to renew that API key.", false);
131
  }
132
  public static function dailyCron(){
350
  add_action('wp_ajax_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
351
  add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
352
  add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
353
+ /*
354
+ add_action('wp_dashboard_setup', 'wordfence::addDashboardWidget');
355
+ */
356
+
357
  }
358
 
359
 
422
  return $schedules;
423
  }
424
  */
425
+ /*
426
+ public static function addDashboardWidget(){
427
+ wp_add_dashboard_widget('wordfenceDashboardWidget', 'Wordfence Security Status', 'wordfence::displayDashboardWidget');
428
+ }
429
+ public static function displayDashboardWidget(){
430
+ require('dashboard.php');
431
+ }
432
+ */
433
  public static function jetpackMobileSetup(){
434
  define('WFDONOTCACHE', true); //Don't cache jetpack mobile theme pages.
435
  }
717
  public static function loginAction($username){
718
  if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
719
  if(! $username){ return; }
720
+ wfConfig::inc('totalLogins');
721
  $user = get_user_by('login', $username);
722
  $userID = $user ? $user->ID : 0;
723
  self::getLog()->logLogin('loginOK', 0, $username);
724
+ if(wfUtils::isAdmin($user)){
725
+ wfConfig::set_ser('lastAdminLogin', array(
726
+ 'userID' => $userID,
727
+ 'username' => $username,
728
+ 'firstName' => $user->first_name,
729
+ 'lastName' => $user->last_name,
730
+ 'time' => wfUtils::localHumanDateShort(),
731
+ 'IP' => wfUtils::getIP()
732
+ ));
733
+ }
734
+
735
  if(user_can($userID, 'update_core')){
736
  if(wfConfig::get('alertOn_adminLogin')){
737
  wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
749
  return $errors;
750
  }
751
  public static function authenticateFilter($authResult){
752
+ wfConfig::inc('totalLoginHits'); //The total hits to wp-login.php including logins, logouts and just hits.
753
  $IP = wfUtils::getIP();
754
  $secEnabled = wfConfig::get('loginSecurityEnabled');
755
  if($secEnabled && (! self::getLog()->isWhitelisted($IP)) && wfConfig::get('isPaid') ){
891
  public static function logoutAction(){
892
  $userID = get_current_user_id();
893
  $userDat = get_user_by('id', $userID);
894
+ if(is_object($userDat)){
895
+ self::getLog()->logLogin('logout', 0, $userDat->user_login);
896
+ }
897
  }
898
  public static function loginInitAction(){
899
  if(self::isLockedOut(wfUtils::getIP())){
1471
  }
1472
  public static function ajax_clearPageCache_callback(){
1473
  $stats = wfCache::clearPageCache();
1474
+ if($stats['error']){
1475
+ $body = "A total of " . $stats['totalErrors'] . " errors occurred while trying to clear your cache. The last error was: " . $stats['error'];
1476
+ return array('ok' => 1, 'heading' => 'Error occurred while clearing cache', 'body' => $body );
1477
+ }
1478
  $body = "A total of " . $stats['filesDeleted'] . ' files were deleted and ' . $stats['dirsDeleted'] . ' directories were removed. We cleared a total of ' . $stats['totalData'] . 'KB of data in the cache.';
1479
  if($stats['totalErrors'] > 0){
1480
  $body .= ' A total of ' . $stats['totalErrors'] . ' errors were encountered. This probably means that we could not remove some of the files or directories in the cache. Please use your CPanel or file manager to remove the rest of the files in the directory: ' . WP_CONTENT_DIR . '/wfcache/';
2545
  return $gen;
2546
  }
2547
  }
2548
+ public static function pushCommentSpamIP($m){
2549
+ if(wfUtils::isValidIP($m[1]) && strpos($m[1], '127.0.0') !== 0 ){
2550
+ self::$commentSpamItems[] = trim($m[1]);
2551
+ }
2552
+ }
2553
+ public static function pushCommentSpamHost($m){
2554
+ self::$commentSpamItems[] = trim($m[1]);
2555
+ }
2556
  public static function preCommentApprovedFilter($approved, $cData){
2557
  if( $approved == 1 && (! is_user_logged_in()) && wfConfig::get('other_noAnonMemberComments') ){
2558
  $user = get_user_by('email', trim($cData['comment_author_email']));
2559
  if($user){
2560
+ wfConfig::inc('totalSpamStopped');
2561
  return 0; //hold for moderation if the user is not signed in but used a members email
2562
  }
2563
  }
2566
  $wf = new wfScanEngine();
2567
  try {
2568
  if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
2569
+ wfConfig::inc('totalSpamStopped');
2570
  return 'spam';
2571
  }
2572
  } catch(Exception $e){
2573
  //This will most likely be an API exception because we can't contact the API, so we ignore it and let the normal comment mechanisms run.
2574
  }
2575
  }
2576
+ if(wfConfig::get('isPaid') && ($approved == 1 || $approved == 0) && wfConfig::get('advancedCommentScanning')){
2577
+ $IPs = array();
2578
+ $hosts = array();
2579
+ self::$commentSpamItems = array();
2580
+ preg_replace_callback('/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/', 'wordfence::pushCommentSpamIP', $cData['comment_content']);
2581
+ $IPs = self::$commentSpamItems;
2582
+ self::$commentSpamItems = array();
2583
+ preg_replace_callback('/https?:\/\/([a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+[a-zA-Z0-9])/i', 'wordfence::pushCommentSpamHost', $cData['comment_content']);
2584
+ $hosts = self::$commentSpamItems;
2585
+ self::$commentSpamItems = array();
2586
+ try {
2587
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2588
+ $res = $api->call('advanced_comment_scan', array(), array(
2589
+ 'author' => $cData['comment_author'],
2590
+ 'email' => $cData['comment_author_email'],
2591
+ 'URL' => $cData['comment_author_url'],
2592
+ 'commentIP' => $cData['comment_author_IP'],
2593
+ 'wfIP' => wfUtils::getIP(),
2594
+ 'hosts' => (sizeof($hosts) > 0 ? implode(',', $hosts) : ''),
2595
+ 'IPs' => (sizeof($IPs) > 0 ? implode(',', $IPs) : '')
2596
+ ));
2597
+ if(is_array($res) && isset($res['spam']) && $res['spam'] == 1){
2598
+ wfConfig::inc('totalSpamStopped');
2599
+ return 'spam';
2600
+ }
2601
+ } catch(Exception $e){
2602
+ //API server is probably down
2603
+ }
2604
+ }
2605
+ wfConfig::inc('totalCommentsFiltered');
2606
  return $approved;
2607
  }
2608
  public static function getMyHomeURL(){
2613
  }
2614
 
2615
  public static function alert($subject, $alertMsg, $IP){
2616
+ wfConfig::inc('totalAlertsSent');
2617
  $emails = wfConfig::getAlertEmails();
2618
  if(sizeof($emails) < 1){ return; }
2619
 
lib/wordfenceConstants.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- define('WORDFENCE_API_VERSION', '2.10');
3
  define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
  define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
1
  <?php
2
+ define('WORDFENCE_API_VERSION', '2.11');
3
  define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
  define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm
4
  Requires at least: 3.3.1
5
  Tested up to: 3.9.1
6
- Stable tag: 5.0.8
7
 
8
  Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
 
@@ -162,9 +162,20 @@ cause a security hole on your site.
162
 
163
  == Changelog ==
164
 
 
 
 
 
 
 
 
 
 
 
 
165
  = 5.0.8 =
166
  * Feature: Support for Jetpack Mobile Theme in Falcon Caching engine. Regular pages are cached, mobile pages are served direct to browser.
167
- * Improvement: Pages that are less than 1000 bytes will not be cached. The avg web page size in 2014 is 1246,000 bytes. Anything less than 1000 bytes is usuall an error.
168
  * Improvement: Wordfence will now request 128M on hosts instead of 64M where memory in php.ini is set too low.
169
  * Fix: Wordfence was caching 404's under certain conditions. Fixed.
170
  * Fix: Nginx/FastCGI users would sometimes receive an error about not being able to edit .htaccess. Fixed.
3
  Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm
4
  Requires at least: 3.3.1
5
  Tested up to: 3.9.1
6
+ Stable tag: 5.0.9
7
 
8
  Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
 
162
 
163
  == Changelog ==
164
 
165
+ = 5.0.9 =
166
+ * Feature: (Premium) Advanced Comment Spam Filter. Checks comment source IP, author URL and hosts and IP's in body against additional spam lists.
167
+ * Feature: (Premium) Check if your site is being Spamvertised i.e. your domain is being included in spam emails. Usually indicates you've been hacked.
168
+ * Feature: (Premium) Check if your website IP is generating spam. Checks against spam lists if your IP is a known source of spam.
169
+ * Improvement: Cache clearing errors are nown shown with clear explanations.
170
+ * Improvement: Added lightweight stats logging internally in preparation for displaying them on the admin UI in the next release.
171
+ * Fix: If a non-existent user tries to sign in it is not logged in the live logins tab. Fixed.
172
+ * Fix: Removed warning "Trying to get property of non-object" that would occur under certain conditions.
173
+ * Fix: Removed call to is_404() which was not having any effect and would issue a warning if debug mode is enabled.
174
+ * Fix: Check if CURL is installed as part of connectivity test.
175
+
176
  = 5.0.8 =
177
  * Feature: Support for Jetpack Mobile Theme in Falcon Caching engine. Regular pages are cached, mobile pages are served direct to browser.
178
+ * Improvement: Pages that are less than 1000 bytes will not be cached. The avg web page size in 2014 is 1246,000 bytes. Anything less than 1000 bytes is usually an error.
179
  * Improvement: Wordfence will now request 128M on hosts instead of 64M where memory in php.ini is set too low.
180
  * Fix: Wordfence was caching 404's under certain conditions. Fixed.
181
  * Fix: Nginx/FastCGI users would sometimes receive an error about not being able to edit .htaccess. Fixed.
wordfence.php CHANGED
@@ -4,13 +4,13 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Site Speedup
6
  Author: Wordfence
7
- Version: 5.0.8
8
  Author URI: http://www.wordfence.com/
9
  */
10
  if(defined('WP_INSTALLING') && WP_INSTALLING){
11
  return;
12
  }
13
- define('WORDFENCE_VERSION', '5.0.8');
14
  if(get_option('wordfenceActivated') != 1){
15
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
  }
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Site Speedup
6
  Author: Wordfence
7
+ Version: 5.0.9
8
  Author URI: http://www.wordfence.com/
9
  */
10
  if(defined('WP_INSTALLING') && WP_INSTALLING){
11
  return;
12
  }
13
+ define('WORDFENCE_VERSION', '5.0.9');
14
  if(get_option('wordfenceActivated') != 1){
15
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
  }