Wordfence Security – Firewall & Malware Scan - Version 6.1.5

Version Description

  • Fix: WordPress language files no longer flagged as changed.
  • Improvement: Accept wildcards in "Immediately block IP's that access these URLs."
  • Fix: Fixed bug when multiple authors have published posts, /?author=N scans show an author archive page.
  • Fix: Fixed issue with IPv6 mapped IPv4 addresses not being treated as IPv4.
  • Improvement: Added WordPress version and various constants to Diagnostics report.
  • Fix: Fixed bug with Windows users unable to save Firewall config.
  • Improvement: Include option for IIS on Windows in Firewall config process, and recommend manual php.ini change only.
  • Fix: Made the 'administrator email address' admin notice dismissable.
Download this release

Release Info

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

Code changes from version 6.1.4 to 6.1.5

css/main.css CHANGED
@@ -518,7 +518,7 @@ h3.wfConfigHeading {
518
  .wfOnOffSwitch-checkbox:checked + .wfOnOffSwitch-label .wfOnOffSwitch-switch {
519
  right: 0 !important ;
520
  }
521
- #wordfenceConfigWarning {
522
  clear: left;
523
  margin-top: 5px;
524
  }
518
  .wfOnOffSwitch-checkbox:checked + .wfOnOffSwitch-label .wfOnOffSwitch-switch {
519
  right: 0 !important ;
520
  }
521
+ #wordfenceConfigWarning, #wordfenceAdminEmailWarning {
522
  clear: left;
523
  margin-top: 5px;
524
  }
js/tourTip.js CHANGED
@@ -25,6 +25,14 @@ window['wordfenceExt'] = {
25
  function(){ jQuery('#wordfenceAutoUpdateChoice').fadeOut(); }
26
  );
27
  },
 
 
 
 
 
 
 
 
28
  removeFromCache: function(postID){
29
  this.ajax('wordfence_removeFromCache', {
30
  id: postID
25
  function(){ jQuery('#wordfenceAutoUpdateChoice').fadeOut(); }
26
  );
27
  },
28
+ adminEmailChoice: function(choice) {
29
+ this.ajax('wordfence_adminEmailChoice', {
30
+ choice: choice
31
+ },
32
+ function(res){ jQuery('#wordfenceAdminEmailWarning').fadeOut(); },
33
+ function(){ jQuery('#wordfenceAdminEmailWarning').fadeOut(); }
34
+ );
35
+ },
36
  removeFromCache: function(postID){
37
  this.ajax('wordfence_removeFromCache', {
38
  id: postID
lib/menu_diagnostic.php CHANGED
@@ -87,6 +87,96 @@ $w = new wfConfig();
87
  </tr>
88
  </tbody>
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  <tbody class="thead">
91
  <tr>
92
  <th colspan="<?php echo $cols ?>">WordPress Plugins</th>
87
  </tr>
88
  </tbody>
89
 
90
+ <tbody class="thead">
91
+ <tr>
92
+ <th colspan="<?php echo $cols ?>">WordPress</th>
93
+ </tr>
94
+ </tbody>
95
+ <tbody>
96
+ <?php
97
+ require(ABSPATH . 'wp-includes/version.php');
98
+ $postRevisions = (defined('WP_POST_REVISIONS') ? WP_POST_REVISIONS : true);
99
+ $wordPressValues = array(
100
+ 'WordPress Version' => array('description' => '', 'value' => $wp_version),
101
+ 'WP_DEBUG' => array('description' => 'WordPress debug mode', 'value' => (defined('WP_DEBUG') && WP_DEBUG ? 'On' : 'Off')),
102
+ 'WP_DEBUG_LOG' => array('description' => 'WordPress error logging override', 'value' => defined('WP_DEBUG_LOG') ? (WP_DEBUG_LOG ? 'Enabled' : 'Disabled') : '(not set)'),
103
+ 'WP_DEBUG_DISPLAY' => array('description' => 'WordPress error display override', 'value' => defined('WP_DEBUG_DISPLAY') ? (WP_DEBUG_LOG ? 'Enabled' : 'Disabled') : '(not set)'),
104
+ 'SCRIPT_DEBUG' => array('description' => 'WordPress script debug mode', 'value' => (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? 'On' : 'Off')),
105
+ 'SAVEQUERIES' => array('description' => 'WordPress query debug mode', 'value' => (defined('SAVEQUERIES') && SAVEQUERIES ? 'On' : 'Off')),
106
+ 'DB_CHARSET' => 'Database character set',
107
+ 'DB_COLLATE' => 'Database collation',
108
+ 'WP_SITEURL' => 'Explicitly set site URL',
109
+ 'WP_HOME' => 'Explicitly set blog URL',
110
+ 'WP_CONTENT_DIR' => array('description' => '"wp-content" folder is in default location', 'value' => (realpath(WP_CONTENT_DIR) === realpath(ABSPATH . 'wp-content') ? 'Yes' : 'No')),
111
+ 'WP_CONTENT_URL' => 'URL to the "wp-content" folder',
112
+ 'WP_PLUGIN_DIR' => array('description' => '"plugins" folder is in default location', 'value' => (realpath(WP_PLUGIN_DIR) === realpath(ABSPATH . 'wp-content/plugins') ? 'Yes' : 'No')),
113
+ 'WP_LANG_DIR' => array('description' => '"languages" folder is in default location', 'value' => (realpath(WP_LANG_DIR) === realpath(ABSPATH . 'wp-content/languages') ? 'Yes' : 'No')),
114
+ 'WPLANG' => 'Language choice',
115
+ 'UPLOADS' => 'Custom upload folder location',
116
+ 'TEMPLATEPATH' => array('description' => 'Theme template folder override', 'value' => (defined('TEMPLATEPATH') && realpath(get_template_directory()) !== realpath(TEMPLATEPATH) ? 'Overridden' : '(not set)')),
117
+ 'STYLESHEETPATH' => array('description' => 'Theme stylesheet folder override', 'value' => (defined('STYLESHEETPATH') && realpath(get_stylesheet_directory()) !== realpath(STYLESHEETPATH) ? 'Overridden' : '(not set)')),
118
+ 'AUTOSAVE_INTERVAL' => 'Post editing automatic saving interval',
119
+ 'WP_POST_REVISIONS' => array('description' => 'Post revisions saved by WordPress', 'value' => is_numeric($postRevisions) ? $postRevisions : ($postRevisions ? 'Unlimited' : 'None')),
120
+ 'COOKIE_DOMAIN' => 'WordPress cookie domain',
121
+ 'COOKIEPATH' => 'WordPress cookie path',
122
+ 'SITECOOKIEPATH' => 'WordPress site cookie path',
123
+ 'ADMIN_COOKIE_PATH' => 'WordPress admin cookie path',
124
+ 'PLUGINS_COOKIE_PATH' => 'WordPress plugins cookie path',
125
+ 'WP_ALLOW_MULTISITE' => array('description' => 'Multisite/network ability enabled', 'value' => (defined('WP_ALLOW_MULTISITE') && WP_ALLOW_MULTISITE ? 'Yes' : 'No')),
126
+ 'NOBLOGREDIRECT' => 'URL redirected to if the visitor tries to access a nonexistent blog',
127
+ 'CONCATENATE_SCRIPTS' => array('description' => 'Concatenate JavaScript files', 'value' => (defined('CONCATENATE_SCRIPTS') && CONCATENATE_SCRIPTS ? 'Yes' : 'No')),
128
+ 'WP_MEMORY_LIMIT' => 'WordPress memory limit',
129
+ 'WP_MAX_MEMORY_LIMIT' => 'Administrative memory limit',
130
+ 'WP_CACHE' => array('description' => 'Built-in caching', 'value' => (defined('WP_CACHE') && WP_CACHE ? 'Enabled' : 'Disabled')),
131
+ 'CUSTOM_USER_TABLE' => array('description' => 'Custom "users" table', 'value' => (defined('CUSTOM_USER_TABLE') ? 'Set' : '(not set)')),
132
+ 'CUSTOM_USER_META_TABLE' => array('description' => 'Custom "usermeta" table', 'value' => (defined('CUSTOM_USER_META_TABLE') ? 'Set' : '(not set)')),
133
+ 'FS_CHMOD_DIR' => array('description' => 'Overridden permissions for a new folder', 'value' => defined('FS_CHMOD_DIR') ? decoct(FS_CHMOD_DIR) : '(not set)'),
134
+ 'FS_CHMOD_FILE' => array('description' => 'Overridden permissions for a new file', 'value' => defined('FS_CHMOD_FILE') ? decoct(FS_CHMOD_FILE) : '(not set)'),
135
+ 'ALTERNATE_WP_CRON' => array('description' => 'Alternate WP cron', 'value' => (defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON ? 'Enabled' : 'Disabled')),
136
+ 'DISABLE_WP_CRON' => array('description' => 'WP cron status', 'value' => (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON ? 'Disabled' : 'Enabled')),
137
+ 'WP_CRON_LOCK_TIMEOUT' => 'Cron running frequency lock',
138
+ 'EMPTY_TRASH_DAYS' => array('description' => 'Interval the trash is automatically emptied at in days', 'value' => (EMPTY_TRASH_DAYS > 0 ? EMPTY_TRASH_DAYS : 'Never')),
139
+ 'WP_ALLOW_REPAIR' => array('description' => 'Automatic database repair', 'value' => (defined('WP_ALLOW_REPAIR') && WP_ALLOW_REPAIR ? 'Enabled' : 'Disabled')),
140
+ 'DO_NOT_UPGRADE_GLOBAL_TABLES' => array('description' => 'Do not upgrade global tables', 'value' => (defined('DO_NOT_UPGRADE_GLOBAL_TABLES') && DO_NOT_UPGRADE_GLOBAL_TABLES ? 'Yes' : 'No')),
141
+ 'DISALLOW_FILE_EDIT' => array('description' => 'Disallow plugin/theme editing', 'value' => (defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT ? 'Yes' : 'No')),
142
+ 'DISALLOW_FILE_MOD' => array('description' => 'Disallow plugin/theme update and installation', 'value' => (defined('DISALLOW_FILE_MOD') && DISALLOW_FILE_MOD ? 'Yes' : 'No')),
143
+ 'IMAGE_EDIT_OVERWRITE' => array('description' => 'Overwrite image edits when restoring the original', 'value' => (defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ? 'Yes' : 'No')),
144
+ 'FORCE_SSL_ADMIN' => array('description' => 'Force SSL for administrative logins', 'value' => (defined('FORCE_SSL_ADMIN') && FORCE_SSL_ADMIN ? 'Yes' : 'No')),
145
+ 'WP_HTTP_BLOCK_EXTERNAL' => array('description' => 'Block external URL requests', 'value' => (defined('WP_HTTP_BLOCK_EXTERNAL') && WP_HTTP_BLOCK_EXTERNAL ? 'Yes' : 'No')),
146
+ 'WP_ACCESSIBLE_HOSTS' => 'Whitelisted hosts',
147
+ 'WP_AUTO_UPDATE_CORE' => array('description' => 'Automatic WP Core updates', 'value' => defined('WP_AUTO_UPDATE_CORE') ? (is_bool(WP_AUTO_UPDATE_CORE) ? (WP_AUTO_UPDATE_CORE ? 'Everything' : 'None') : WP_AUTO_UPDATE_CORE) : 'Default'),
148
+ );
149
+
150
+ foreach ($wordPressValues as $settingName => $settingData):
151
+ $escapedName = esc_html($settingName);
152
+ $escapedDescription = '';
153
+ $escapedValue = '(not set)';
154
+ if (is_array($settingData)) {
155
+ $escapedDescription = esc_html($settingData['description']);
156
+ if (isset($settingData['value'])) {
157
+ $escapedValue = esc_html($settingData['value']);
158
+ }
159
+ }
160
+ else {
161
+ $escapedDescription = esc_html($settingData);
162
+ if (defined($settingName)) {
163
+ $escapedValue = esc_html(constant($settingName));
164
+ }
165
+ }
166
+ ?>
167
+ <tr>
168
+ <td><strong><?php echo $escapedName ?></strong></td>
169
+ <td><?php echo $escapedDescription ?></td>
170
+ <td><?php echo $escapedValue ?></td>
171
+ </tr>
172
+ <?php endforeach ?>
173
+ </tbody>
174
+ <tbody class="empty-row">
175
+ <tr>
176
+ <td colspan="<?php echo $cols ?>"></td>
177
+ </tr>
178
+ </tbody>
179
+
180
  <tbody class="thead">
181
  <tr>
182
  <th colspan="<?php echo $cols ?>">WordPress Plugins</th>
lib/menu_options.php CHANGED
@@ -885,11 +885,10 @@ $w = new wfConfig();
885
  value="<?php $w->f( 'bannedURLs' ); ?>" size="40"/></td>
886
  </tr>
887
  <tr>
888
- <th colspan="2" style="color: #999;">Separate multiple URL's with commas. If you see an attacker
889
- repeatedly probing your site for a known vulnerability you can use this to immediately block
890
- them.<br/>
891
- All URL's must start with a '/' without quotes and must be relative. e.g. /badURLone/,
892
- /bannedPage.html, /dont-access/this/URL/
893
  <br/><br/></th>
894
  </tr>
895
 
885
  value="<?php $w->f( 'bannedURLs' ); ?>" size="40"/></td>
886
  </tr>
887
  <tr>
888
+ <th colspan="2" style="color: #999;">Separate multiple URL's with commas. Asterisks are wildcards,
889
+ but use with care. If you see an attacker repeatedly probing your site for a known vulnerability
890
+ you can use this to immediately block them. All URL's must start with a '/' without quotes and
891
+ must be relative. e.g. /badURLone/, /bannedPage.html, /dont-access/this/URL/, /starts/with-*
 
892
  <br/><br/></th>
893
  </tr>
894
 
lib/wfCache.php CHANGED
@@ -513,7 +513,7 @@ class wfCache {
513
  $excludedURLs = "";
514
  if(wfConfig::get('bannedURLs', false)){
515
  foreach(explode(',', wfConfig::get('bannedURLs', false)) as $URL){
516
- $excludedURLs .= "RewriteCond %{REQUEST_URI} !^" . self::regexSpaceFix(preg_quote(trim($URL))) . "$\n\t";
517
  }
518
  }
519
 
513
  $excludedURLs = "";
514
  if(wfConfig::get('bannedURLs', false)){
515
  foreach(explode(',', wfConfig::get('bannedURLs', false)) as $URL){
516
+ $excludedURLs .= "RewriteCond %{REQUEST_URI} !" . wfUtils::patternToRegex($URL, '', '') . "\n\t";
517
  }
518
  }
519
 
lib/wfUtils.php CHANGED
@@ -8,7 +8,8 @@ class wfUtils {
8
  private static $lastDisplayErrors = false;
9
  public static function patternToRegex($pattern, $mod = 'i', $sep = '/') {
10
  $pattern = preg_quote(trim($pattern), $sep);
11
- return $sep . str_replace("\\*", '.*', $pattern) . $sep . $mod;
 
12
  }
13
  public static function makeTimeAgo($secs, $noSeconds = false) {
14
  if($secs < 1){
@@ -274,8 +275,8 @@ class wfUtils {
274
  }
275
 
276
  // IPv4 mapped IPv6
277
- if (preg_match('/^((?:0{1,4}(?::|)){0,5})(::)?ffff:((?:\d{1,3}(?:\.|$)){4})$/i', $ip, $matches)) {
278
- $octets = explode('.', $matches[3]);
279
  return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]);
280
  }
281
 
@@ -512,6 +513,10 @@ class wfUtils {
512
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
513
  }
514
  if (self::isValidIP($j)) {
 
 
 
 
515
  if (self::isPrivateAddress($j)) {
516
  $privates[] = array($j, $var);
517
  } else {
@@ -530,6 +535,10 @@ class wfUtils {
530
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
531
  }
532
  if(self::isValidIP($j)){
 
 
 
 
533
  if(self::isPrivateAddress($j)){
534
  $privates[] = array($j, $var);
535
  } else {
@@ -547,6 +556,10 @@ class wfUtils {
547
  $item = preg_replace('/:\d+$/', '', $item); //Strip off port
548
  }
549
  if(self::isValidIP($item)){
 
 
 
 
550
  if(self::isPrivateAddress($item)){
551
  $privates[] = array($item, $var);
552
  } else {
@@ -560,6 +573,15 @@ class wfUtils {
560
  return false;
561
  }
562
  }
 
 
 
 
 
 
 
 
 
563
  public static function extractHostname($str){
564
  if(preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)){
565
  return strtolower($matches[1]);
8
  private static $lastDisplayErrors = false;
9
  public static function patternToRegex($pattern, $mod = 'i', $sep = '/') {
10
  $pattern = preg_quote(trim($pattern), $sep);
11
+ $pattern = str_replace(' ', '\s', $pattern);
12
+ return $sep . '^' . str_replace('\*', '.*', $pattern) . '$' . $sep . $mod;
13
  }
14
  public static function makeTimeAgo($secs, $noSeconds = false) {
15
  if($secs < 1){
275
  }
276
 
277
  // IPv4 mapped IPv6
278
+ if (preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $ip, $matches)) {
279
+ $octets = explode('.', $matches[1]);
280
  return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]);
281
  }
282
 
513
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
514
  }
515
  if (self::isValidIP($j)) {
516
+ if (self::isIPv6MappedIPv4($j)) {
517
+ $j = self::inet_ntop(self::inet_pton($j));
518
+ }
519
+
520
  if (self::isPrivateAddress($j)) {
521
  $privates[] = array($j, $var);
522
  } else {
535
  $j = preg_replace('/:\d+$/', '', $j); //Strip off port
536
  }
537
  if(self::isValidIP($j)){
538
+ if (self::isIPv6MappedIPv4($j)) {
539
+ $j = self::inet_ntop(self::inet_pton($j));
540
+ }
541
+
542
  if(self::isPrivateAddress($j)){
543
  $privates[] = array($j, $var);
544
  } else {
556
  $item = preg_replace('/:\d+$/', '', $item); //Strip off port
557
  }
558
  if(self::isValidIP($item)){
559
+ if (self::isIPv6MappedIPv4($item)) {
560
+ $item = self::inet_ntop(self::inet_pton($item));
561
+ }
562
+
563
  if(self::isPrivateAddress($item)){
564
  $privates[] = array($item, $var);
565
  } else {
573
  return false;
574
  }
575
  }
576
+
577
+ /**
578
+ * @param string $ip
579
+ * @return bool
580
+ */
581
+ public static function isIPv6MappedIPv4($ip) {
582
+ return preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/i', $ip) > 0;
583
+ }
584
+
585
  public static function extractHostname($str){
586
  if(preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)){
587
  return strtolower($matches[1]);
lib/wordfenceClass.php CHANGED
@@ -1100,7 +1100,7 @@ SQL
1100
  if(wfConfig::get('bannedURLs', false)){
1101
  $URLs = explode(',', wfConfig::get('bannedURLs'));
1102
  foreach($URLs as $URL){
1103
- if($_SERVER['REQUEST_URI'] == trim($URL)){
1104
  $wfLog->blockIP($IP, "Accessed a banned URL.");
1105
  $wfLog->do503(3600, "Accessed a banned URL.");
1106
  //exits
@@ -1773,6 +1773,15 @@ SQL
1773
  }
1774
  return array('ok' => 1);
1775
  }
 
 
 
 
 
 
 
 
 
1776
  public static function ajax_removeFromCache_callback(){
1777
  $id = $_POST['id'];
1778
  $link = get_permalink($id);
@@ -3469,7 +3478,7 @@ HTML;
3469
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
3470
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
3471
  'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'checkFalconHtaccess',
3472
- 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'saveCacheOptions', 'clearPageCache',
3473
  'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
3474
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
3475
  'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
@@ -3573,7 +3582,11 @@ HTML;
3573
  echo '<div id="wordfenceConfigWarning" class="fade error"><p><strong>Wordfence could not get an API key from the Wordfence scanning servers when it activated.</strong> You can try to fix this by going to the Wordfence "options" page and hitting "Save Changes". This will cause Wordfence to retry fetching an API key for you. If you keep seeing this error it usually means your WordPress server can\'t connect to our scanning servers. You can try asking your WordPress host to allow your WordPress server to connect to noc1.wordfence.com.</p></div>';
3574
  }
3575
  public static function adminEmailWarning(){
3576
- echo '<div id="wordfenceConfigWarning" class="fade error"><p><strong>You have not set an administrator email address to receive alerts for Wordfence.</strong> Please <a href="' . self::getMyOptionsURL() . '">click here to go to the Wordfence Options Page</a> and set an email address where you will receive security alerts from this site.</p></div>';
 
 
 
 
3577
  }
3578
  public static function autoUpdateNotice(){
3579
  echo '<div id="wordfenceAutoUpdateChoice" class="fade error"><p><strong>Do you want Wordfence to stay up-to-date automatically?</strong>&nbsp;&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'yes\'); return false;">Yes, enable auto-update.</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'no\'); return false;">No thanks.</a></p></div>';
@@ -3608,7 +3621,7 @@ HTML;
3608
  }
3609
  }
3610
  if(! $warningAdded){
3611
- if(wfConfig::get('tourClosed') == '1' && (! wfConfig::get('alertEmails')) ){
3612
  $warningAdded = true;
3613
  if(wfUtils::isAdminPageMU()){
3614
  add_action('network_admin_notices', 'wordfence::adminEmailWarning');
@@ -3864,7 +3877,7 @@ document.location.href=$adminURL;
3864
  ($serverInfo->isCGI() || $serverInfo->isFastCGI())),
3865
  array("litespeed", 'LiteSpeed', $serverInfo->isLiteSpeed()),
3866
  array("nginx", 'NGINX', $serverInfo->isNGINX()),
3867
- // array("iis", 'Windows (IIS)', $serverInfo->isIIS()),
3868
  );
3869
  $wafActionContent = '<p>To be as secure as possible, the Wordfence Web Application Firewall is designed
3870
  to run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially
@@ -4467,7 +4480,10 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
4467
  (isset($_POST['author']) && is_numeric(preg_replace('/[^0-9]/', '', $_POST['author'])))
4468
  )
4469
  ) {
4470
- $query_vars['author'] = -1;
 
 
 
4471
  }
4472
  return $query_vars;
4473
  }
@@ -5110,7 +5126,7 @@ LIMIT %d", $lastSendTime, $limit));
5110
  if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) {
5111
  echo '<div class="updated is-dismissible"><p>The installation was successful! Your site is protected to the fullest extent!</p></div>';
5112
  } else {
5113
- echo '<div class="notice notice-error"><p>The changes have not yet taken effect. If you are using LiteSpeed
5114
  as your web server or CGI/FastCGI interface, you may need to wait a few minutes for the changes to take effect since the
5115
  configuration files are sometimes cached. You also may need to select a different server configuration in order to
5116
  complete this step, but wait for a few minutes before trying. You can try refreshing this page. </p></div>';
@@ -5206,6 +5222,7 @@ class wfWAFAutoPrependHelper {
5206
  case 'apache-suphp':
5207
  case 'nginx':
5208
  case 'litespeed':
 
5209
  if (file_exists($userIniPath)) {
5210
  $backups[] = $userIniPath;
5211
  }
@@ -5357,6 +5374,7 @@ $userIniHtaccessDirectives
5357
  case 'nginx':
5358
  case 'apache-suphp':
5359
  case 'litespeed':
 
5360
  $autoPrependIni = sprintf("; Wordfence WAF
5361
  auto_prepend_file = '%s'
5362
  ; END Wordfence WAF
1100
  if(wfConfig::get('bannedURLs', false)){
1101
  $URLs = explode(',', wfConfig::get('bannedURLs'));
1102
  foreach($URLs as $URL){
1103
+ if(preg_match(wfUtils::patternToRegex($URL, ''), $_SERVER['REQUEST_URI'])){
1104
  $wfLog->blockIP($IP, "Accessed a banned URL.");
1105
  $wfLog->do503(3600, "Accessed a banned URL.");
1106
  //exits
1773
  }
1774
  return array('ok' => 1);
1775
  }
1776
+ public static function ajax_adminEmailChoice_callback() {
1777
+ $choice = $_POST['choice'];
1778
+ wfConfig::set('adminEmailChoice', '1');
1779
+ if ($choice == 'mine') {
1780
+ $email = wp_get_current_user()->user_email;
1781
+ wfConfig::set('alertEmails', $email);
1782
+ }
1783
+ return array('ok' => 1);
1784
+ }
1785
  public static function ajax_removeFromCache_callback(){
1786
  $id = $_POST['id'];
1787
  $link = get_permalink($id);
3478
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
3479
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
3480
  'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'checkFalconHtaccess',
3481
+ 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'adminEmailChoice', 'saveCacheOptions', 'clearPageCache',
3482
  'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
3483
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
3484
  'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
3582
  echo '<div id="wordfenceConfigWarning" class="fade error"><p><strong>Wordfence could not get an API key from the Wordfence scanning servers when it activated.</strong> You can try to fix this by going to the Wordfence "options" page and hitting "Save Changes". This will cause Wordfence to retry fetching an API key for you. If you keep seeing this error it usually means your WordPress server can\'t connect to our scanning servers. You can try asking your WordPress host to allow your WordPress server to connect to noc1.wordfence.com.</p></div>';
3583
  }
3584
  public static function adminEmailWarning(){
3585
+ $url = network_admin_url('admin.php?page=WordfenceSecOpt&wafAction=useMineForAdminEmailAlerts');
3586
+ $dismissURL = network_admin_url('admin.php?page=WordfenceSecOpt&wafAction=dismissAdminEmailNotice&nonce=' .
3587
+ rawurlencode(wp_create_nonce('wfDismissAdminEmailWarning')));
3588
+ echo '<div id="wordfenceAdminEmailWarning" class="fade error"><p><strong>You have not set an administrator email address to receive alerts for Wordfence.</strong> Please <a href="' . self::getMyOptionsURL() . '">click here to go to the Wordfence Options Page</a> and set an email address where you will receive security alerts from this site.</p><p><a class="button button-small" href="#" onclick="wordfenceExt.adminEmailChoice(\'mine\'); return false;"">Use My Email Address</a>
3589
+ <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.adminEmailChoice(\'no\'); return false;">Dismiss</a></p></div>';
3590
  }
3591
  public static function autoUpdateNotice(){
3592
  echo '<div id="wordfenceAutoUpdateChoice" class="fade error"><p><strong>Do you want Wordfence to stay up-to-date automatically?</strong>&nbsp;&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'yes\'); return false;">Yes, enable auto-update.</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'no\'); return false;">No thanks.</a></p></div>';
3621
  }
3622
  }
3623
  if(! $warningAdded){
3624
+ if(wfConfig::get('tourClosed') == '1' && (!wfConfig::get('alertEmails') && (!wfConfig::get('adminEmailChoice')))){
3625
  $warningAdded = true;
3626
  if(wfUtils::isAdminPageMU()){
3627
  add_action('network_admin_notices', 'wordfence::adminEmailWarning');
3877
  ($serverInfo->isCGI() || $serverInfo->isFastCGI())),
3878
  array("litespeed", 'LiteSpeed', $serverInfo->isLiteSpeed()),
3879
  array("nginx", 'NGINX', $serverInfo->isNGINX()),
3880
+ array("iis", 'Windows (IIS)', $serverInfo->isIIS()),
3881
  );
3882
  $wafActionContent = '<p>To be as secure as possible, the Wordfence Web Application Firewall is designed
3883
  to run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially
4480
  (isset($_POST['author']) && is_numeric(preg_replace('/[^0-9]/', '', $_POST['author'])))
4481
  )
4482
  ) {
4483
+ status_header(404);
4484
+ nocache_headers();
4485
+ include(get_404_template());
4486
+ exit;
4487
  }
4488
  return $query_vars;
4489
  }
5126
  if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) {
5127
  echo '<div class="updated is-dismissible"><p>The installation was successful! Your site is protected to the fullest extent!</p></div>';
5128
  } else {
5129
+ echo '<div class="notice notice-error"><p>The changes have not yet taken effect. If you are using LiteSpeed or IIS
5130
  as your web server or CGI/FastCGI interface, you may need to wait a few minutes for the changes to take effect since the
5131
  configuration files are sometimes cached. You also may need to select a different server configuration in order to
5132
  complete this step, but wait for a few minutes before trying. You can try refreshing this page. </p></div>';
5222
  case 'apache-suphp':
5223
  case 'nginx':
5224
  case 'litespeed':
5225
+ case 'iis':
5226
  if (file_exists($userIniPath)) {
5227
  $backups[] = $userIniPath;
5228
  }
5374
  case 'nginx':
5375
  case 'apache-suphp':
5376
  case 'litespeed':
5377
+ case 'iis':
5378
  $autoPrependIni = sprintf("; Wordfence WAF
5379
  auto_prepend_file = '%s'
5380
  ; END Wordfence WAF
lib/wordfenceHash.php CHANGED
@@ -199,12 +199,11 @@ class wordfenceHash {
199
  //exits
200
  }
201
 
202
- $exclude = WordfenceScanner::getExcludeFilePattern();
203
  if ($exclude && preg_match($exclude, $realFile)) {
204
  return;
205
  }
206
 
207
-
208
  //Put this after the fork, that way we will at least scan one more file after we fork if it takes us more than 10 seconds to search for the stoppedOnFile
209
  if($this->stoppedOnFile && $file != $this->stoppedOnFile){
210
  return;
@@ -230,99 +229,125 @@ class wordfenceHash {
230
  if($this->malwareEnabled && $this->isMalwarePrefix($md5)){
231
  $this->possibleMalware[] = array($file, $md5);
232
  }
233
- if(isset($this->knownFiles['core'][$file])){
234
- if(strtoupper($this->knownFiles['core'][$file]) == $shac){
235
- $knownFile = 1;
236
- } else {
237
- if($this->coreEnabled){
238
- $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
239
- $fileContents = @file_get_contents($localFile);
240
- if($fileContents && (! preg_match('/<\?' . 'php[\r\n\s\t]*\/\/[\r\n\s\t]*Silence is golden\.[\r\n\s\t]*(?:\?>)?[\r\n\s\t]*$/s', $fileContents))){ //<?php
241
- if(! $this->isSafeFile($shac)){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
- $this->haveIssues['core'] = true;
244
- $this->engine->addIssue(
245
- 'file',
246
- 1,
247
- 'coreModified' . $file . $md5,
248
- 'coreModified' . $file,
249
- 'WordPress core file modified: ' . $file,
250
- "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
251
- array(
252
- 'file' => $file,
253
- 'cType' => 'core',
254
- 'canDiff' => true,
255
- 'canFix' => true,
256
- 'canDelete' => false
257
  )
258
  );
 
259
  }
260
  }
261
  }
262
- }
263
- } else if(isset($this->knownFiles['plugins'][$file])){
264
- if(in_array($shac, $this->knownFiles['plugins'][$file])){
265
- $knownFile = 1;
266
- } else {
267
- if($this->pluginsEnabled){
268
- if(! $this->isSafeFile($shac)){
269
- $itemName = $this->knownFiles['plugins'][$file][0];
270
- $itemVersion = $this->knownFiles['plugins'][$file][1];
271
- $cKey = $this->knownFiles['plugins'][$file][2];
272
- $this->haveIssues['plugins'] = true;
273
- $this->engine->addIssue(
274
- 'file',
275
- 2,
276
- 'modifiedplugin' . $file . $md5,
277
- 'modifiedplugin' . $file,
278
- 'Modified plugin file: ' . $file,
279
- "This file belongs to plugin \"$itemName\" version \"$itemVersion\" and has been modified from the file that is distributed by WordPress.org for this version. Please use the link to see how the file has changed. If you have modified this file yourself, you can safely ignore this warning. If you see a lot of changed files in a plugin that have been made by the author, then try uninstalling and reinstalling the plugin to force an upgrade. Doing this is a workaround for plugin authors who don't manage their code correctly. [See our FAQ on www.wordfence.com for more info]",
280
- array(
281
- 'file' => $file,
282
- 'cType' => 'plugin',
283
- 'canDiff' => true,
284
- 'canFix' => true,
285
- 'canDelete' => false,
286
- 'cName' => $itemName,
287
- 'cVersion' => $itemVersion,
288
- 'cKey' => $cKey
 
 
 
 
289
  )
290
  );
 
291
  }
292
- }
293
 
294
- }
295
- } else if(isset($this->knownFiles['themes'][$file])){
296
- if(in_array($shac, $this->knownFiles['themes'][$file])){
297
- $knownFile = 1;
298
- } else {
299
- if($this->themesEnabled){
300
- if(! $this->isSafeFile($shac)){
301
- $itemName = $this->knownFiles['themes'][$file][0];
302
- $itemVersion = $this->knownFiles['themes'][$file][1];
303
- $cKey = $this->knownFiles['themes'][$file][2];
304
- $this->haveIssues['themes'] = true;
305
- $this->engine->addIssue(
306
- 'file',
307
- 2,
308
- 'modifiedtheme' . $file . $md5,
309
- 'modifiedtheme' . $file,
310
- 'Modified theme file: ' . $file,
311
- "This file belongs to theme \"$itemName\" version \"$itemVersion\" and has been modified from the original distribution. It is common for site owners to modify their theme files, so if you have modified this file yourself you can safely ignore this warning.",
312
- array(
313
- 'file' => $file,
314
- 'cType' => 'theme',
315
- 'canDiff' => true,
316
- 'canFix' => true,
317
- 'canDelete' => false,
318
- 'cName' => $itemName,
319
- 'cVersion' => $itemVersion,
320
- 'cKey' => $cKey
 
 
 
 
 
321
  )
322
  );
 
323
  }
324
- }
325
 
 
326
  }
327
  }
328
  // knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
199
  //exits
200
  }
201
 
202
+ $exclude = wordfenceScanner::getExcludeFilePattern(wordfenceScanner::EXCLUSION_PATTERNS_USER);
203
  if ($exclude && preg_match($exclude, $realFile)) {
204
  return;
205
  }
206
 
 
207
  //Put this after the fork, that way we will at least scan one more file after we fork if it takes us more than 10 seconds to search for the stoppedOnFile
208
  if($this->stoppedOnFile && $file != $this->stoppedOnFile){
209
  return;
229
  if($this->malwareEnabled && $this->isMalwarePrefix($md5)){
230
  $this->possibleMalware[] = array($file, $md5);
231
  }
232
+
233
+ $knownFileExclude = wordfenceScanner::getExcludeFilePattern(wordfenceScanner::EXCLUSION_PATTERNS_KNOWN_FILES);
234
+ $allowKnownFileScan = true;
235
+ if ($knownFileExclude) {
236
+ $allowKnownFileScan = !preg_match($knownFileExclude, $realFile);
237
+ }
238
+
239
+ if ($allowKnownFileScan)
240
+ {
241
+ if (isset($this->knownFiles['core'][$file]))
242
+ {
243
+ if (strtoupper($this->knownFiles['core'][$file]) == $shac)
244
+ {
245
+ $knownFile = 1;
246
+ } else
247
+ {
248
+ if ($this->coreEnabled)
249
+ {
250
+ $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
251
+ $fileContents = @file_get_contents($localFile);
252
+ if ($fileContents && (!preg_match('/<\?' . 'php[\r\n\s\t]*\/\/[\r\n\s\t]*Silence is golden\.[\r\n\s\t]*(?:\?>)?[\r\n\s\t]*$/s', $fileContents)))
253
+ { //<?php
254
+ if (!$this->isSafeFile($shac))
255
+ {
256
 
257
+ $this->haveIssues['core'] = true;
258
+ $this->engine->addIssue(
259
+ 'file',
260
+ 1,
261
+ 'coreModified' . $file . $md5,
262
+ 'coreModified' . $file,
263
+ 'WordPress core file modified: ' . $file,
264
+ "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
265
+ array(
266
+ 'file' => $file,
267
+ 'cType' => 'core',
268
+ 'canDiff' => true,
269
+ 'canFix' => true,
270
+ 'canDelete' => false
271
  )
272
  );
273
+ }
274
  }
275
  }
276
  }
277
+ } else if (isset($this->knownFiles['plugins'][$file]))
278
+ {
279
+ if (in_array($shac, $this->knownFiles['plugins'][$file]))
280
+ {
281
+ $knownFile = 1;
282
+ } else
283
+ {
284
+ if ($this->pluginsEnabled)
285
+ {
286
+ if (!$this->isSafeFile($shac))
287
+ {
288
+ $itemName = $this->knownFiles['plugins'][$file][0];
289
+ $itemVersion = $this->knownFiles['plugins'][$file][1];
290
+ $cKey = $this->knownFiles['plugins'][$file][2];
291
+ $this->haveIssues['plugins'] = true;
292
+ $this->engine->addIssue(
293
+ 'file',
294
+ 2,
295
+ 'modifiedplugin' . $file . $md5,
296
+ 'modifiedplugin' . $file,
297
+ 'Modified plugin file: ' . $file,
298
+ "This file belongs to plugin \"$itemName\" version \"$itemVersion\" and has been modified from the file that is distributed by WordPress.org for this version. Please use the link to see how the file has changed. If you have modified this file yourself, you can safely ignore this warning. If you see a lot of changed files in a plugin that have been made by the author, then try uninstalling and reinstalling the plugin to force an upgrade. Doing this is a workaround for plugin authors who don't manage their code correctly. [See our FAQ on www.wordfence.com for more info]",
299
+ array(
300
+ 'file' => $file,
301
+ 'cType' => 'plugin',
302
+ 'canDiff' => true,
303
+ 'canFix' => true,
304
+ 'canDelete' => false,
305
+ 'cName' => $itemName,
306
+ 'cVersion' => $itemVersion,
307
+ 'cKey' => $cKey
308
  )
309
  );
310
+ }
311
  }
 
312
 
313
+ }
314
+ } else if (isset($this->knownFiles['themes'][$file]))
315
+ {
316
+ if (in_array($shac, $this->knownFiles['themes'][$file]))
317
+ {
318
+ $knownFile = 1;
319
+ } else
320
+ {
321
+ if ($this->themesEnabled)
322
+ {
323
+ if (!$this->isSafeFile($shac))
324
+ {
325
+ $itemName = $this->knownFiles['themes'][$file][0];
326
+ $itemVersion = $this->knownFiles['themes'][$file][1];
327
+ $cKey = $this->knownFiles['themes'][$file][2];
328
+ $this->haveIssues['themes'] = true;
329
+ $this->engine->addIssue(
330
+ 'file',
331
+ 2,
332
+ 'modifiedtheme' . $file . $md5,
333
+ 'modifiedtheme' . $file,
334
+ 'Modified theme file: ' . $file,
335
+ "This file belongs to theme \"$itemName\" version \"$itemVersion\" and has been modified from the original distribution. It is common for site owners to modify their theme files, so if you have modified this file yourself you can safely ignore this warning.",
336
+ array(
337
+ 'file' => $file,
338
+ 'cType' => 'theme',
339
+ 'canDiff' => true,
340
+ 'canFix' => true,
341
+ 'canDelete' => false,
342
+ 'cName' => $itemName,
343
+ 'cVersion' => $itemVersion,
344
+ 'cKey' => $cKey
345
  )
346
  );
347
+ }
348
  }
 
349
 
350
+ }
351
  }
352
  }
353
  // knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
lib/wordfenceScanner.php CHANGED
@@ -3,6 +3,24 @@ require_once('wordfenceConstants.php');
3
  require_once('wordfenceClass.php');
4
  require_once('wordfenceURLHoover.php');
5
  class wordfenceScanner {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  //serialized:
7
  protected $path = '';
8
  protected $results = array();
@@ -14,7 +32,10 @@ class wordfenceScanner {
14
  protected $lastStatusTime = false;
15
  protected $patterns = "";
16
  protected $api = false;
17
- protected static $excludePattern = NULL;
 
 
 
18
  /** @var wfScanEngine */
19
  protected $scanEngine;
20
 
@@ -87,29 +108,46 @@ class wordfenceScanner {
87
  }
88
 
89
  /**
90
- * Return regular expression to exclude files or false if
91
- * there is no pattern
92
  *
93
- * @return string|boolean
 
94
  */
95
- public static function getExcludeFilePattern() {
96
- if (self::$excludePattern !== NULL) {
97
- return self::$excludePattern;
98
  }
99
- if(wfConfig::get('scan_exclude', false)){
100
- $exParts = explode("\n", wfUtils::cleanupOneEntryPerLine(wfConfig::get('scan_exclude')));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  foreach($exParts as &$exPart){
102
  $exPart = preg_quote(trim($exPart), '/');
103
  $exPart = preg_replace('/\\\\\*/', '.*', $exPart);
104
  }
105
 
106
- self::$excludePattern = '/^(?:' . implode('|', array_filter($exParts)) . ')$/i';
107
- self::$excludePattern = '/(?:' . implode('|', array_filter($exParts)) . ')$/i';
108
- } else {
109
- self::$excludePattern = false;
 
110
  }
111
 
112
- return self::$excludePattern;
113
  }
114
 
115
  /**
@@ -127,7 +165,7 @@ class wordfenceScanner {
127
  }
128
  $db = new wfDB();
129
  $lastCount = 'whatever';
130
- $excludePattern = self::getExcludeFilePattern();
131
  while(true){
132
  $thisCount = $db->querySingle("select count(*) from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0");
133
  if($thisCount == $lastCount){
3
  require_once('wordfenceClass.php');
4
  require_once('wordfenceURLHoover.php');
5
  class wordfenceScanner {
6
+ /*
7
+ * Mask to return all patterns in the exclusion list.
8
+ * @var int
9
+ */
10
+ const EXCLUSION_PATTERNS_ALL = PHP_INT_MAX;
11
+ /*
12
+ * Mask for patterns that the user has added.
13
+ */
14
+ const EXCLUSION_PATTERNS_USER = 0x1;
15
+ /*
16
+ * Mask for patterns that should be excluded from the known files scan.
17
+ */
18
+ const EXCLUSION_PATTERNS_KNOWN_FILES = 0x2;
19
+ /*
20
+ * Mask for patterns that should be excluded from the malware scan.
21
+ */
22
+ const EXCLUSION_PATTERNS_MALWARE = 0x4;
23
+
24
  //serialized:
25
  protected $path = '';
26
  protected $results = array();
32
  protected $lastStatusTime = false;
33
  protected $patterns = "";
34
  protected $api = false;
35
+ protected static $excludePatterns = array();
36
+ protected static $builtinExclusions = array(
37
+ array('pattern' => 'wp-includes/version.php', 'include' => self::EXCLUSION_PATTERNS_KNOWN_FILES), //Excluded from the known files scan because non-en_US installations will have extra content that fails the check, still in malware scan
38
+ );
39
  /** @var wfScanEngine */
40
  protected $scanEngine;
41
 
108
  }
109
 
110
  /**
111
+ * Return regular expression to exclude files or false if
112
+ * there is no pattern
113
  *
114
+ * @param $whichPatterns int Bitmask indicating which patterns to include.
115
+ * @return string|boolean
116
  */
117
+ public static function getExcludeFilePattern($whichPatterns = self::EXCLUSION_PATTERNS_USER) {
118
+ if (isset(self::$excludePatterns[$whichPatterns])) {
119
+ return self::$excludePatterns[$whichPatterns];
120
  }
121
+
122
+ $exParts = array();
123
+ if (($whichPatterns & self::EXCLUSION_PATTERNS_USER) > 0)
124
+ {
125
+ if (wfConfig::get('scan_exclude', false))
126
+ {
127
+ $exParts = explode("\n", wfUtils::cleanupOneEntryPerLine(wfConfig::get('scan_exclude')));
128
+ }
129
+ }
130
+
131
+ foreach (self::$builtinExclusions as $pattern) {
132
+ if (($pattern['include'] & $whichPatterns) > 0) {
133
+ $exParts[] = $pattern['pattern'];
134
+ }
135
+ }
136
+
137
+ if (!empty($exParts)) {
138
  foreach($exParts as &$exPart){
139
  $exPart = preg_quote(trim($exPart), '/');
140
  $exPart = preg_replace('/\\\\\*/', '.*', $exPart);
141
  }
142
 
143
+ //self::$excludePattern = '/^(?:' . implode('|', array_filter($exParts)) . ')$/i';
144
+ self::$excludePatterns[$whichPatterns] = '/(?:' . implode('|', array_filter($exParts)) . ')$/i';
145
+ }
146
+ else {
147
+ self::$excludePatterns[$whichPatterns]= false;
148
  }
149
 
150
+ return self::$excludePatterns[$whichPatterns];
151
  }
152
 
153
  /**
165
  }
166
  $db = new wfDB();
167
  $lastCount = 'whatever';
168
+ $excludePattern = self::getExcludeFilePattern(self::EXCLUSION_PATTERNS_USER & self::EXCLUSION_PATTERNS_MALWARE);
169
  while(true){
170
  $thisCount = $db->querySingle("select count(*) from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0");
171
  if($thisCount == $lastCount){
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: mmaunder
3
  Tags: wordpress, security, web application firewall, waf, 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, two factor authentication, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching, IPv6, IP version 6
4
  Requires at least: 3.9
5
- Tested up to: 4.5
6
- Stable tag: 6.1.4
7
 
8
  The Wordfence WordPress security plugin provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
  == Description ==
@@ -195,22 +195,32 @@ Designed for every skill level, [The WordPress Security Learning Center](https:/
195
 
196
  == Changelog ==
197
 
 
 
 
 
 
 
 
 
 
 
198
  = 6.1.4 =
199
- Fix: Fixed potential bug with 'stored data not found after a fork. Got type: boolean'.
200
- Improvement: Added bulk actions and filters to WAF whitelist table.
201
- Improvement: Added a check while in learning mode to verify the response is not 404 before whitelising.
202
- Fix: Added index to attackLogTime. wfHits trimmed on runInstall now.
203
- Fix: Fixed attack data sync for hosts that cannot use wp-cron.
204
- Improvement: Use wftest@wordfence.com as the Diagnostics page default email address.
205
- Improvement: When WFWAF_ENABLED is set to false to disable the firewall, show this on the Firewall page.
206
- Fix: Prevent warnings when $_SERVER is empty.
207
- Fix: Bug fix for illegal string offset.
208
- Fix: Hooked up multibyte string functions to binary safe equivalents.
209
- Fix: Hooked up reverse IP lookup in Live Traffic.
210
- Fix: Add the user the web server (or PHP) is currently running as to Diagnostics page.
211
- Improvement: Pause Live Traffic after scrolling past the first entry.
212
- Improvement: Move "Permanently block all temporarily blocked IP addresses" button to top of blocked IP list.
213
- Fix: Added JSON fallback for PHP installations that don't have JSON enabled.
214
 
215
  = 6.1.3 =
216
  * Improvement: Added dismiss button to the Wordfence WAF setup admin notice.
2
  Contributors: mmaunder
3
  Tags: wordpress, security, web application firewall, waf, 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, two factor authentication, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching, IPv6, IP version 6
4
  Requires at least: 3.9
5
+ Tested up to: 4.5.1
6
+ Stable tag: 6.1.5
7
 
8
  The Wordfence WordPress security plugin provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
  == Description ==
195
 
196
  == Changelog ==
197
 
198
+ = 6.1.5 =
199
+ * Fix: WordPress language files no longer flagged as changed.
200
+ * Improvement: Accept wildcards in "Immediately block IP's that access these URLs."
201
+ * Fix: Fixed bug when multiple authors have published posts, /?author=N scans show an author archive page.
202
+ * Fix: Fixed issue with IPv6 mapped IPv4 addresses not being treated as IPv4.
203
+ * Improvement: Added WordPress version and various constants to Diagnostics report.
204
+ * Fix: Fixed bug with Windows users unable to save Firewall config.
205
+ * Improvement: Include option for IIS on Windows in Firewall config process, and recommend manual php.ini change only.
206
+ * Fix: Made the 'administrator email address' admin notice dismissable.
207
+
208
  = 6.1.4 =
209
+ * Fix: Fixed potential bug with 'stored data not found after a fork. Got type: boolean'.
210
+ * Improvement: Added bulk actions and filters to WAF whitelist table.
211
+ * Improvement: Added a check while in learning mode to verify the response is not 404 before whitelising.
212
+ * Fix: Added index to attackLogTime. wfHits trimmed on runInstall now.
213
+ * Fix: Fixed attack data sync for hosts that cannot use wp-cron.
214
+ * Improvement: Use wftest@wordfence.com as the Diagnostics page default email address.
215
+ * Improvement: When WFWAF_ENABLED is set to false to disable the firewall, show this on the Firewall page.
216
+ * Fix: Prevent warnings when $_SERVER is empty.
217
+ * Fix: Bug fix for illegal string offset.
218
+ * Fix: Hooked up multibyte string functions to binary safe equivalents.
219
+ * Fix: Hooked up reverse IP lookup in Live Traffic.
220
+ * Fix: Add the user the web server (or PHP) is currently running as to Diagnostics page.
221
+ * Improvement: Pause Live Traffic after scrolling past the first entry.
222
+ * Improvement: Move "Permanently block all temporarily blocked IP addresses" button to top of blocked IP list.
223
+ * Fix: Added JSON fallback for PHP installations that don't have JSON enabled.
224
 
225
  = 6.1.3 =
226
  * Improvement: Added dismiss button to the Wordfence WAF setup admin notice.
vendor/wordfence/wf-waf/src/init.php CHANGED
@@ -4,7 +4,7 @@ define('WFWAF_VERSION', '1.0.0');
4
  define('WFWAF_PATH', dirname(__FILE__) . '/');
5
  define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
6
  define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
7
- define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.1/');
8
  if (!defined('WFWAF_DEBUG')) {
9
  define('WFWAF_DEBUG', false);
10
  }
@@ -12,6 +12,8 @@ if (!defined('WFWAF_ENABLED')) {
12
  define('WFWAF_ENABLED', true);
13
  }
14
 
 
 
15
  require_once WFWAF_LIB_PATH . 'waf.php';
16
  require_once WFWAF_LIB_PATH . 'utils.php';
17
 
4
  define('WFWAF_PATH', dirname(__FILE__) . '/');
5
  define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
6
  define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
7
+ define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.2/');
8
  if (!defined('WFWAF_DEBUG')) {
9
  define('WFWAF_DEBUG', false);
10
  }
12
  define('WFWAF_ENABLED', true);
13
  }
14
 
15
+ define('WFWAF_IS_WINDOWS', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
16
+
17
  require_once WFWAF_LIB_PATH . 'waf.php';
18
  require_once WFWAF_LIB_PATH . 'utils.php';
19
 
vendor/wordfence/wf-waf/src/lib/parser/lexer.php CHANGED
@@ -301,6 +301,18 @@ class wfWAFBaseParser {
301
  $this->lexer = $lexer;
302
  }
303
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  /**
305
  * @param wfWAFLexerToken $token
306
  * @param int $type
301
  $this->lexer = $lexer;
302
  }
303
 
304
+ /**
305
+ * @param wfWAFLexerToken $token
306
+ * @param mixed $type
307
+ * @return bool
308
+ */
309
+ protected function isTokenOfType($token, $type) {
310
+ if (is_array($type)) {
311
+ return $token && in_array($token->getType(), $type);
312
+ }
313
+ return $token && $token->getType() === $type;
314
+ }
315
+
316
  /**
317
  * @param wfWAFLexerToken $token
318
  * @param int $type
vendor/wordfence/wf-waf/src/lib/parser/parser.php CHANGED
@@ -50,6 +50,7 @@ class wfWAFRuleParser extends wfWAFBaseParser {
50
  $action->getCategory(),
51
  $action->getScore(),
52
  $action->getDescription(),
 
53
  $action->getAction(),
54
  $comparisonGroup
55
  );
@@ -312,22 +313,38 @@ class wfWAFRuleParser extends wfWAFBaseParser {
312
  $subject = array(
313
  $globalToken->getValue() . '.' . $globalToken2->getValue(),
314
  );
315
- while (true) {
316
- $dotToken = $this->expectNextToken();
317
- if ($dotToken->getType() !== wfWAFRuleLexer::T_DOT) {
318
- $this->index--;
319
- break;
320
- }
321
- $paramToken = $this->expectNextToken();
322
- $this->expectTokenTypeEquals($paramToken, wfWAFRuleLexer::T_IDENTIFIER);
323
- $subject[] = $paramToken->getValue();
324
  }
 
325
  if (count($subject) === 1) {
326
  list($subject) = $subject;
327
  }
328
  return $subject;
329
  }
330
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  /**
332
  * @return wfWAFRuleParserURLParam
333
  * @throws wfWAFParserSyntaxError
@@ -411,6 +428,17 @@ class wfWAFRuleParser extends wfWAFBaseParser {
411
  return $value;
412
  }
413
 
 
 
 
 
 
 
 
 
 
 
 
414
  /**
415
  * @param wfWAFLexerToken $token
416
  * @return bool
@@ -441,6 +469,7 @@ class wfWAFRuleParserAction {
441
  private $category;
442
  private $score;
443
  private $description;
 
444
  private $action;
445
 
446
  /**
@@ -529,6 +558,20 @@ class wfWAFRuleParserAction {
529
  $this->description = $description;
530
  }
531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  /**
533
  * @return mixed
534
  */
@@ -588,7 +631,9 @@ class wfWAFRuleParserURLParam {
588
  * @return string
589
  */
590
  public function renderRule($action) {
591
- return sprintf('%s(url=%s, param=%s%s)', $action, wfWAFRule::exportString($this->getUrl()), $this->renderParam($this->getParam()),
 
 
592
  $this->getRules() ? ', rules=[' . join(', ', array_map('intval', $this->getRules())) . ']' : '');
593
  }
594
 
@@ -597,7 +642,21 @@ class wfWAFRuleParserURLParam {
597
  * @return mixed
598
  */
599
  private function renderParam($param) {
600
- return str_replace(array('[', ']'), array('.', ''), $param);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  }
602
 
603
  /**
50
  $action->getCategory(),
51
  $action->getScore(),
52
  $action->getDescription(),
53
+ $action->getWhitelist(),
54
  $action->getAction(),
55
  $comparisonGroup
56
  );
313
  $subject = array(
314
  $globalToken->getValue() . '.' . $globalToken2->getValue(),
315
  );
316
+ $savePoint = $this->index;
317
+ while (($property = $this->parsePropertyAccessor()) !== false) {
318
+ $subject[] = $property;
319
+ $savePoint = $this->index;
 
 
 
 
 
320
  }
321
+ $this->index = $savePoint;
322
  if (count($subject) === 1) {
323
  list($subject) = $subject;
324
  }
325
  return $subject;
326
  }
327
 
328
+ /**
329
+ * @return bool|mixed|string
330
+ * @throws wfWAFParserSyntaxError
331
+ */
332
+ private function parsePropertyAccessor() {
333
+ $savePoint = $this->index;
334
+ $nextToken = $this->nextToken();
335
+ if ($this->isTokenOfType($nextToken, wfWAFRuleLexer::T_DOT)) {
336
+ $property = $this->expectNextToken();
337
+ $this->expectTokenTypeEquals($property, wfWAFRuleLexer::T_IDENTIFIER);
338
+ return $property->getValue();
339
+ } else if ($this->isTokenOfType($nextToken, wfWAFRuleLexer::T_OPEN_BRACKET)) {
340
+ $property = $this->expectLiteral();
341
+ $this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_CLOSE_BRACKET);
342
+ return $property;
343
+ }
344
+ $this->index = $savePoint;
345
+ return false;
346
+ }
347
+
348
  /**
349
  * @return wfWAFRuleParserURLParam
350
  * @throws wfWAFParserSyntaxError
428
  return $value;
429
  }
430
 
431
+ /**
432
+ * @param wfWAFLexerToken $token
433
+ * @param string|array $value
434
+ * @return bool
435
+ */
436
+ private function isIdentifierWithValue($token, $value) {
437
+ return $token && $token->getType() === wfWAFRuleLexer::T_IDENTIFIER &&
438
+ (is_array($value) ? in_array($token->getLowerCaseValue(), array_map('strtolower', $value)) :
439
+ $token->getLowerCaseValue() === strtolower($value));
440
+ }
441
+
442
  /**
443
  * @param wfWAFLexerToken $token
444
  * @return bool
469
  private $category;
470
  private $score;
471
  private $description;
472
+ private $whitelist = 1;
473
  private $action;
474
 
475
  /**
558
  $this->description = $description;
559
  }
560
 
561
+ /**
562
+ * @return mixed
563
+ */
564
+ public function getWhitelist() {
565
+ return $this->whitelist;
566
+ }
567
+
568
+ /**
569
+ * @param mixed $whitelist
570
+ */
571
+ public function setWhitelist($whitelist) {
572
+ $this->whitelist = $whitelist;
573
+ }
574
+
575
  /**
576
  * @return mixed
577
  */
631
  * @return string
632
  */
633
  public function renderRule($action) {
634
+ return sprintf('%s(url=%s, param=%s%s)', $action,
635
+ wfWAFRule::exportString($this->getUrl()),
636
+ $this->renderParam($this->getParam()),
637
  $this->getRules() ? ', rules=[' . join(', ', array_map('intval', $this->getRules())) . ']' : '');
638
  }
639
 
642
  * @return mixed
643
  */
644
  private function renderParam($param) {
645
+ if (preg_match('/([a-zA-Z_][\\w_]*?\\.[a-zA-Z_][\\w_]*)(.*)/', $param, $matches)) {
646
+ list(, $global, $params) = $matches;
647
+ if (strlen($params) > 0) {
648
+ if (preg_match_all('/\\[([^\\]]*?)\\]/', $params, $matches)) {
649
+ $rendered = $global;
650
+ foreach ($matches[1] as $prop) {
651
+ $single = "'" . str_replace(array("'", '\\'), array("\\'", "\\\\"), $prop) . "'";
652
+ $double = '"' . str_replace(array('"', '\\'), array('\\"', "\\\\"), $prop) . '"';
653
+ $rendered .= sprintf('[%s]', strlen($single) <= strlen($double) ? $single : $double);
654
+ }
655
+ return $rendered;
656
+ }
657
+ }
658
+ }
659
+ return $param;
660
  }
661
 
662
  /**
vendor/wordfence/wf-waf/src/lib/parser/sqli.php CHANGED
@@ -2562,18 +2562,6 @@ class wfWAFSQLiParser extends wfWAFBaseParser {
2562
  $token->getLowerCaseValue() === wfWAFUtils::strtolower($value));
2563
  }
2564
 
2565
- /**
2566
- * @param wfWAFLexerToken $token
2567
- * @param mixed $type
2568
- * @return bool
2569
- */
2570
- private function isTokenOfType($token, $type) {
2571
- if (is_array($type)) {
2572
- return $token && in_array($token->getType(), $type);
2573
- }
2574
- return $token && $token->getType() === $type;
2575
- }
2576
-
2577
  /**
2578
  * @param wfWAFLexerToken $token
2579
  * @return bool
2562
  $token->getLowerCaseValue() === wfWAFUtils::strtolower($value));
2563
  }
2564
 
 
 
 
 
 
 
 
 
 
 
 
 
2565
  /**
2566
  * @param wfWAFLexerToken $token
2567
  * @return bool
vendor/wordfence/wf-waf/src/lib/rules.php CHANGED
@@ -25,6 +25,7 @@ class wfWAFRule implements wfWAFRuleInterface {
25
  private $category;
26
  private $score;
27
  private $description;
 
28
  private $action;
29
  private $comparisonGroup;
30
  /**
@@ -39,12 +40,32 @@ class wfWAFRule implements wfWAFRuleInterface {
39
  * @param string $category
40
  * @param int $score
41
  * @param string $description
 
42
  * @param string $action
43
  * @param wfWAFRuleComparisonGroup $comparisonGroup
44
  * @return wfWAFRule
45
  */
46
- public static function create($waf, $ruleID, $type, $category, $score, $description, $action, $comparisonGroup) {
47
- return new self($waf, $ruleID, $type, $category, $score, $description, $action, $comparisonGroup);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
  /**
@@ -62,16 +83,18 @@ class wfWAFRule implements wfWAFRuleInterface {
62
  * @param string $category
63
  * @param int $score
64
  * @param string $description
 
65
  * @param string $action
66
  * @param wfWAFRuleComparisonGroup $comparisonGroup
67
  */
68
- public function __construct($waf, $ruleID, $type, $category, $score, $description, $action, $comparisonGroup) {
69
  $this->setWAF($waf);
70
  $this->setRuleID($ruleID);
71
  $this->setType($type);
72
  $this->setCategory($category);
73
  $this->setScore($score);
74
  $this->setDescription($description);
 
75
  $this->setAction($action);
76
  $this->setComparisonGroup($comparisonGroup);
77
  }
@@ -80,12 +103,13 @@ class wfWAFRule implements wfWAFRuleInterface {
80
  * @return string
81
  */
82
  public function render() {
83
- return sprintf('%s::create($this, %d, %s, %s, %s, %s, %s, %s)', get_class($this),
84
  $this->getRuleID(),
85
  var_export($this->getType(), true),
86
  var_export($this->getCategory(), true),
87
  var_export($this->getScore(), true),
88
  var_export($this->getDescription(), true),
 
89
  var_export($this->getAction(), true),
90
  $this->getComparisonGroup()->render()
91
  );
@@ -106,7 +130,8 @@ RULE
106
  $this->getRuleID() ? 'id=' . (int) $this->getRuleID() : '',
107
  $this->getCategory() ? 'category=' . self::exportString($this->getCategory()) : '',
108
  $this->getScore() > 0 ? 'score=' . (int) $this->getScore() : '',
109
- $this->getCategory() ? 'description=' . self::exportString($this->getDescription()) : '',
 
110
  )))
111
  );
112
  }
@@ -140,6 +165,7 @@ RULE
140
  'category' => $this->getCategory(),
141
  'score' => $this->getScore(),
142
  'description' => $this->getDescription(),
 
143
  'action' => $this->getAction(),
144
  );
145
  }
@@ -214,6 +240,20 @@ RULE
214
  $this->description = $description;
215
  }
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  /**
218
  * @return string
219
  */
@@ -415,6 +455,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
415
  'lengthlessthan',
416
  'currentuseris',
417
  'currentuserisnot',
 
418
  );
419
 
420
  /**
@@ -653,6 +694,10 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
653
  return !$this->currentUserIs($subject);
654
  }
655
 
 
 
 
 
656
  /**
657
  * @return mixed
658
  */
25
  private $category;
26
  private $score;
27
  private $description;
28
+ private $whitelist;
29
  private $action;
30
  private $comparisonGroup;
31
  /**
40
  * @param string $category
41
  * @param int $score
42
  * @param string $description
43
+ * @param int $whitelist
44
  * @param string $action
45
  * @param wfWAFRuleComparisonGroup $comparisonGroup
46
  * @return wfWAFRule
47
  */
48
+ public static function create() {
49
+ $waf = func_get_arg(0);
50
+ $ruleID = func_get_arg(1);
51
+ $type = func_get_arg(2);
52
+ $category = func_get_arg(3);
53
+ $score = func_get_arg(4);
54
+ $description = func_get_arg(5);
55
+ $whitelist = 1;
56
+ $action = '';
57
+ $comparisonGroup = null;
58
+ //Compatibility with old compiled rules
59
+ if (func_num_args() == 8) { //Pre-whitelist flag
60
+ $action = func_get_arg(6);
61
+ $comparisonGroup = func_get_arg(7);
62
+ }
63
+ else if (func_num_args() == 9) { //Whitelist flag
64
+ $whitelist = func_get_arg(6);
65
+ $action = func_get_arg(7);
66
+ $comparisonGroup = func_get_arg(8);
67
+ }
68
+ return new self($waf, $ruleID, $type, $category, $score, $description, $whitelist, $action, $comparisonGroup);
69
  }
70
 
71
  /**
83
  * @param string $category
84
  * @param int $score
85
  * @param string $description
86
+ * @param int $whitelist
87
  * @param string $action
88
  * @param wfWAFRuleComparisonGroup $comparisonGroup
89
  */
90
+ public function __construct($waf, $ruleID, $type, $category, $score, $description, $whitelist, $action, $comparisonGroup) {
91
  $this->setWAF($waf);
92
  $this->setRuleID($ruleID);
93
  $this->setType($type);
94
  $this->setCategory($category);
95
  $this->setScore($score);
96
  $this->setDescription($description);
97
+ $this->setWhitelist($whitelist);
98
  $this->setAction($action);
99
  $this->setComparisonGroup($comparisonGroup);
100
  }
103
  * @return string
104
  */
105
  public function render() {
106
+ return sprintf('%s::create($this, %d, %s, %s, %s, %s, %d, %s, %s)', get_class($this),
107
  $this->getRuleID(),
108
  var_export($this->getType(), true),
109
  var_export($this->getCategory(), true),
110
  var_export($this->getScore(), true),
111
  var_export($this->getDescription(), true),
112
+ var_export($this->getWhitelist(), true),
113
  var_export($this->getAction(), true),
114
  $this->getComparisonGroup()->render()
115
  );
130
  $this->getRuleID() ? 'id=' . (int) $this->getRuleID() : '',
131
  $this->getCategory() ? 'category=' . self::exportString($this->getCategory()) : '',
132
  $this->getScore() > 0 ? 'score=' . (int) $this->getScore() : '',
133
+ $this->getDescription() ? 'description=' . self::exportString($this->getDescription()) : '',
134
+ $this->getWhitelist() == 0 ? 'whitelist=0' : '',
135
  )))
136
  );
137
  }
165
  'category' => $this->getCategory(),
166
  'score' => $this->getScore(),
167
  'description' => $this->getDescription(),
168
+ 'whitelist' => $this->getWhitelist(),
169
  'action' => $this->getAction(),
170
  );
171
  }
240
  $this->description = $description;
241
  }
242
 
243
+ /**
244
+ * @return int
245
+ */
246
+ public function getWhitelist() {
247
+ return $this->whitelist;
248
+ }
249
+
250
+ /**
251
+ * @param string $whitelist
252
+ */
253
+ public function setWhitelist($whitelist) {
254
+ $this->whitelist = $whitelist;
255
+ }
256
+
257
  /**
258
  * @return string
259
  */
455
  'lengthlessthan',
456
  'currentuseris',
457
  'currentuserisnot',
458
+ 'md5equals',
459
  );
460
 
461
  /**
694
  return !$this->currentUserIs($subject);
695
  }
696
 
697
+ public function md5Equals($subject) {
698
+ return md5((string) $subject) === $this->getExpected();
699
+ }
700
+
701
  /**
702
  * @return mixed
703
  */
vendor/wordfence/wf-waf/src/lib/storage/file.php CHANGED
@@ -482,7 +482,17 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
482
  return;
483
  }
484
 
485
- wfWAFStorageFile::atomicFilePutContents($this->getConfigFile(), self::LOG_FILE_HEADER . serialize($this->data));
 
 
 
 
 
 
 
 
 
 
486
  }
487
 
488
  /**
@@ -1051,7 +1061,11 @@ class wfWAFAttackDataStorageFileEngine {
1051
  public function truncate() {
1052
  $defaultHeader = $this->getDefaultHeader();
1053
  $this->close();
1054
- wfWAFStorageFile::atomicFilePutContents($this->getFile(), $defaultHeader, 'attack');
 
 
 
 
1055
  $this->header = array();
1056
  $this->offsetTable = array();
1057
  $this->open();
482
  return;
483
  }
484
 
485
+ if (WFWAF_IS_WINDOWS) {
486
+ self::lock($this->configFileHandle, LOCK_UN);
487
+ fclose($this->configFileHandle);
488
+ file_put_contents($this->getConfigFile(), self::LOG_FILE_HEADER . serialize($this->data), LOCK_EX);
489
+ } else {
490
+ wfWAFStorageFile::atomicFilePutContents($this->getConfigFile(), self::LOG_FILE_HEADER . serialize($this->data));
491
+ }
492
+
493
+ if (WFWAF_IS_WINDOWS) {
494
+ $this->configFileHandle = fopen($this->getConfigFile(), 'r+');
495
+ }
496
  }
497
 
498
  /**
1061
  public function truncate() {
1062
  $defaultHeader = $this->getDefaultHeader();
1063
  $this->close();
1064
+ if (WFWAF_IS_WINDOWS) {
1065
+ file_put_contents($this->getFile(), $defaultHeader, LOCK_EX);
1066
+ } else {
1067
+ wfWAFStorageFile::atomicFilePutContents($this->getFile(), $defaultHeader, 'attack');
1068
+ }
1069
  $this->header = array();
1070
  $this->offsetTable = array();
1071
  $this->open();
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -763,18 +763,20 @@ HTML
763
  * @var wfWAFRuleComparisonFailure $failedComparison
764
  */
765
  $rule = $failedRule['rule'];
766
- $failedComparison = $failedRule['failedComparison'];
 
767
 
768
- $data = array(
769
- 'timestamp' => time(),
770
- 'description' => 'Whitelisted while in Learning Mode.',
771
- 'ip' => $this->getRequest()->getIP(),
772
- );
773
- if (function_exists('get_current_user_id')) {
774
- $data['userID'] = get_current_user_id();
 
 
 
775
  }
776
- $this->whitelistRuleForParam($this->getRequest()->getPath(), $failedComparison->getParamKey(),
777
- $rule->getRuleID(), $data);
778
  }
779
  }
780
  }
763
  * @var wfWAFRuleComparisonFailure $failedComparison
764
  */
765
  $rule = $failedRule['rule'];
766
+ if ($rule->getWhitelist()) {
767
+ $failedComparison = $failedRule['failedComparison'];
768
 
769
+ $data = array(
770
+ 'timestamp' => time(),
771
+ 'description' => 'Whitelisted while in Learning Mode.',
772
+ 'ip' => $this->getRequest()->getIP(),
773
+ );
774
+ if (function_exists('get_current_user_id')) {
775
+ $data['userID'] = get_current_user_id();
776
+ }
777
+ $this->whitelistRuleForParam($this->getRequest()->getPath(), $failedComparison->getParamKey(),
778
+ $rule->getRuleID(), $data);
779
  }
 
 
780
  }
781
  }
782
  }
waf/bootstrap.php CHANGED
@@ -224,7 +224,15 @@ try {
224
 
225
  if (!file_exists(wfWAF::getInstance()->getCompiledRulesFile()) || !filesize(wfWAF::getInstance()->getCompiledRulesFile())) {
226
  try {
227
- wfWAF::getInstance()->updateRuleSet(file_get_contents(WFWAF_PATH . 'baseRules.rules'), false);
 
 
 
 
 
 
 
 
228
  } catch (wfWAFBuildRulesException $e) {
229
  // Log this somewhere
230
  error_log($e->getMessage());
224
 
225
  if (!file_exists(wfWAF::getInstance()->getCompiledRulesFile()) || !filesize(wfWAF::getInstance()->getCompiledRulesFile())) {
226
  try {
227
+ if (is_writable(wfWAF::getInstance()->getCompiledRulesFile()) &&
228
+ wfWAF::getInstance()->getStorageEngine()->getConfig('apiKey') !== null &&
229
+ wfWAF::getInstance()->getStorageEngine()->getConfig('createInitialRulesDelay') < time()
230
+ ) {
231
+ $event = new wfWAFCronFetchRulesEvent(time() - 60);
232
+ $event->setWaf(wfWAF::getInstance());
233
+ $event->fire();
234
+ wfWAF::getInstance()->getStorageEngine()->setConfig('createInitialRulesDelay', time() + (5 * 60));
235
+ }
236
  } catch (wfWAFBuildRulesException $e) {
237
  // Log this somewhere
238
  error_log($e->getMessage());
wordfence.php CHANGED
@@ -4,14 +4,14 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
- Version: 6.1.4
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '6.1.4');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
+ Version: 6.1.5
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.1.5');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17