Wordfence Security – Firewall & Malware Scan - Version 2.1.5

Version Description

  • Fixed bug that caused WF to not work when certain DB caching plugins are used and override wpdb object.
  • Fixed Wordfence so activity log only shows our own errors unless in debug mode.
  • Wordfence now deletes all it's tables and deletes all saved options when you deactivate the plugin.
  • Removed all exit() on error statements. Critical errors are handled more gracefully by writing to the log instead.
  • Fixed a bug that would cause a database loop until running out of memory under certain error conditions.
  • Suppressed useless warnings that occur in environments with basedir set or where functions are disabled for security reasons.
  • Removed redundant check that executed on every request and put it in activation instead.
  • If serialization during scan breaks, exit gracefully instead of looping.
  • Disk space in log is now shown as Gigabytes and formatted nicely.
  • Removed wdie() function which is a little obnoxious. Writing to WF error log instead.
  • Fixed bug where a non-empty but useless HTTP header can break getIP() function.
  • Added useful data to error output if getIP() tells you it can't work on your system.
  • Removed option to start scan in debug because it's no longer possible with a forked scan.
  • Removed option to test process running time on a system because it breaks on most systems and confuses customers.
  • Database connection errors no longer call die() but log an error instead in a way that removes the risk of a logging loop.
  • Removed dropAll.php script because we now clean up tables on deactivate and it's not needed.
  • Updated readme to show that we support 3.4.
Download this release

Release Info

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

Code changes from version 2.1.4 to 2.1.5

lib/dropAll.php DELETED
@@ -1,8 +0,0 @@
1
- <?php
2
- require_once('wfSchema.php');
3
- if((! isset($_SERVER)) || isset($_SERVER['REQUEST_URI'])){ echo "Running under web interface. Exiting.\n"; exit(0); }
4
- if(! (isset($argv[1]) && isset($argv[2]) && isset($argv[3]) && isset($argv[4])) ){ echo "Usage: {$argv[0]} <DB username> <DB password> <DB name> <prefix>\n"; exit(); } $s = new wfSchema('localhost', $argv[1], $argv[2], $argv[3]);
5
-
6
- $s->dropAll($argv[4]);
7
-
8
- ?>
 
 
 
 
 
 
 
 
lib/menu_options.php CHANGED
@@ -208,9 +208,7 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
208
  <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>
209
  <tr><th>Enable debugging mode</th><td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn" value="1" <?php $w->cb('debugOn'); ?> /></td></tr>
210
  <tr><th colspan="2"><a href="/?_wfsf=sysinfo&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Click to view your system's configuration in a new window</a></th></tr>
211
- <tr><th colspan="2"><a href="<?php echo wfUtils::getBaseURL(); ?>wfscan.php?debugMode=1&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Start a scan in debug mode (advanced users only)</a></th></tr>
212
  <tr><th colspan="2"><a href="/?_wfsf=testmem&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Test your WordPress host's available memory</a></th></tr>
213
- <tr><th colspan="2"><a href="/?_wfsf=testtime&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Test your WordPress host's process running time (you may see a blank screen for up to 3 minutes)</a></th></tr>
214
  </table>
215
  <p><table border="0" cellpadding="0" cellspacing="0"><tr><td><input type="button" id="button1" name="button1" class="button-primary" value="Save Changes" onclick="WFAD.saveConfig();" /></td><td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg">&nbsp;Your changes have been saved!</span></td></tr></table></p>
216
  </div>
208
  <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>
209
  <tr><th>Enable debugging mode</th><td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn" value="1" <?php $w->cb('debugOn'); ?> /></td></tr>
210
  <tr><th colspan="2"><a href="/?_wfsf=sysinfo&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Click to view your system's configuration in a new window</a></th></tr>
 
211
  <tr><th colspan="2"><a href="/?_wfsf=testmem&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Test your WordPress host's available memory</a></th></tr>
 
212
  </table>
213
  <p><table border="0" cellpadding="0" cellspacing="0"><tr><td><input type="button" id="button1" name="button1" class="button-primary" value="Save Changes" onclick="WFAD.saveConfig();" /></td><td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg">&nbsp;Your changes have been saved!</span></td></tr></table></p>
214
  </div>
lib/viewFullActivityLog.php CHANGED
@@ -11,10 +11,13 @@
11
  <?php
12
  $db = new wfDB();
13
  global $wpdb;
 
14
  $table = $wpdb->base_prefix . 'wfStatus';
15
  $q = $db->query("select ctime, level, type, msg from $table order by ctime desc");
16
  while($r = mysql_fetch_assoc($q)){
17
- echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime']) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . htmlspecialchars($r['msg']) . "</div>\n";
 
 
18
  }
19
  ?>
20
  </body>
11
  <?php
12
  $db = new wfDB();
13
  global $wpdb;
14
+ $debugOn = wfConfig::get('debugOn', 0);
15
  $table = $wpdb->base_prefix . 'wfStatus';
16
  $q = $db->query("select ctime, level, type, msg from $table order by ctime desc");
17
  while($r = mysql_fetch_assoc($q)){
18
+ if($r['level'] < 4 || $debugOn){
19
+ echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime']) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . htmlspecialchars($r['msg']) . "</div>\n";
20
+ }
21
  }
22
  ?>
23
  </body>
lib/wfConfig.php CHANGED
@@ -376,7 +376,7 @@ class wfConfig {
376
  $msg = "wfConfig::set() got an array as second param with key: $key - Please report this bug. Exiting.";
377
  wfstatus(1, 'error', $msg);
378
  error_log($msg);
379
- exit(0);
380
  }
381
 
382
  self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
@@ -450,7 +450,7 @@ class wfConfig {
450
  wordfence::status(4, 'info', "Serialized data for $key is " . strlen($serialized) . " bytes and is greater than max_allowed packet so writing it to disk file: " . $fullFile);
451
  } else {
452
  wordfence::status(1, 'error', "Your database doesn't allow big packets so we have to use files to store temporary data and Wordfence can't find a place to write them. Either ask your admin to increase max_allowed_packet on your MySQL database, or make one of the following directories writable by your web server: " . implode(', ', $dirs));
453
- exit();
454
  }
455
  fwrite($fh, self::$tmpFileHeader);
456
  fwrite($fh, $serialized);
@@ -458,12 +458,12 @@ class wfConfig {
458
  return true;
459
  } else {
460
  wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. We then tried to save it to disk, but you don't have a temporary directory that is writable. You can fix this by making the /wp-content/plugins/wordfence/tmp/ directory writable by your web server. Or by increasing your max_allowed_packet configuration variable in your mysql database.");
461
- exit(0);
462
  }
463
 
464
  } else {
465
  wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. This particular variable can't be saved to disk. Please ask your administrator to increase max_allowed_packet and also report this in the Wordfence forums because it may be a bug. Thanks.");
466
- exit(0);
467
  }
468
  } else {
469
  //Delete temp files on disk or else the DB will be written to but get_ser will see files on disk and read them instead
@@ -496,17 +496,19 @@ class wfConfig {
496
  if(! self::$tmpDirCache){
497
  $dirs = array(wfUtils::getPluginBaseDir() . 'wordfence/tmp/', sys_get_temp_dir(), ABSPATH . 'wp-content/uploads/');
498
  $finalDir = 'notmp';
 
499
  foreach($dirs as $dir){
500
  $dir = rtrim($dir, '/') . '/';
501
- $fh = fopen($dir . 'wftmptest.txt', 'w');
502
  if(! $fh){ continue; }
503
- $bytes = fwrite($fh, 'test');
504
- if($bytes != 4){ fclose($fh); continue; }
505
- fclose($fh);
506
- if(! unlink($dir . 'wftmptest.txt')){ continue; }
507
  $finalDir = $dir;
508
  break;
509
  }
 
510
  self::$tmpDirCache = $finalDir;
511
  }
512
  if(self::$tmpDirCache == 'notmp'){
376
  $msg = "wfConfig::set() got an array as second param with key: $key - Please report this bug. Exiting.";
377
  wfstatus(1, 'error', $msg);
378
  error_log($msg);
379
+ return;
380
  }
381
 
382
  self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
450
  wordfence::status(4, 'info', "Serialized data for $key is " . strlen($serialized) . " bytes and is greater than max_allowed packet so writing it to disk file: " . $fullFile);
451
  } else {
452
  wordfence::status(1, 'error', "Your database doesn't allow big packets so we have to use files to store temporary data and Wordfence can't find a place to write them. Either ask your admin to increase max_allowed_packet on your MySQL database, or make one of the following directories writable by your web server: " . implode(', ', $dirs));
453
+ return false;
454
  }
455
  fwrite($fh, self::$tmpFileHeader);
456
  fwrite($fh, $serialized);
458
  return true;
459
  } else {
460
  wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. We then tried to save it to disk, but you don't have a temporary directory that is writable. You can fix this by making the /wp-content/plugins/wordfence/tmp/ directory writable by your web server. Or by increasing your max_allowed_packet configuration variable in your mysql database.");
461
+ return false;
462
  }
463
 
464
  } else {
465
  wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. This particular variable can't be saved to disk. Please ask your administrator to increase max_allowed_packet and also report this in the Wordfence forums because it may be a bug. Thanks.");
466
+ return false;
467
  }
468
  } else {
469
  //Delete temp files on disk or else the DB will be written to but get_ser will see files on disk and read them instead
496
  if(! self::$tmpDirCache){
497
  $dirs = array(wfUtils::getPluginBaseDir() . 'wordfence/tmp/', sys_get_temp_dir(), ABSPATH . 'wp-content/uploads/');
498
  $finalDir = 'notmp';
499
+ wfUtils::errorsOff();
500
  foreach($dirs as $dir){
501
  $dir = rtrim($dir, '/') . '/';
502
+ $fh = @fopen($dir . 'wftmptest.txt', 'w');
503
  if(! $fh){ continue; }
504
+ $bytes = @fwrite($fh, 'test');
505
+ if($bytes != 4){ @fclose($fh); continue; }
506
+ @fclose($fh);
507
+ if(! @unlink($dir . 'wftmptest.txt')){ continue; }
508
  $finalDir = $dir;
509
  break;
510
  }
511
+ wfUtils::errorsOn();
512
  self::$tmpDirCache = $finalDir;
513
  }
514
  if(self::$tmpDirCache == 'notmp'){
lib/wfDB.php CHANGED
@@ -16,21 +16,32 @@ class wfDB {
16
  } else {
17
  global $wpdb;
18
  if(! $wpdb){
19
- self::wfdie("The WordPress variable wpdb is not defined.");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
- if(! $wpdb->dbhost ){ self::wfdie("The WordPress variable from wpdb dbhost is not defined."); }
22
- if(! $wpdb->dbuser ){ self::wfdie("The WordPress variable from wpdb dbuser is not defined."); }
23
- if(! isset($wpdb->dbpassword) ){ self::wfdie("The WordPress variable from wpdb dbpassword is not defined."); }
24
- if(! $wpdb->dbname ){ self::wfdie("The WordPress variable from wpdb dbname is not defined."); }
25
- $this->dbhost = $wpdb->dbhost;
26
- $this->dbuser = $wpdb->dbuser;
27
- $this->dbpassword = $wpdb->dbpassword;
28
- $this->dbname = $wpdb->dbname;
29
  }
30
  if($createNewHandle){
31
  $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
32
  if($dbh === false){
33
- self::wfdie("Could not connect to database on " . $this->dbhost . " with user " . $this->dbuser . ' : ' . mysql_error());
 
34
  }
35
  mysql_select_db($this->dbname, $dbh);
36
  $this->dbh = $dbh;
@@ -45,7 +56,8 @@ class wfDB {
45
  } else {
46
  $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
47
  if($dbh === false){
48
- self::wfdie("Could not connect to database on " . $this->dbhost . " with user " . $this->dbuser . ' : ' . mysql_error());
 
49
  }
50
 
51
  mysql_select_db($this->dbname, $dbh);
@@ -66,16 +78,32 @@ class wfDB {
66
  } else if(sizeof($args) > 1){
67
  $query = call_user_func_array('sprintf', $args);
68
  } else {
69
- wfdie("No arguments passed to querySingle()");
70
  }
71
  $res = mysql_query($query, $this->dbh);
72
- $err = mysql_error();
73
- if( (! preg_match('/Wordfence DB error/i', $query)) && $err){ //prevent loops
74
- $this->errorMsg = $err;
75
- $trace=debug_backtrace(); $caller=array_shift($trace); wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
76
- }
77
  return mysql_fetch_assoc($res); //returns false if no rows found
78
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  public function querySingle(){
80
  $this->errorMsg = false;
81
  $args = func_get_args();
@@ -87,14 +115,10 @@ class wfDB {
87
  }
88
  $query = call_user_func_array('sprintf', $args);
89
  } else {
90
- wfdie("No arguments passed to querySingle()");
91
  }
92
  $res = mysql_query($query, $this->dbh);
93
- $err = mysql_error();
94
- if( (! preg_match('/Wordfence DB error/i', $query)) && $err){
95
- $this->errorMsg = $err;
96
- $trace=debug_backtrace(); $caller=array_shift($trace); wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
97
- }
98
  if(! $res){
99
  return false;
100
  }
@@ -120,13 +144,9 @@ class wfDB {
120
  }
121
  $res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
122
  } else {
123
- wfdie("No arguments passed to query()");
124
- }
125
- $err = mysql_error();
126
- if( (! $isStatusQuery) && $err){ //isStatusQuery prevents loops if status itself is causing error
127
- $this->errorMsg = $err;
128
- $trace=debug_backtrace(); $caller=array_shift($trace); wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
129
  }
 
130
  return $res;
131
  }
132
  public function queryIgnoreError(){ //sprintfString, arguments
@@ -140,14 +160,15 @@ class wfDB {
140
  }
141
  $res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
142
  } else {
143
- wfdie("No arguments passed to query()");
144
  }
145
  return $res;
146
  }
147
 
148
- private function wfdie($msg){
149
- error_log("Wordfence critical database error: $msg");
150
- exit(1);
 
151
  }
152
  public function createKeyIfNotExists($table, $col, $keyName){
153
  global $wpdb; $prefix = $wpdb->base_prefix;
16
  } else {
17
  global $wpdb;
18
  if(! $wpdb){
19
+ self::criticalError("The WordPress variable wpdb is not defined. Wordfence can't function without this being defined as it is in all standard WordPress installs.");
20
+ return;
21
+ }
22
+ $sources = array(
23
+ array('dbhost', 'DB_HOST'),
24
+ array('dbuser', 'DB_USER'),
25
+ array('dbpassword', 'DB_PASSWORD'),
26
+ array('dbname', 'DB_NAME')
27
+ );
28
+ foreach($sources as $src){
29
+ $prop = $src[0];
30
+ if(isset($wpdb->$prop)){
31
+ $this->$prop = $wpdb->$prop;
32
+ } else if(defined($src[1])){
33
+ $this->$prop = constant($src[1]);
34
+ } else {
35
+ self::criticalError("Wordfence DB connect error. wpdb.$prop is not set and " . $src[1] . " is not defined.");
36
+ return;
37
+ }
38
  }
 
 
 
 
 
 
 
 
39
  }
40
  if($createNewHandle){
41
  $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
42
  if($dbh === false){
43
+ self::criticalError("Wordfence could not connect to your database. Error was: " . mysql_error());
44
+ return;
45
  }
46
  mysql_select_db($this->dbname, $dbh);
47
  $this->dbh = $dbh;
56
  } else {
57
  $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
58
  if($dbh === false){
59
+ self::criticalError("Wordfence could not connect to your database. The error was: " . mysql_error());
60
+ return;
61
  }
62
 
63
  mysql_select_db($this->dbname, $dbh);
78
  } else if(sizeof($args) > 1){
79
  $query = call_user_func_array('sprintf', $args);
80
  } else {
81
+ $this->handleError("No arguments passed to querySingle()");
82
  }
83
  $res = mysql_query($query, $this->dbh);
84
+ $this->handleError();
 
 
 
 
85
  return mysql_fetch_assoc($res); //returns false if no rows found
86
  }
87
+ public function handleError($err = false){
88
+ if(! $err){
89
+ $err = mysql_error();
90
+ }
91
+ if($err){
92
+ $trace=debug_backtrace();
93
+ $first=array_shift($trace);
94
+ $caller=array_shift($trace);
95
+ $msg = "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err";
96
+ global $wpdb;
97
+ $statusTable = $wpdb->base_prefix . 'wfStatus';
98
+ mysql_query(sprintf("insert into " . $statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')",
99
+ mysql_real_escape_string(sprintf('%.6f', microtime(true))),
100
+ mysql_real_escape_string(1),
101
+ mysql_real_escape_string('error'),
102
+ mysql_real_escape_string($msg)), $this->dbh);
103
+ error_log($msg);
104
+ return;
105
+ }
106
+ }
107
  public function querySingle(){
108
  $this->errorMsg = false;
109
  $args = func_get_args();
115
  }
116
  $query = call_user_func_array('sprintf', $args);
117
  } else {
118
+ $this->handleError("No arguments passed to querySingle()");
119
  }
120
  $res = mysql_query($query, $this->dbh);
121
+ $this->handleError();
 
 
 
 
122
  if(! $res){
123
  return false;
124
  }
144
  }
145
  $res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
146
  } else {
147
+ $this->handleError("No arguments passed to query()");
 
 
 
 
 
148
  }
149
+ $this->handleError();
150
  return $res;
151
  }
152
  public function queryIgnoreError(){ //sprintfString, arguments
160
  }
161
  $res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
162
  } else {
163
+ $this->handleError("No arguments passed to query()");
164
  }
165
  return $res;
166
  }
167
 
168
+ private static function criticalError($msg){
169
+ $msg = "Wordfence critical database error: $msg";
170
+ error_log($msg);
171
+ return;
172
  }
173
  public function createKeyIfNotExists($table, $col, $keyName){
174
  global $wpdb; $prefix = $wpdb->base_prefix;
lib/wfLog.php CHANGED
@@ -55,7 +55,8 @@ class wfLog {
55
  } else if($type == 'hit'){
56
  $table = $this->leechTable;
57
  } else {
58
- wfUtils::wdie("Invalid type to logLeechAndBlock(): $type");
 
59
  }
60
  $IP = wfUtils::getIP();
61
  $this->getDB()->query("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_aton($IP));
@@ -254,7 +255,8 @@ class wfLog {
254
  } else if($type == 'topLeechers'){
255
  $table = $this->leechTable;
256
  } else {
257
- wfUtils::wdie("Invalid type to getLeechers(): $type");
 
258
  }
259
  $res = $this->getDB()->query("select IP, sum(hits) as totalHits from $table where eMin > ((unix_timestamp() - 86400) / 60) group by IP order by totalHits desc limit 20");
260
  $results = array();
@@ -313,7 +315,8 @@ class wfLog {
313
  } else if($type == 'ruser'){
314
  $typeSQL = " and userID > 0 ";
315
  } else {
316
- wfUtils::wdie("Invalid log type to wfLog: $type");
 
317
  }
318
 
319
  $r1 = $this->getDB()->query("select * from " . $this->hitsTable . " where ctime > %f $IPSQL $typeSQL order by ctime desc limit %s",
@@ -328,7 +331,8 @@ class wfLog {
328
  );
329
 
330
  } else {
331
- wfUtils::wdie("getHits got invalid hitType: $hitType");
 
332
  }
333
  $results = array();
334
  while($res = mysql_fetch_assoc($r1)){
55
  } else if($type == 'hit'){
56
  $table = $this->leechTable;
57
  } else {
58
+ wordfence::status(1, 'error', "Invalid type to logLeechAndBlock(): $type");
59
+ return;
60
  }
61
  $IP = wfUtils::getIP();
62
  $this->getDB()->query("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_aton($IP));
255
  } else if($type == 'topLeechers'){
256
  $table = $this->leechTable;
257
  } else {
258
+ wordfence::status(1, 'error', "Invalid type to getLeechers(): $type");
259
+ return false;
260
  }
261
  $res = $this->getDB()->query("select IP, sum(hits) as totalHits from $table where eMin > ((unix_timestamp() - 86400) / 60) group by IP order by totalHits desc limit 20");
262
  $results = array();
315
  } else if($type == 'ruser'){
316
  $typeSQL = " and userID > 0 ";
317
  } else {
318
+ wordfence::status(1, 'error', "Invalid log type to wfLog: $type");
319
+ return false;
320
  }
321
 
322
  $r1 = $this->getDB()->query("select * from " . $this->hitsTable . " where ctime > %f $IPSQL $typeSQL order by ctime desc limit %s",
331
  );
332
 
333
  } else {
334
+ wordfence::status(1, 'error', "getHits got invalid hitType: $hitType");
335
+ return false;
336
  }
337
  $results = array();
338
  while($res = mysql_fetch_assoc($r1)){
lib/wfScanEngine.php CHANGED
@@ -89,9 +89,10 @@ class wfScanEngine {
89
  }
90
  }
91
  public function fork(){
92
- wfConfig::set_ser('wfsd_engine', $this, true);
93
- wfUtils::clearScanLock();
94
- self::startScan(true);
 
95
  exit(0);
96
  }
97
  public function emailNewIssues(){
@@ -625,13 +626,15 @@ class wfScanEngine {
625
  }
626
  private function scan_diskSpace(){
627
  $this->statusIDX['diskSpace'] = wordfence::statusStart("Scanning to check available disk space");
628
- $total = disk_total_space('.');
629
- $free = disk_free_space('.');
630
- $this->status(2, 'info', "Total space: $total Free space: $free");
 
631
  if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
632
  wordfence::statusEnd($this->statusIDX['diskSpace'], false);
633
  return;
634
  }
 
635
  $level = false;
636
  $spaceLeft = sprintf('%.2f', ($free / $total * 100));
637
  $this->status(2, 'info', "The disk has $spaceLeft percent space available");
89
  }
90
  }
91
  public function fork(){
92
+ if(wfConfig::set_ser('wfsd_engine', $this, true)){
93
+ wfUtils::clearScanLock();
94
+ self::startScan(true);
95
+ } //Otherwise there was an error so don't start another scan.
96
  exit(0);
97
  }
98
  public function emailNewIssues(){
626
  }
627
  private function scan_diskSpace(){
628
  $this->statusIDX['diskSpace'] = wordfence::statusStart("Scanning to check available disk space");
629
+ wfUtils::errorsOff();
630
+ $total = @disk_total_space('.');
631
+ $free = @disk_free_space('.');
632
+ wfUtils::errorsOn();
633
  if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
634
  wordfence::statusEnd($this->statusIDX['diskSpace'], false);
635
  return;
636
  }
637
+ $this->status(2, 'info', "Total disk space: " . sprintf('%.4f', ($total / 1024 / 1024 / 1024)) . "GB -- Free disk space: " . sprintf('%.4f', ($free / 1024 / 1024 / 1024)) . "GB");
638
  $level = false;
639
  $spaceLeft = sprintf('%.2f', ($free / $total * 100));
640
  $this->status(2, 'info', "The disk has $spaceLeft percent space available");
lib/wfSchema.php CHANGED
@@ -157,9 +157,9 @@ class wfSchema {
157
  $this->prefix = $wpdb->base_prefix;
158
  }
159
  }
160
- public function dropAll($prefix){
161
  foreach($this->tables as $table => $def){
162
- $this->db->query("drop table if exists " . $prefix . $table);
163
  }
164
  }
165
  public function createAll(){
157
  $this->prefix = $wpdb->base_prefix;
158
  }
159
  }
160
+ public function dropAll(){
161
  foreach($this->tables as $table => $def){
162
+ $this->db->query("drop table if exists " . $this->prefix . $table);
163
  }
164
  }
165
  public function createAll(){
lib/wfUtils.php CHANGED
@@ -3,6 +3,8 @@ require_once('wfConfig.php');
3
  class wfUtils {
4
  private static $isWindows = false;
5
  public static $scanLockFH = false;
 
 
6
  public static function makeTimeAgo($secs, $noSeconds = false) {
7
  if($secs < 1){
8
  return "a moment";
@@ -72,27 +74,41 @@ class wfUtils {
72
  //return ABSPATH . 'wp-content/plugins/';
73
  }
74
  public static function getIP(){
75
- $ip = 0;
76
  if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
77
- $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
78
  }
79
- if((! $ip) && isset($_SERVER['REMOTE_ADDR'])){
80
- $ip = $_SERVER['REMOTE_ADDR'];
81
  }
82
- if(preg_match('/,/', $ip)){
83
- $parts = explode(',', $ip);
84
- $ip = trim($parts[0]);
85
  }
86
- if(preg_match('/:\d+$/', $ip)){
87
- $ip = preg_replace('/:\d+$/', '', $ip);
88
  }
89
- if(self::isValidIP($ip)){
90
- return $ip;
91
  } else {
92
- $msg = "Wordfence is not able to determine the IP addresses of visitors to your site and can't operate. We received IP: $ip from header1: " . $_SERVER['HTTP_X_FORWARDED_FOR'] . " and header2: " . $_SERVER['REMOTE_ADDR'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  wordfence::status(1, 'error', $msg);
94
  error_log($msg);
95
- exit(0);
96
  }
97
  }
98
  public static function isValidIP($IP){
@@ -115,11 +131,6 @@ class wfUtils {
115
  public static function editUserLink($userID){
116
  return get_admin_url() . 'user-edit.php?user_id=' . $userID;
117
  }
118
- public static function wdie($err){
119
- $trace=debug_backtrace(); $caller=array_shift($trace);
120
- error_log("Wordfence error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
121
- exit();
122
- }
123
  public static function tmpl($file, $data){
124
  extract($data);
125
  ob_start();
@@ -132,8 +143,8 @@ class wfUtils {
132
  public static function encrypt($str){
133
  $key = wfConfig::get('encKey');
134
  if(! $key){
135
- error_log("Wordfence error: No encryption key found!");
136
- exit();
137
  }
138
  $db = new wfDB();
139
  return $db->querySingle("select HEX(AES_ENCRYPT('%s', '%s')) as val", $str, $key);
@@ -141,8 +152,8 @@ class wfUtils {
141
  public static function decrypt($str){
142
  $key = wfConfig::get('encKey');
143
  if(! $key){
144
- error_log("Wordfence error: No encryption key found!");
145
- exit();
146
  }
147
  $db = new wfDB();
148
  return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
@@ -362,6 +373,18 @@ class wfUtils {
362
  return $host;
363
  }
364
  }
 
 
 
 
 
 
 
 
 
 
 
 
365
  }
366
 
367
 
3
  class wfUtils {
4
  private static $isWindows = false;
5
  public static $scanLockFH = false;
6
+ private static $lastErrorReporting = false;
7
+ private static $lastDisplayErrors = false;
8
  public static function makeTimeAgo($secs, $noSeconds = false) {
9
  if($secs < 1){
10
  return "a moment";
74
  //return ABSPATH . 'wp-content/plugins/';
75
  }
76
  public static function getIP(){
77
+ $IP = 0;
78
  if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
79
+ $IP = $_SERVER['HTTP_X_FORWARDED_FOR'];
80
  }
81
+ if((! preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)) && isset($_SERVER['REMOTE_ADDR'])){
82
+ $IP = $_SERVER['REMOTE_ADDR'];
83
  }
84
+ if(preg_match('/,/', $IP)){
85
+ $parts = explode(',', $IP);
86
+ $IP = trim($parts[0]);
87
  }
88
+ if(preg_match('/:\d+$/', $IP)){
89
+ $IP = preg_replace('/:\d+$/', '', $IP);
90
  }
91
+ if(self::isValidIP($IP)){
92
+ return $IP;
93
  } else {
94
+ $msg = "Wordfence can't get the IP of clients and therefore can't operate. We received IP: $IP. X-Forwarded-For was: " . $_SERVER['HTTP_X_FORWARDED_FOR'] . " REMOTE_ADDR was: " . $_SERVER['REMOTE_ADDR'];
95
+ $possible = array();
96
+ foreach($_SERVER as $key => $val){
97
+ if(preg_match('/^\d+\.\d+\.\d+\.\d+/', $val) && strlen($val) < 255){
98
+ if($val != '127.0.0.1'){
99
+ $possible[$key] = $val;
100
+ }
101
+ }
102
+ }
103
+ if(sizeof($possible) > 0){
104
+ $msg .= " Report the following on the Wordfence forums and they may be able to help. Headers that may contain the client IP: ";
105
+ foreach($possible as $key => $val){
106
+ $msg .= "$key => $val ";
107
+ }
108
+ }
109
  wordfence::status(1, 'error', $msg);
110
  error_log($msg);
111
+ return false;
112
  }
113
  }
114
  public static function isValidIP($IP){
131
  public static function editUserLink($userID){
132
  return get_admin_url() . 'user-edit.php?user_id=' . $userID;
133
  }
 
 
 
 
 
134
  public static function tmpl($file, $data){
135
  extract($data);
136
  ob_start();
143
  public static function encrypt($str){
144
  $key = wfConfig::get('encKey');
145
  if(! $key){
146
+ wordfence::status(1, 'error', "Wordfence error: No encryption key found!");
147
+ return false;
148
  }
149
  $db = new wfDB();
150
  return $db->querySingle("select HEX(AES_ENCRYPT('%s', '%s')) as val", $str, $key);
152
  public static function decrypt($str){
153
  $key = wfConfig::get('encKey');
154
  if(! $key){
155
+ wordfence::status(1, 'error', "Wordfence error: No encryption key found!");
156
+ return false;
157
  }
158
  $db = new wfDB();
159
  return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
373
  return $host;
374
  }
375
  }
376
+ public static function errorsOff(){
377
+ self::$lastErrorReporting = @ini_get('error_reporting');
378
+ @error_reporting(0);
379
+ self::$lastDisplayErrors = @ini_get('display_errors');
380
+ @ini_set('display_errors', 0);
381
+ if(class_exists('wfScan')){ wfScan::$errorHandlingOn = false; }
382
+ }
383
+ public static function errorsOn(){
384
+ @error_reporting(self::$lastErrorReporting);
385
+ @ini_set('display_errors', self::$lastDisplayErrors);
386
+ if(class_exists('wfScan')){ wfScan::$errorHandlingOn = true; }
387
+ }
388
  }
389
 
390
 
lib/wordfenceClass.php CHANGED
@@ -34,6 +34,11 @@ class wordfence {
34
  wp_clear_scheduled_hook('wordfence_daily_cron');
35
  wp_clear_scheduled_hook('wordfence_hourly_cron');
36
  wp_clear_scheduled_hook('wordfence_scheduled_scan');
 
 
 
 
 
37
  }
38
  public static function hourlyCron(){
39
  global $wpdb; $p = $wpdb->base_prefix;
@@ -187,7 +192,7 @@ class wordfence {
187
  $db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
188
  $db->queryIgnoreError("alter table $prefix"."wfBlocks add column permanent tinyint UNSIGNED default 0");
189
  $db->queryIgnoreError("alter table $prefix"."wfStatus modify column msg varchar(1000) NOT NULL");
190
-
191
  //Must be the final line
192
  update_option('wordfence_version', WORDFENCE_VERSION);
193
  }
@@ -202,12 +207,6 @@ class wordfence {
202
  if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!)
203
  }
204
 
205
- //Upgrading from 2.0.3 we changed isPaid from 'free' or 'paid' to true and false
206
- if(wfConfig::get('isPaid') == 'free'){
207
- wfConfig::set('isPaid', '');
208
- }
209
- //end
210
-
211
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
212
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
213
  add_action('plugins_loaded', 'wordfence::veryFirstAction');
34
  wp_clear_scheduled_hook('wordfence_daily_cron');
35
  wp_clear_scheduled_hook('wordfence_hourly_cron');
36
  wp_clear_scheduled_hook('wordfence_scheduled_scan');
37
+ $schema = new wfSchema();
38
+ $schema->dropAll();
39
+ foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
40
+ delete_option($opt);
41
+ }
42
  }
43
  public static function hourlyCron(){
44
  global $wpdb; $p = $wpdb->base_prefix;
192
  $db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
193
  $db->queryIgnoreError("alter table $prefix"."wfBlocks add column permanent tinyint UNSIGNED default 0");
194
  $db->queryIgnoreError("alter table $prefix"."wfStatus modify column msg varchar(1000) NOT NULL");
195
+
196
  //Must be the final line
197
  update_option('wordfence_version', WORDFENCE_VERSION);
198
  }
207
  if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!)
208
  }
209
 
 
 
 
 
 
 
210
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
211
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
212
  add_action('plugins_loaded', 'wordfence::veryFirstAction');
lib/wordfenceHash.php CHANGED
@@ -26,7 +26,7 @@ class wordfenceHash {
26
  $path .= '/';
27
  }
28
  if(! is_readable($path)){
29
- wordfence::status(1, 'error', "Could not read directory $path to do sacn.");
30
  exit();
31
  }
32
  $files = scandir($path);
26
  $path .= '/';
27
  }
28
  if(! is_readable($path)){
29
+ wordfence::status(1, 'error', "Could not read directory $path to do scan.");
30
  exit();
31
  }
32
  $files = scandir($path);
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: mmaunder
3
  Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
4
  Requires at least: 3.3.1
5
- Tested up to: 3.3.2
6
- Stable tag: 2.1.4
7
 
8
  Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
9
 
@@ -152,6 +152,25 @@ or a theme, because often these have been updated to fix a security hole.
152
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
153
 
154
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  = 2.1.4 =
156
  * Fixed registered users not appearing in live traffic.
157
  * Fixed temp file deletion bug that caused warnings and loops.
2
  Contributors: mmaunder
3
  Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
4
  Requires at least: 3.3.1
5
+ Tested up to: 3.4
6
+ Stable tag: 2.1.5
7
 
8
  Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
9
 
152
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
153
 
154
  == Changelog ==
155
+ = 2.1.5 =
156
+ * Fixed bug that caused WF to not work when certain DB caching plugins are used and override wpdb object.
157
+ * Fixed Wordfence so activity log only shows our own errors unless in debug mode.
158
+ * Wordfence now deletes all it's tables and deletes all saved options when you deactivate the plugin.
159
+ * Removed all exit() on error statements. Critical errors are handled more gracefully by writing to the log instead.
160
+ * Fixed a bug that would cause a database loop until running out of memory under certain error conditions.
161
+ * Suppressed useless warnings that occur in environments with basedir set or where functions are disabled for security reasons.
162
+ * Removed redundant check that executed on every request and put it in activation instead.
163
+ * If serialization during scan breaks, exit gracefully instead of looping.
164
+ * Disk space in log is now shown as Gigabytes and formatted nicely.
165
+ * Removed wdie() function which is a little obnoxious. Writing to WF error log instead.
166
+ * Fixed bug where a non-empty but useless HTTP header can break getIP() function.
167
+ * Added useful data to error output if getIP() tells you it can't work on your system.
168
+ * Removed option to start scan in debug because it's no longer possible with a forked scan.
169
+ * Removed option to test process running time on a system because it breaks on most systems and confuses customers.
170
+ * Database connection errors no longer call die() but log an error instead in a way that removes the risk of a logging loop.
171
+ * Removed dropAll.php script because we now clean up tables on deactivate and it's not needed.
172
+ * Updated readme to show that we support 3.4.
173
+
174
  = 2.1.4 =
175
  * Fixed registered users not appearing in live traffic.
176
  * Fixed temp file deletion bug that caused warnings and loops.
wfscan.php CHANGED
@@ -23,6 +23,7 @@ require_once('lib/wfScanEngine.php');
23
 
24
  class wfScan {
25
  public static $debugMode = false;
 
26
  public static function wfScanMain(){
27
  $db = new wfDB();
28
  if($db->errorMsg){
@@ -112,7 +113,14 @@ class wfScan {
112
  }
113
  }
114
  public static function error_handler($errno, $errstr, $errfile, $errline){
115
- wordfence::status(1, 'error', "$errstr ($errno) File: $errfile Line: $errline");
 
 
 
 
 
 
 
116
  }
117
  public static function shutdown(){
118
  wfUtils::clearScanLock();
23
 
24
  class wfScan {
25
  public static $debugMode = false;
26
+ public static $errorHandlingOn = true;
27
  public static function wfScanMain(){
28
  $db = new wfDB();
29
  if($db->errorMsg){
113
  }
114
  }
115
  public static function error_handler($errno, $errstr, $errfile, $errline){
116
+ if(self::$errorHandlingOn){
117
+ if(preg_match('/wordfence\//', $errfile)){
118
+ $level = 1; //It's one of our files, so level 1
119
+ } else {
120
+ $level = 4; //It's someone elses plugin so only show if debug is enabled
121
+ }
122
+ wordfence::status($level, 'error', "$errstr ($errno) File: $errfile Line: $errline");
123
+ }
124
  }
125
  public static function shutdown(){
126
  wfUtils::clearScanLock();
wordfence.php CHANGED
@@ -4,10 +4,10 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://wordfence.com/
5
  Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
  Author: Mark Maunder
7
- Version: 2.1.4
8
  Author URI: http://wordfence.com/
9
  */
10
- define('WORDFENCE_VERSION', '2.1.4');
11
  if(! defined('WORDFENCE_VERSIONONLY_MODE')){
12
  require_once('lib/wordfenceConstants.php');
13
  require_once('lib/wordfenceClass.php');
4
  Plugin URI: http://wordfence.com/
5
  Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
  Author: Mark Maunder
7
+ Version: 2.1.5
8
  Author URI: http://wordfence.com/
9
  */
10
+ define('WORDFENCE_VERSION', '2.1.5');
11
  if(! defined('WORDFENCE_VERSIONONLY_MODE')){
12
  require_once('lib/wordfenceConstants.php');
13
  require_once('lib/wordfenceClass.php');