Wordfence Security – Firewall & Malware Scan - Version 7.4.0

Version Description

  • August 22, 2019 =
  • Improvement: Added a MySQL-based configuration and data storage for the WAF to expand the number of hosting environments supported. For more detail, see: https://www.wordfence.com/help/firewall/mysqli-storage-engine/
  • Improvement: Updated bundled GeoIP database.
  • Fix: Fixed several console notices when running via the CLI.
Download this release

Release Info

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

Code changes from version 7.3.6 to 7.4.0

Files changed (77) hide show
  1. css/{activity-report-widget.1564590761.css → activity-report-widget.1566486436.css} +0 -0
  2. css/{diff.1564590761.css → diff.1566486436.css} +0 -0
  3. css/{dt_table.1564590761.css → dt_table.1566486436.css} +0 -0
  4. css/{fullLog.1564590761.css → fullLog.1566486436.css} +0 -0
  5. css/{iptraf.1564590761.css → iptraf.1566486436.css} +0 -0
  6. css/{jquery-ui-timepicker-addon.1564590761.css → jquery-ui-timepicker-addon.1566486436.css} +0 -0
  7. css/{jquery-ui.min.1564590761.css → jquery-ui.min.1566486436.css} +0 -0
  8. css/{jquery-ui.structure.min.1564590761.css → jquery-ui.structure.min.1566486436.css} +0 -0
  9. css/{jquery-ui.theme.min.1564590761.css → jquery-ui.theme.min.1566486436.css} +0 -0
  10. css/{main.1564590761.css → main.1566486436.css} +0 -0
  11. css/{phpinfo.1564590761.css → phpinfo.1566486436.css} +0 -0
  12. css/{wf-adminbar.1564590761.css → wf-adminbar.1566486436.css} +0 -0
  13. css/{wf-colorbox.1564590761.css → wf-colorbox.1566486436.css} +0 -0
  14. css/{wf-font-awesome.1564590761.css → wf-font-awesome.1566486436.css} +0 -0
  15. css/{wf-global.1564590761.css → wf-global.1566486436.css} +0 -0
  16. css/{wf-ionicons.1564590761.css → wf-ionicons.1566486436.css} +0 -0
  17. css/{wf-onboarding.1564590761.css → wf-onboarding.1566486436.css} +0 -0
  18. css/{wf-roboto-font.1564590761.css → wf-roboto-font.1566486436.css} +0 -0
  19. css/{wfselect2.min.1564590761.css → wfselect2.min.1566486436.css} +0 -0
  20. css/{wordfenceBox.1564590761.css → wordfenceBox.1566486436.css} +0 -0
  21. js/{Chart.bundle.min.1564590761.js → Chart.bundle.min.1566486436.js} +0 -0
  22. js/{admin.1564590761.js → admin.1566486436.js} +0 -0
  23. js/{admin.ajaxWatcher.1564590761.js → admin.ajaxWatcher.1566486436.js} +0 -0
  24. js/{admin.liveTraffic.1564590761.js → admin.liveTraffic.1566486436.js} +0 -0
  25. js/{date.1564590761.js → date.1566486436.js} +0 -0
  26. js/{jquery-ui-timepicker-addon.1564590761.js → jquery-ui-timepicker-addon.1566486436.js} +0 -0
  27. js/{jquery.colorbox-min.1564590761.js → jquery.colorbox-min.1566486436.js} +0 -0
  28. js/{jquery.colorbox.1564590761.js → jquery.colorbox.1566486436.js} +0 -0
  29. js/{jquery.dataTables.min.1564590761.js → jquery.dataTables.min.1566486436.js} +0 -0
  30. js/{jquery.qrcode.min.1564590761.js → jquery.qrcode.min.1566486436.js} +0 -0
  31. js/{jquery.tmpl.min.1564590761.js → jquery.tmpl.min.1566486436.js} +0 -0
  32. js/{jquery.tools.min.1564590761.js → jquery.tools.min.1566486436.js} +0 -0
  33. js/{knockout-3.3.0.1564590761.js → knockout-3.3.0.1566486436.js} +0 -0
  34. js/{wfdashboard.1564590761.js → wfdashboard.1566486436.js} +0 -0
  35. js/{wfdropdown.1564590761.js → wfdropdown.1566486436.js} +0 -0
  36. js/{wfglobal.1564590761.js → wfglobal.1566486436.js} +0 -0
  37. js/{wfpopover.1564590761.js → wfpopover.1566486436.js} +0 -0
  38. js/{wfselect2.min.1564590761.js → wfselect2.min.1566486436.js} +0 -0
  39. lib/GeoLite2-Country.mmdb +0 -0
  40. lib/wfConfig.php +17 -1
  41. lib/wfCrawl.php +5 -0
  42. lib/wfUtils.php +7 -4
  43. lib/wordfenceClass.php +81 -29
  44. modules/login-security/css/{admin-global.1564590761.css → admin-global.1566486436.css} +0 -0
  45. modules/login-security/css/{admin.1564590761.css → admin.1566486436.css} +0 -0
  46. modules/login-security/css/{colorbox.1564590761.css → colorbox.1566486436.css} +0 -0
  47. modules/login-security/css/{font-awesome.1564590761.css → font-awesome.1566486436.css} +0 -0
  48. modules/login-security/css/{ionicons.1564590761.css → ionicons.1566486436.css} +0 -0
  49. modules/login-security/css/{jquery-ui-timepicker-addon.1564590761.css → jquery-ui-timepicker-addon.1566486436.css} +0 -0
  50. modules/login-security/css/{jquery-ui.min.1564590761.css → jquery-ui.min.1566486436.css} +0 -0
  51. modules/login-security/css/{jquery-ui.structure.min.1564590761.css → jquery-ui.structure.min.1566486436.css} +0 -0
  52. modules/login-security/css/{jquery-ui.theme.min.1564590761.css → jquery-ui.theme.min.1566486436.css} +0 -0
  53. modules/login-security/css/{login.1564590761.css → login.1566486436.css} +0 -0
  54. modules/login-security/js/{admin-global.1564590761.js → admin-global.1566486436.js} +0 -0
  55. modules/login-security/js/{admin.1564590761.js → admin.1566486436.js} +0 -0
  56. modules/login-security/js/{jquery-ui-timepicker-addon.1564590761.js → jquery-ui-timepicker-addon.1566486436.js} +0 -0
  57. modules/login-security/js/{jquery.colorbox.1564590761.js → jquery.colorbox.1566486436.js} +0 -0
  58. modules/login-security/js/{jquery.colorbox.min.1564590761.js → jquery.colorbox.min.1566486436.js} +0 -0
  59. modules/login-security/js/{jquery.qrcode.min.1564590761.js → jquery.qrcode.min.1566486436.js} +0 -0
  60. modules/login-security/js/{jquery.tmpl.min.1564590761.js → jquery.tmpl.min.1566486436.js} +0 -0
  61. modules/login-security/js/{login.1564590761.js → login.1566486436.js} +0 -0
  62. modules/login-security/wordfence-login-security.php +1 -1
  63. readme.txt +6 -1
  64. vendor/wordfence/wf-waf/src/init.php +1 -0
  65. vendor/wordfence/wf-waf/src/lib/parser/parser.php +7 -0
  66. vendor/wordfence/wf-waf/src/lib/rules.php +89 -0
  67. vendor/wordfence/wf-waf/src/lib/storage.php +8 -0
  68. vendor/wordfence/wf-waf/src/lib/storage/file.php +40 -2
  69. vendor/wordfence/wf-waf/src/lib/storage/mysql.php +1120 -0
  70. vendor/wordfence/wf-waf/src/lib/utils.php +155 -49
  71. vendor/wordfence/wf-waf/src/lib/waf.php +69 -18
  72. views/waf/option-rules.php +3 -0
  73. views/waf/waf-install.php +1 -1
  74. views/waf/waf-uninstall.php +6 -1
  75. waf/bootstrap.php +86 -22
  76. waf/wfWAFIPBlocksController.php +1 -1
  77. wordfence.php +6 -4
css/{activity-report-widget.1564590761.css → activity-report-widget.1566486436.css} RENAMED
File without changes
css/{diff.1564590761.css → diff.1566486436.css} RENAMED
File without changes
css/{dt_table.1564590761.css → dt_table.1566486436.css} RENAMED
File without changes
css/{fullLog.1564590761.css → fullLog.1566486436.css} RENAMED
File without changes
css/{iptraf.1564590761.css → iptraf.1566486436.css} RENAMED
File without changes
css/{jquery-ui-timepicker-addon.1564590761.css → jquery-ui-timepicker-addon.1566486436.css} RENAMED
File without changes
css/{jquery-ui.min.1564590761.css → jquery-ui.min.1566486436.css} RENAMED
File without changes
css/{jquery-ui.structure.min.1564590761.css → jquery-ui.structure.min.1566486436.css} RENAMED
File without changes
css/{jquery-ui.theme.min.1564590761.css → jquery-ui.theme.min.1566486436.css} RENAMED
File without changes
css/{main.1564590761.css → main.1566486436.css} RENAMED
File without changes
css/{phpinfo.1564590761.css → phpinfo.1566486436.css} RENAMED
File without changes
css/{wf-adminbar.1564590761.css → wf-adminbar.1566486436.css} RENAMED
File without changes
css/{wf-colorbox.1564590761.css → wf-colorbox.1566486436.css} RENAMED
File without changes
css/{wf-font-awesome.1564590761.css → wf-font-awesome.1566486436.css} RENAMED
File without changes
css/{wf-global.1564590761.css → wf-global.1566486436.css} RENAMED
File without changes
css/{wf-ionicons.1564590761.css → wf-ionicons.1566486436.css} RENAMED
File without changes
css/{wf-onboarding.1564590761.css → wf-onboarding.1566486436.css} RENAMED
File without changes
css/{wf-roboto-font.1564590761.css → wf-roboto-font.1566486436.css} RENAMED
File without changes
css/{wfselect2.min.1564590761.css → wfselect2.min.1566486436.css} RENAMED
File without changes
css/{wordfenceBox.1564590761.css → wordfenceBox.1566486436.css} RENAMED
File without changes
js/{Chart.bundle.min.1564590761.js → Chart.bundle.min.1566486436.js} RENAMED
File without changes
js/{admin.1564590761.js → admin.1566486436.js} RENAMED
File without changes
js/{admin.ajaxWatcher.1564590761.js → admin.ajaxWatcher.1566486436.js} RENAMED
File without changes
js/{admin.liveTraffic.1564590761.js → admin.liveTraffic.1566486436.js} RENAMED
File without changes
js/{date.1564590761.js → date.1566486436.js} RENAMED
File without changes
js/{jquery-ui-timepicker-addon.1564590761.js → jquery-ui-timepicker-addon.1566486436.js} RENAMED
File without changes
js/{jquery.colorbox-min.1564590761.js → jquery.colorbox-min.1566486436.js} RENAMED
File without changes
js/{jquery.colorbox.1564590761.js → jquery.colorbox.1566486436.js} RENAMED
File without changes
js/{jquery.dataTables.min.1564590761.js → jquery.dataTables.min.1566486436.js} RENAMED
File without changes
js/{jquery.qrcode.min.1564590761.js → jquery.qrcode.min.1566486436.js} RENAMED
File without changes
js/{jquery.tmpl.min.1564590761.js → jquery.tmpl.min.1566486436.js} RENAMED
File without changes
js/{jquery.tools.min.1564590761.js → jquery.tools.min.1566486436.js} RENAMED
File without changes
js/{knockout-3.3.0.1564590761.js → knockout-3.3.0.1566486436.js} RENAMED
File without changes
js/{wfdashboard.1564590761.js → wfdashboard.1566486436.js} RENAMED
File without changes
js/{wfdropdown.1564590761.js → wfdropdown.1566486436.js} RENAMED
File without changes
js/{wfglobal.1564590761.js → wfglobal.1566486436.js} RENAMED
File without changes
js/{wfpopover.1564590761.js → wfpopover.1566486436.js} RENAMED
File without changes
js/{wfselect2.min.1564590761.js → wfselect2.min.1566486436.js} RENAMED
File without changes
lib/GeoLite2-Country.mmdb CHANGED
Binary file
lib/wfConfig.php CHANGED
@@ -491,6 +491,8 @@ class wfConfig {
491
  wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val, 'synced');
492
  } catch (wfWAFStorageFileException $e) {
493
  error_log($e->getMessage());
 
 
494
  }
495
  }
496
 
@@ -880,7 +882,7 @@ class wfConfig {
880
  }
881
  public static function liveTrafficEnabled(&$overriden = null){
882
  $enabled = self::get('liveTrafficEnabled');
883
- if (WORDFENCE_DISABLE_LIVE_TRAFFIC || function_exists('wpe_site')) {
884
  $enabled = false;
885
  if ($overriden !== null) {
886
  $overriden = true;
@@ -1419,6 +1421,20 @@ Options -ExecCGI
1419
  if (method_exists(wfWAF::getInstance()->getStorageEngine(), 'purgeIPBlocks')) {
1420
  wfWAF::getInstance()->getStorageEngine()->purgeIPBlocks(wfWAFStorageInterface::IP_BLOCKS_BLACKLIST);
1421
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1422
  $saved = true;
1423
  break;
1424
  }
491
  wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val, 'synced');
492
  } catch (wfWAFStorageFileException $e) {
493
  error_log($e->getMessage());
494
+ } catch (wfWAFStorageEngineMySQLiException $e) {
495
+ error_log($e->getMessage());
496
  }
497
  }
498
 
882
  }
883
  public static function liveTrafficEnabled(&$overriden = null){
884
  $enabled = self::get('liveTrafficEnabled');
885
+ if (WORDFENCE_DISABLE_LIVE_TRAFFIC || WF_IS_WP_ENGINE) {
886
  $enabled = false;
887
  if ($overriden !== null) {
888
  $overriden = true;
1421
  if (method_exists(wfWAF::getInstance()->getStorageEngine(), 'purgeIPBlocks')) {
1422
  wfWAF::getInstance()->getStorageEngine()->purgeIPBlocks(wfWAFStorageInterface::IP_BLOCKS_BLACKLIST);
1423
  }
1424
+ if ($value) {
1425
+ $cron = wfWAF::getInstance()->getStorageEngine()->getConfig('cron', array(), 'livewaf');
1426
+ if (!is_array($cron)) {
1427
+ $cron = array();
1428
+ }
1429
+ foreach ($cron as $cronKey => $cronJob) {
1430
+ if ($cronJob instanceof wfWAFCronFetchBlacklistPrefixesEvent) {
1431
+ unset($cron[$cronKey]);
1432
+ }
1433
+ }
1434
+ $cron[] = new wfWAFCronFetchBlacklistPrefixesEvent(time() - 1);
1435
+ wfWAF::getInstance()->getStorageEngine()->setConfig('cron', $cron, 'livewaf');
1436
+ }
1437
+
1438
  $saved = true;
1439
  break;
1440
  }
lib/wfCrawl.php CHANGED
@@ -115,6 +115,11 @@ class wfCrawl {
115
  if ($ip === null) {
116
  $ip = wfUtils::getIP();
117
  }
 
 
 
 
 
118
  if (array_key_exists($ip, $verified)) {
119
  return $verified[$ip];
120
  }
115
  if ($ip === null) {
116
  $ip = wfUtils::getIP();
117
  }
118
+
119
+ if ($ip === null || $ip === false) { //Likely a CLI execution
120
+ return false;
121
+ }
122
+
123
  if (array_key_exists($ip, $verified)) {
124
  return $verified[$ip];
125
  }
lib/wfUtils.php CHANGED
@@ -2337,19 +2337,22 @@ class wfUtils {
2337
  }
2338
 
2339
  public static function wafInstallationType() {
 
 
 
2340
  try {
2341
  $status = (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) ? 'disabled' : wfWaf::getInstance()->getStorageEngine()->getConfig('wafStatus');
2342
  if (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) {
2343
- return "{$status}|const";
2344
  }
2345
  else if (defined('WFWAF_SUBDIRECTORY_INSTALL') && WFWAF_SUBDIRECTORY_INSTALL) {
2346
- return "{$status}|subdir";
2347
  }
2348
  else if (defined('WFWAF_AUTO_PREPEND') && WFWAF_AUTO_PREPEND) {
2349
- return "{$status}|extended";
2350
  }
2351
 
2352
- return "{$status}|basic";
2353
  }
2354
  catch (Exception $e) {
2355
  //Do nothing
2337
  }
2338
 
2339
  public static function wafInstallationType() {
2340
+ $storage = 'file';
2341
+ if (defined('WFWAF_STORAGE_ENGINE')) { $storage = WFWAF_STORAGE_ENGINE; }
2342
+
2343
  try {
2344
  $status = (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) ? 'disabled' : wfWaf::getInstance()->getStorageEngine()->getConfig('wafStatus');
2345
  if (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) {
2346
+ return "{$status}|const|{$storage}";
2347
  }
2348
  else if (defined('WFWAF_SUBDIRECTORY_INSTALL') && WFWAF_SUBDIRECTORY_INSTALL) {
2349
+ return "{$status}|subdir|{$storage}";
2350
  }
2351
  else if (defined('WFWAF_AUTO_PREPEND') && WFWAF_AUTO_PREPEND) {
2352
+ return "{$status}|extended|{$storage}";
2353
  }
2354
 
2355
+ return "{$status}|basic|{$storage}";
2356
  }
2357
  catch (Exception $e) {
2358
  //Do nothing
lib/wordfenceClass.php CHANGED
@@ -156,6 +156,8 @@ class wordfence {
156
  }
157
  } catch (wfWAFStorageFileException $e) {
158
  error_log($e->getMessage());
 
 
159
  }
160
  }
161
  }
@@ -2213,7 +2215,11 @@ SQL
2213
 
2214
  if (empty($_GET['wordfence_syncAttackData'])) {
2215
  $table_wfHits = wfDB::networkTable('wfHits');
2216
- $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
 
 
 
 
2217
  if (get_site_option('wordfence_lastSyncAttackData', 0) < time() - 4) {
2218
  if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
2219
  if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
@@ -2271,6 +2277,8 @@ SQL
2271
  }
2272
  } catch (wfWAFStorageFileException $e) {
2273
  // We don't have anywhere to write files in this scenario.
 
 
2274
  }
2275
  }
2276
 
@@ -4406,6 +4414,11 @@ SQL
4406
  'error' => __('An error occurred while saving the configuration.', 'wordfence'),
4407
  );
4408
  }
 
 
 
 
 
4409
  catch (Exception $e) {
4410
  return array(
4411
  'error' => $e->getMessage(),
@@ -5684,6 +5697,8 @@ HTML;
5684
  }
5685
  } catch (wfWAFStorageFileException $e) {
5686
  error_log($e->getMessage());
 
 
5687
  }
5688
  }
5689
 
@@ -6387,6 +6402,17 @@ JQUERY;
6387
  $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH);
6388
  $storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please
6389
  update permissions on the parent directory so the web server can write to it.';
 
 
 
 
 
 
 
 
 
 
 
6390
  }
6391
 
6392
  require(dirname(__FILE__) . '/menu_options.php');
@@ -6441,6 +6467,17 @@ JQUERY;
6441
  $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH);
6442
  $storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please
6443
  update permissions on the parent directory so the web server can write to it.';
 
 
 
 
 
 
 
 
 
 
 
6444
  }
6445
 
6446
  if (isset($_GET['subpage']) && $_GET['subpage'] == 'waf_options') {
@@ -7400,33 +7437,35 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
7400
 
7401
  $whitelistedURLParams = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLParams', array(), 'livewaf');
7402
  $data['whitelistedURLParams'] = array();
7403
- foreach ($whitelistedURLParams as $urlParamKey => $rules) {
7404
- list($path, $paramKey) = explode('|', $urlParamKey);
7405
- $whitelistData = null;
7406
- foreach ($rules as $ruleID => $whitelistedData) {
7407
- if ($whitelistData === null) {
7408
- $whitelistData = $whitelistedData;
7409
- continue;
7410
- }
7411
- if ($ruleID === 'all') {
7412
- $whitelistData = $whitelistedData;
7413
- break;
 
 
7414
  }
7415
- }
7416
 
7417
- if (is_array($whitelistData) && array_key_exists('userID', $whitelistData) && function_exists('get_user_by')) {
7418
- $user = get_user_by('id', $whitelistData['userID']);
7419
- if ($user) {
7420
- $whitelistData['username'] = $user->user_login;
 
7421
  }
7422
- }
7423
 
7424
- $data['whitelistedURLParams'][] = array(
7425
- 'path' => $path,
7426
- 'paramKey' => $paramKey,
7427
- 'ruleID' => array_keys($rules),
7428
- 'data' => $whitelistData,
7429
- );
 
7430
  }
7431
 
7432
  $data['disabledRules'] = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('disabledRules');
@@ -7456,7 +7495,7 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
7456
 
7457
  $currentAutoPrependFile = ini_get('auto_prepend_file');
7458
  $currentAutoPrepend = null;
7459
- if (isset($_POST['currentAutoPrepend'])) {
7460
  $currentAutoPrepend = $_POST['currentAutoPrepend'];
7461
  }
7462
 
@@ -7659,7 +7698,7 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
7659
  return $response;
7660
  }
7661
  else { //.user.ini and .htaccess modified if applicable and waiting period elapsed or otherwise ready to advance to next step
7662
- if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) { //.user.ini modified, but the WAF is still enabled
7663
  $retryAttempted = (isset($_POST['retryAttempted']) && $_POST['retryAttempted']);
7664
  $userIniError = '<p class="wf-error">';
7665
  $userIniError .= __('Extended Protection Mode has not been disabled. This may be because <code>auto_prepend_file</code> is configured somewhere else or the value is still cached by PHP.', 'wordfence');
@@ -8109,20 +8148,29 @@ ALERTMSG;
8109
  $log = self::getLog();
8110
  $waf = wfWAF::getInstance();
8111
  $table_wfHits = wfDB::networkTable('wfHits');
8112
- $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
 
 
 
 
 
8113
  if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
8114
  $attackData = $waf->getStorageEngine()->getNewestAttackDataArray($lastAttackMicroseconds);
8115
  if ($attackData) {
8116
  foreach ($attackData as $request) {
8117
- if (count($request) !== 9 && count($request) !== 10 /* with metadata */) {
8118
  continue;
8119
  }
8120
 
8121
  list($logTimeMicroseconds, $requestTime, $ip, $learningMode, $paramKey, $paramValue, $failedRules, $ssl, $requestString) = $request;
8122
  $metadata = null;
8123
- if (count($request) == 10) {
 
8124
  $metadata = $request[9];
8125
  }
 
 
 
8126
 
8127
  // Skip old entries and hits in learning mode, since they'll get picked up anyways.
8128
  if ($logTimeMicroseconds <= $lastAttackMicroseconds || $learningMode) {
@@ -8132,6 +8180,10 @@ ALERTMSG;
8132
  $statusCode = 403;
8133
 
8134
  $hit = new wfRequestModel();
 
 
 
 
8135
  $hit->attackLogTime = $logTimeMicroseconds;
8136
  $hit->ctime = $requestTime;
8137
  $hit->IP = wfUtils::inet_pton($ip);
156
  }
157
  } catch (wfWAFStorageFileException $e) {
158
  error_log($e->getMessage());
159
+ } catch (wfWAFStorageEngineMySQLiException $e) {
160
+ error_log($e->getMessage());
161
  }
162
  }
163
  }
2215
 
2216
  if (empty($_GET['wordfence_syncAttackData'])) {
2217
  $table_wfHits = wfDB::networkTable('wfHits');
2218
+ if ($waf->getStorageEngine() instanceof wfWAFStorageMySQL) {
2219
+ $lastAttackMicroseconds = floatval($waf->getStorageEngine()->getConfig('lastAttackDataTruncateTime'));
2220
+ } else {
2221
+ $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
2222
+ }
2223
  if (get_site_option('wordfence_lastSyncAttackData', 0) < time() - 4) {
2224
  if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
2225
  if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
2277
  }
2278
  } catch (wfWAFStorageFileException $e) {
2279
  // We don't have anywhere to write files in this scenario.
2280
+ } catch (wfWAFStorageEngineMySQLiException $e) {
2281
+ // Ignore and continue
2282
  }
2283
  }
2284
 
4414
  'error' => __('An error occurred while saving the configuration.', 'wordfence'),
4415
  );
4416
  }
4417
+ catch (wfWAFStorageEngineMySQLiException $e) {
4418
+ return array(
4419
+ 'error' => __('An error occurred while saving the configuration.', 'wordfence'),
4420
+ );
4421
+ }
4422
  catch (Exception $e) {
4423
  return array(
4424
  'error' => $e->getMessage(),
5697
  }
5698
  } catch (wfWAFStorageFileException $e) {
5699
  error_log($e->getMessage());
5700
+ } catch (wfWAFStorageEngineMySQLiException $e) {
5701
+ error_log($e->getMessage());
5702
  }
5703
  }
5704
 
6402
  $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH);
6403
  $storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please
6404
  update permissions on the parent directory so the web server can write to it.';
6405
+ } catch (wfWAFStorageEngineMySQLiException $e) {
6406
+ $wafData = array(
6407
+ 'learningMode' => false,
6408
+ 'rules' => array(),
6409
+ 'whitelistedURLParams' => array(),
6410
+ 'disabledRules' => array(),
6411
+ 'isPaid' => (bool) wfConfig::get('isPaid', 0),
6412
+ );
6413
+ $logPath = null;
6414
+ $storageExceptionMessage = 'An error occured when fetching the WAF configuration from the database. <pre>' .
6415
+ esc_html($e->getMessage()) . '</pre>';
6416
  }
6417
 
6418
  require(dirname(__FILE__) . '/menu_options.php');
6467
  $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH);
6468
  $storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please
6469
  update permissions on the parent directory so the web server can write to it.';
6470
+ } catch (wfWAFStorageEngineMySQLiException $e) {
6471
+ $wafData = array(
6472
+ 'learningMode' => false,
6473
+ 'rules' => array(),
6474
+ 'whitelistedURLParams' => array(),
6475
+ 'disabledRules' => array(),
6476
+ 'isPaid' => (bool) wfConfig::get('isPaid', 0),
6477
+ );
6478
+ $logPath = null;
6479
+ $storageExceptionMessage = 'An error occured when fetching the WAF configuration from the database. <pre>' .
6480
+ esc_html($e->getMessage()) . '</pre>';
6481
  }
6482
 
6483
  if (isset($_GET['subpage']) && $_GET['subpage'] == 'waf_options') {
7437
 
7438
  $whitelistedURLParams = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedURLParams', array(), 'livewaf');
7439
  $data['whitelistedURLParams'] = array();
7440
+ if (is_array($whitelistedURLParams)) {
7441
+ foreach ($whitelistedURLParams as $urlParamKey => $rules) {
7442
+ list($path, $paramKey) = explode('|', $urlParamKey);
7443
+ $whitelistData = null;
7444
+ foreach ($rules as $ruleID => $whitelistedData) {
7445
+ if ($whitelistData === null) {
7446
+ $whitelistData = $whitelistedData;
7447
+ continue;
7448
+ }
7449
+ if ($ruleID === 'all') {
7450
+ $whitelistData = $whitelistedData;
7451
+ break;
7452
+ }
7453
  }
 
7454
 
7455
+ if (is_array($whitelistData) && array_key_exists('userID', $whitelistData) && function_exists('get_user_by')) {
7456
+ $user = get_user_by('id', $whitelistData['userID']);
7457
+ if ($user) {
7458
+ $whitelistData['username'] = $user->user_login;
7459
+ }
7460
  }
 
7461
 
7462
+ $data['whitelistedURLParams'][] = array(
7463
+ 'path' => $path,
7464
+ 'paramKey' => $paramKey,
7465
+ 'ruleID' => array_keys($rules),
7466
+ 'data' => $whitelistData,
7467
+ );
7468
+ }
7469
  }
7470
 
7471
  $data['disabledRules'] = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('disabledRules');
7495
 
7496
  $currentAutoPrependFile = ini_get('auto_prepend_file');
7497
  $currentAutoPrepend = null;
7498
+ if (isset($_POST['currentAutoPrepend']) && !WF_IS_WP_ENGINE) {
7499
  $currentAutoPrepend = $_POST['currentAutoPrepend'];
7500
  }
7501
 
7698
  return $response;
7699
  }
7700
  else { //.user.ini and .htaccess modified if applicable and waiting period elapsed or otherwise ready to advance to next step
7701
+ if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL && !WF_IS_WP_ENGINE) { //.user.ini modified, but the WAF is still enabled
7702
  $retryAttempted = (isset($_POST['retryAttempted']) && $_POST['retryAttempted']);
7703
  $userIniError = '<p class="wf-error">';
7704
  $userIniError .= __('Extended Protection Mode has not been disabled. This may be because <code>auto_prepend_file</code> is configured somewhere else or the value is still cached by PHP.', 'wordfence');
8148
  $log = self::getLog();
8149
  $waf = wfWAF::getInstance();
8150
  $table_wfHits = wfDB::networkTable('wfHits');
8151
+ if ($waf->getStorageEngine() instanceof wfWAFStorageMySQL) {
8152
+ $lastAttackMicroseconds = floatval($waf->getStorageEngine()->getConfig('lastAttackDataTruncateTime'));
8153
+ } else {
8154
+ $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
8155
+ }
8156
+
8157
  if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
8158
  $attackData = $waf->getStorageEngine()->getNewestAttackDataArray($lastAttackMicroseconds);
8159
  if ($attackData) {
8160
  foreach ($attackData as $request) {
8161
+ if (count($request) !== 9 && count($request) !== 10 /* with metadata */ && count($request) !== 11) {
8162
  continue;
8163
  }
8164
 
8165
  list($logTimeMicroseconds, $requestTime, $ip, $learningMode, $paramKey, $paramValue, $failedRules, $ssl, $requestString) = $request;
8166
  $metadata = null;
8167
+ $recordID = null;
8168
+ if (array_key_exists(9, $request)) {
8169
  $metadata = $request[9];
8170
  }
8171
+ if (array_key_exists(10, $request)) {
8172
+ $recordID = $request[10];
8173
+ }
8174
 
8175
  // Skip old entries and hits in learning mode, since they'll get picked up anyways.
8176
  if ($logTimeMicroseconds <= $lastAttackMicroseconds || $learningMode) {
8180
  $statusCode = 403;
8181
 
8182
  $hit = new wfRequestModel();
8183
+ if (is_numeric($recordID)) {
8184
+ $hit->id = $recordID;
8185
+ }
8186
+
8187
  $hit->attackLogTime = $logTimeMicroseconds;
8188
  $hit->ctime = $requestTime;
8189
  $hit->IP = wfUtils::inet_pton($ip);
modules/login-security/css/{admin-global.1564590761.css → admin-global.1566486436.css} RENAMED
File without changes
modules/login-security/css/{admin.1564590761.css → admin.1566486436.css} RENAMED
File without changes
modules/login-security/css/{colorbox.1564590761.css → colorbox.1566486436.css} RENAMED
File without changes
modules/login-security/css/{font-awesome.1564590761.css → font-awesome.1566486436.css} RENAMED
File without changes
modules/login-security/css/{ionicons.1564590761.css → ionicons.1566486436.css} RENAMED
File without changes
modules/login-security/css/{jquery-ui-timepicker-addon.1564590761.css → jquery-ui-timepicker-addon.1566486436.css} RENAMED
File without changes
modules/login-security/css/{jquery-ui.min.1564590761.css → jquery-ui.min.1566486436.css} RENAMED
File without changes
modules/login-security/css/{jquery-ui.structure.min.1564590761.css → jquery-ui.structure.min.1566486436.css} RENAMED
File without changes
modules/login-security/css/{jquery-ui.theme.min.1564590761.css → jquery-ui.theme.min.1566486436.css} RENAMED
File without changes
modules/login-security/css/{login.1564590761.css → login.1566486436.css} RENAMED
File without changes
modules/login-security/js/{admin-global.1564590761.js → admin-global.1566486436.js} RENAMED
File without changes
modules/login-security/js/{admin.1564590761.js → admin.1566486436.js} RENAMED
File without changes
modules/login-security/js/{jquery-ui-timepicker-addon.1564590761.js → jquery-ui-timepicker-addon.1566486436.js} RENAMED
File without changes
modules/login-security/js/{jquery.colorbox.1564590761.js → jquery.colorbox.1566486436.js} RENAMED
File without changes
modules/login-security/js/{jquery.colorbox.min.1564590761.js → jquery.colorbox.min.1566486436.js} RENAMED
File without changes
modules/login-security/js/{jquery.qrcode.min.1564590761.js → jquery.qrcode.min.1566486436.js} RENAMED
File without changes
modules/login-security/js/{jquery.tmpl.min.1564590761.js → jquery.tmpl.min.1566486436.js} RENAMED
File without changes
modules/login-security/js/{login.1564590761.js → login.1566486436.js} RENAMED
File without changes
modules/login-security/wordfence-login-security.php CHANGED
@@ -27,7 +27,7 @@ else {
27
  define('WORDFENCE_LS_FROM_CORE', ($wfCoreActive && isset($wfCoreLoading) && $wfCoreLoading));
28
 
29
  define('WORDFENCE_LS_VERSION', '1.0.3');
30
- define('WORDFENCE_LS_BUILD_NUMBER', '1564590761');
31
 
32
  if (!defined('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES')) { define('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES', 15); }
33
 
27
  define('WORDFENCE_LS_FROM_CORE', ($wfCoreActive && isset($wfCoreLoading) && $wfCoreLoading));
28
 
29
  define('WORDFENCE_LS_VERSION', '1.0.3');
30
+ define('WORDFENCE_LS_BUILD_NUMBER', '1566486436');
31
 
32
  if (!defined('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES')) { define('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES', 15); }
33
 
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: security, firewall, malware scanner, web application firewall, two factor
4
  Requires at least: 3.9
5
  Requires PHP: 5.3
6
  Tested up to: 5.2
7
- Stable tag: 7.3.6
8
 
9
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
10
 
@@ -185,6 +185,11 @@ Secure your website with Wordfence.
185
 
186
  == Changelog ==
187
 
 
 
 
 
 
188
  = 7.3.6 - July 31, 2019 =
189
  * Improvement: Multiple "php.ini file in core directory" issues are now consolidated into a single issue for clearer scan results.
190
  * Improvement: The AJAX error detection for false positive WAF blocks now better detects and processes the response for presenting the whitelisting prompt.
4
  Requires at least: 3.9
5
  Requires PHP: 5.3
6
  Tested up to: 5.2
7
+ Stable tag: 7.4.0
8
 
9
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
10
 
185
 
186
  == Changelog ==
187
 
188
+ = 7.4.0 - August 22, 2019 =
189
+ * Improvement: Added a MySQL-based configuration and data storage for the WAF to expand the number of hosting environments supported. For more detail, see: https://www.wordfence.com/help/firewall/mysqli-storage-engine/
190
+ * Improvement: Updated bundled GeoIP database.
191
+ * Fix: Fixed several console notices when running via the CLI.
192
+
193
  = 7.3.6 - July 31, 2019 =
194
  * Improvement: Multiple "php.ini file in core directory" issues are now consolidated into a single issue for clearer scan results.
195
  * Improvement: The AJAX error detection for false positive WAF blocks now better detects and processes the response for presenting the whitelisting prompt.
vendor/wordfence/wf-waf/src/init.php CHANGED
@@ -21,6 +21,7 @@ require_once WFWAF_LIB_PATH . 'xmlrpc.php';
21
 
22
  require_once WFWAF_LIB_PATH . 'storage.php';
23
  require_once WFWAF_LIB_PATH . 'storage/file.php';
 
24
 
25
  require_once WFWAF_LIB_PATH . 'config.php';
26
 
21
 
22
  require_once WFWAF_LIB_PATH . 'storage.php';
23
  require_once WFWAF_LIB_PATH . 'storage/file.php';
24
+ require_once WFWAF_LIB_PATH . 'storage/mysql.php';
25
 
26
  require_once WFWAF_LIB_PATH . 'config.php';
27
 
vendor/wordfence/wf-waf/src/lib/parser/parser.php CHANGED
@@ -797,6 +797,13 @@ class wfWAFRuleVariable {
797
  $this->value = $value;
798
  }
799
 
 
 
 
 
 
 
 
800
  public function render() {
801
  return sprintf('new %s($this, %s, %s)', get_class($this),
802
  var_export($this->getName(), true), var_export($this->getValue(), true));
797
  $this->value = $value;
798
  }
799
 
800
+ public function __sleep() {
801
+ return array(
802
+ 'name',
803
+ 'value',
804
+ );
805
+ }
806
+
807
  public function render() {
808
  return sprintf('new %s($this, %s, %s)', get_class($this),
809
  var_export($this->getName(), true), var_export($this->getValue(), true));
vendor/wordfence/wf-waf/src/lib/rules.php CHANGED
@@ -28,6 +28,7 @@ class wfWAFRule implements wfWAFRuleInterface {
28
  private $description;
29
  private $whitelist;
30
  private $action;
 
31
  private $comparisonGroup;
32
  /**
33
  * @var wfWAF
@@ -100,6 +101,19 @@ class wfWAFRule implements wfWAFRuleInterface {
100
  $this->setComparisonGroup($comparisonGroup);
101
  }
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  /**
104
  * @return string
105
  */
@@ -295,6 +309,9 @@ RULE
295
  */
296
  public function setWAF($waf) {
297
  $this->waf = $waf;
 
 
 
298
  }
299
  }
300
 
@@ -335,6 +352,14 @@ class wfWAFRuleLogicalOperator implements wfWAFRuleInterface {
335
  $this->setComparison($comparison);
336
  }
337
 
 
 
 
 
 
 
 
 
338
  /**
339
  * @return string
340
  * @throws wfWAFRuleLogicalOperatorException
@@ -504,6 +529,15 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
504
  $this->setSubjects($subjects);
505
  }
506
 
 
 
 
 
 
 
 
 
 
507
  /**
508
  * @param string|array $subject
509
  * @return string
@@ -1174,6 +1208,16 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
1174
  */
1175
  public function setWAF($waf) {
1176
  $this->waf = $waf;
 
 
 
 
 
 
 
 
 
 
1177
  }
1178
 
1179
  /**
@@ -1196,6 +1240,8 @@ class wfWAFRuleComparisonGroup implements wfWAFRuleInterface {
1196
  private $items = array();
1197
  private $failedComparisons = array();
1198
  private $result = false;
 
 
1199
  /**
1200
  * @var wfWAFRule
1201
  */
@@ -1208,6 +1254,12 @@ class wfWAFRuleComparisonGroup implements wfWAFRuleInterface {
1208
  }
1209
  }
1210
 
 
 
 
 
 
 
1211
  public function add($item) {
1212
  $this->items[] = $item;
1213
  }
@@ -1373,6 +1425,25 @@ class wfWAFRuleComparisonGroup implements wfWAFRuleInterface {
1373
  public function setRule($rule) {
1374
  $this->rule = $rule;
1375
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1376
  }
1377
 
1378
  class wfWAFRuleComparisonFailure {
@@ -1410,6 +1481,17 @@ class wfWAFRuleComparisonFailure {
1410
  $this->setMatches($matches);
1411
  }
1412
 
 
 
 
 
 
 
 
 
 
 
 
1413
  /**
1414
  * @return mixed
1415
  */
@@ -1532,6 +1614,13 @@ class wfWAFRuleComparisonSubject {
1532
  $this->filters = $filters;
1533
  }
1534
 
 
 
 
 
 
 
 
1535
  /**
1536
  * @return mixed|null
1537
  */
28
  private $description;
29
  private $whitelist;
30
  private $action;
31
+ /** @var wfWAFRuleComparisonGroup */
32
  private $comparisonGroup;
33
  /**
34
  * @var wfWAF
101
  $this->setComparisonGroup($comparisonGroup);
102
  }
103
 
104
+ public function __sleep() {
105
+ return array(
106
+ 'ruleID',
107
+ 'type',
108
+ 'category',
109
+ 'score',
110
+ 'description',
111
+ 'whitelist',
112
+ 'action',
113
+ 'comparisonGroup',
114
+ );
115
+ }
116
+
117
  /**
118
  * @return string
119
  */
309
  */
310
  public function setWAF($waf) {
311
  $this->waf = $waf;
312
+ if ($this->comparisonGroup) {
313
+ $this->comparisonGroup->setWAF($waf);
314
+ }
315
  }
316
  }
317
 
352
  $this->setComparison($comparison);
353
  }
354
 
355
+ public function __sleep() {
356
+ return array(
357
+ 'operator',
358
+ 'currentValue',
359
+ 'comparison',
360
+ );
361
+ }
362
+
363
  /**
364
  * @return string
365
  * @throws wfWAFRuleLogicalOperatorException
529
  $this->setSubjects($subjects);
530
  }
531
 
532
+ public function __sleep() {
533
+ return array(
534
+ 'rule',
535
+ 'action',
536
+ 'expected',
537
+ 'subjects',
538
+ );
539
+ }
540
+
541
  /**
542
  * @param string|array $subject
543
  * @return string
1208
  */
1209
  public function setWAF($waf) {
1210
  $this->waf = $waf;
1211
+ if (is_array($this->subjects)) {
1212
+ foreach ($this->subjects as $subject) {
1213
+ if (is_object($subject) && method_exists($subject, 'setWAF')) {
1214
+ $subject->setWAF($waf);
1215
+ }
1216
+ }
1217
+ }
1218
+ if (is_object($this->expected) && method_exists($this->expected, 'setWAF')) {
1219
+ $this->expected->setWAF($waf);
1220
+ }
1221
  }
1222
 
1223
  /**
1240
  private $items = array();
1241
  private $failedComparisons = array();
1242
  private $result = false;
1243
+ private $waf;
1244
+
1245
  /**
1246
  * @var wfWAFRule
1247
  */
1254
  }
1255
  }
1256
 
1257
+ public function __sleep() {
1258
+ return array(
1259
+ 'items',
1260
+ );
1261
+ }
1262
+
1263
  public function add($item) {
1264
  $this->items[] = $item;
1265
  }
1425
  public function setRule($rule) {
1426
  $this->rule = $rule;
1427
  }
1428
+
1429
+ /**
1430
+ * @return mixed
1431
+ */
1432
+ public function getWAF() {
1433
+ return $this->waf;
1434
+ }
1435
+
1436
+ /**
1437
+ * @param mixed $waf
1438
+ */
1439
+ public function setWAF($waf) {
1440
+ $this->waf = $waf;
1441
+ foreach ($this->items as $item) {
1442
+ if (is_object($item) && method_exists($item, 'setWAF')) {
1443
+ $item->setWAF($waf);
1444
+ }
1445
+ }
1446
+ }
1447
  }
1448
 
1449
  class wfWAFRuleComparisonFailure {
1481
  $this->setMatches($matches);
1482
  }
1483
 
1484
+ public function __sleep() {
1485
+ return array(
1486
+ 'paramKey',
1487
+ 'expected',
1488
+ 'action',
1489
+ 'multiplier',
1490
+ 'paramValue',
1491
+ 'matches',
1492
+ );
1493
+ }
1494
+
1495
  /**
1496
  * @return mixed
1497
  */
1614
  $this->filters = $filters;
1615
  }
1616
 
1617
+ public function __sleep() {
1618
+ return array(
1619
+ 'subject',
1620
+ 'filters',
1621
+ );
1622
+ }
1623
+
1624
  /**
1625
  * @return mixed|null
1626
  */
vendor/wordfence/wf-waf/src/lib/storage.php CHANGED
@@ -67,5 +67,13 @@ interface wfWAFStorageInterface {
67
  public function getRulesDSLCacheFile();
68
 
69
  public function isAttackDataFull();
 
 
 
 
 
 
 
 
70
  }
71
  }
67
  public function getRulesDSLCacheFile();
68
 
69
  public function isAttackDataFull();
70
+
71
+ public function vacuum();
72
+
73
+ public function getRules();
74
+
75
+ public function setRules($rules);
76
+
77
+ public function needsInitialRules();
78
  }
79
  }
vendor/wordfence/wf-waf/src/lib/storage/file.php CHANGED
@@ -6,7 +6,12 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
6
  const LOG_FILE_HEADER = "<?php exit('Access denied'); __halt_compiler(); ?>\n";
7
  const LOG_INFO_HEADER = "******************************************************************\nThis file is used by the Wordfence Web Application Firewall. Read \nmore at https://docs.wordfence.com/en/Web_Application_Firewall_FAQ\n******************************************************************\n";
8
  const IP_BLOCK_RECORD_SIZE = 24;
9
-
 
 
 
 
 
10
  public static function allowFileWriting() {
11
  if (defined('WFWAF_ALWAYS_ALLOW_FILE_WRITING') && WFWAF_ALWAYS_ALLOW_FILE_WRITING) {
12
  return true;
@@ -123,6 +128,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
123
  */
124
  private $ipCacheFile;
125
  private $configFile;
 
126
  private $rulesDSLCacheFile;
127
  private $dataChanged = array();
128
  private $data = array();
@@ -139,12 +145,14 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
139
  * @param string|null $attackDataFile
140
  * @param string|null $ipCacheFile
141
  * @param string|null $configFile
 
142
  * @param null $rulesDSLCacheFile
143
  */
144
- public function __construct($attackDataFile = null, $ipCacheFile = null, $configFile = null, $rulesDSLCacheFile = null) {
145
  $this->setAttackDataFile($attackDataFile);
146
  $this->setIPCacheFile($ipCacheFile);
147
  $this->setConfigFile($configFile);
 
148
  $this->setRulesDSLCacheFile($rulesDSLCacheFile);
149
  }
150
 
@@ -884,6 +892,36 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
884
  public function setAttackDataEngine($attackDataEngine) {
885
  $this->attackDataEngine = $attackDataEngine;
886
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
  }
888
 
889
  class wfWAFAttackDataStorageFileEngine {
6
  const LOG_FILE_HEADER = "<?php exit('Access denied'); __halt_compiler(); ?>\n";
7
  const LOG_INFO_HEADER = "******************************************************************\nThis file is used by the Wordfence Web Application Firewall. Read \nmore at https://docs.wordfence.com/en/Web_Application_Firewall_FAQ\n******************************************************************\n";
8
  const IP_BLOCK_RECORD_SIZE = 24;
9
+ private $rules;
10
+ private $failScores;
11
+ private $variables;
12
+ private $whitelistedParams;
13
+ private $blacklistedParams;
14
+
15
  public static function allowFileWriting() {
16
  if (defined('WFWAF_ALWAYS_ALLOW_FILE_WRITING') && WFWAF_ALWAYS_ALLOW_FILE_WRITING) {
17
  return true;
128
  */
129
  private $ipCacheFile;
130
  private $configFile;
131
+ private $rulesFile;
132
  private $rulesDSLCacheFile;
133
  private $dataChanged = array();
134
  private $data = array();
145
  * @param string|null $attackDataFile
146
  * @param string|null $ipCacheFile
147
  * @param string|null $configFile
148
+ * @param string|null $rulesFile
149
  * @param null $rulesDSLCacheFile
150
  */
151
+ public function __construct($attackDataFile = null, $ipCacheFile = null, $configFile = null, $rulesFile = null, $rulesDSLCacheFile = null) {
152
  $this->setAttackDataFile($attackDataFile);
153
  $this->setIPCacheFile($ipCacheFile);
154
  $this->setConfigFile($configFile);
155
+ $this->setRulesFile($rulesFile);
156
  $this->setRulesDSLCacheFile($rulesDSLCacheFile);
157
  }
158
 
892
  public function setAttackDataEngine($attackDataEngine) {
893
  $this->attackDataEngine = $attackDataEngine;
894
  }
895
+
896
+ public function getRules() {
897
+ throw new wfWAFStorageFileException('wfWAFStorageFile::getRules not implemented.');
898
+ }
899
+
900
+ public function setRules($rules) {
901
+ throw new wfWAFStorageFileException('wfWAFStorageFile::getRules not implemented.');
902
+ }
903
+
904
+ public function needsInitialRules() {
905
+ if (file_exists($this->getRulesFile())) {
906
+ return is_writeable($this->getRulesFile()) && !filesize($this->getRulesFile());
907
+ } else {
908
+ return is_writeable(dirname($this->getRulesFile()));
909
+ }
910
+ }
911
+
912
+ /**
913
+ * @return mixed
914
+ */
915
+ public function getRulesFile() {
916
+ return $this->rulesFile;
917
+ }
918
+
919
+ /**
920
+ * @param mixed $rulesFile
921
+ */
922
+ public function setRulesFile($rulesFile) {
923
+ $this->rulesFile = $rulesFile;
924
+ }
925
  }
926
 
927
  class wfWAFAttackDataStorageFileEngine {
vendor/wordfence/wf-waf/src/lib/storage/mysql.php ADDED
@@ -0,0 +1,1120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ */
6
+ class wfWAFStorageMySQL implements wfWAFStorageInterface {
7
+
8
+ private $_usingLowercase;
9
+
10
+ /**
11
+ * @var wfWAFStorageEngineDatabase
12
+ */
13
+ private $db;
14
+ /**
15
+ * @var string
16
+ */
17
+ private $tablePrefix;
18
+ private $uninstalled;
19
+ private $dataChanged = false;
20
+ private $data = array();
21
+ private $dataToSave = array();
22
+
23
+ public $installing = false;
24
+
25
+ /**
26
+ * @param wfWAFStorageEngineDatabase $engine
27
+ * @param string $tablePrefix
28
+ */
29
+ public function __construct($engine, $tablePrefix = 'wp_') {
30
+ $this->db = $engine;
31
+ $this->tablePrefix = $tablePrefix;
32
+ }
33
+
34
+ public function usingLowercase() {
35
+ if ($this->_usingLowercase === null) {
36
+ $table = $this->tablePrefix . 'wfConfig';
37
+ $tableExists = $this->getDb()->get_var("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND BINARY TABLE_NAME='$table'");
38
+ $this->_usingLowercase = $tableExists !== $table;
39
+ }
40
+ return $this->_usingLowercase;
41
+ }
42
+
43
+ /**
44
+ * Returns the table with the site (single site installations) or network (multisite) prefix added.
45
+ *
46
+ * @param string $table
47
+ * @param bool $applyCaseConversion Whether or not to convert the table case to what is actually in use.
48
+ * @return string
49
+ */
50
+ public function networkTable($table, $applyCaseConversion = true) {
51
+ if ($this->usingLowercase() && $applyCaseConversion) {
52
+ $table = strtolower($table);
53
+ }
54
+ return $this->tablePrefix . $table;
55
+ }
56
+
57
+ /**
58
+ * Check if there's attack before a certain timestamp.
59
+ *
60
+ * @param int $olderThan
61
+ * @return bool
62
+ */
63
+ public function hasPreviousAttackData($olderThan) {
64
+ $table = $this->networkTable('wfHits');
65
+ $lastAttackDataTruncateTime = floatval($this->getConfig('lastAttackDataTruncateTime'));
66
+ $count = $this->db->get_var('SELECT count(*) FROM ' . $table . ' where attackLogTime < ? and attackLogTime > ?', array(
67
+ sprintf('%.6f', $olderThan),
68
+ $lastAttackDataTruncateTime,
69
+ ));
70
+ return $count > 0;
71
+ }
72
+
73
+ /**
74
+ * Check if there's attack data after a given timestamp.
75
+ *
76
+ * @param int $newerThan
77
+ * @return bool
78
+ */
79
+ public function hasNewerAttackData($newerThan) {
80
+ $table = $this->networkTable('wfHits');
81
+ $lastAttackDataTruncateTime = floatval($this->getConfig('lastAttackDataTruncateTime'));
82
+ $count = $this->db->get_var('SELECT count(*) FROM ' . $table . ' where attackLogTime > ?', array(
83
+ sprintf('%.6f', max($newerThan, $lastAttackDataTruncateTime)),
84
+ ));
85
+ return $count > 0;
86
+ }
87
+
88
+ /**
89
+ * Get all attack data.
90
+ *
91
+ *
92
+ */
93
+ public function getAttackData() {
94
+ $table = $this->networkTable('wfHits');
95
+ $lastAttackDataTruncateTime = floatval($this->getConfig('lastAttackDataTruncateTime'));
96
+ $results = $this->db->get_results('SELECT * FROM ' . $table . ' WHERE attackLogTime > ?', array(
97
+ $lastAttackDataTruncateTime,
98
+ ));
99
+
100
+ $data = array();
101
+ foreach ($results as $row) {
102
+ $actionData = wfWAFUtils::json_decode($row['actionData'], true);
103
+ $data[] = array(
104
+ $row['attackLogTime'],
105
+ $row['ctime'],
106
+ wfWAFUtils::inet_ntop($row['IP']),
107
+ (array_key_exists('learningMode', $actionData) ? $actionData['learningMode'] : 0),
108
+ (array_key_exists('paramKey', $actionData) ? $actionData['paramKey'] : false),
109
+ (array_key_exists('paramValue', $actionData) ? $actionData['paramValue'] : false),
110
+ (array_key_exists('failedRules', $actionData) ? $actionData['failedRules'] : ''),
111
+ strpos($row['URL'], 'https') === 0 ? 1 : 0,
112
+ (array_key_exists('fullRequest', $actionData) ? $actionData['fullRequest'] : ''),
113
+ );
114
+ }
115
+ return wfWAFUtils::json_encode($data);
116
+ }
117
+
118
+ /**
119
+ * Get all attack data in array format.
120
+ */
121
+ public function getAttackDataArray() {
122
+ return $this->getNewestAttackDataArray(floatval($this->getConfig('lastAttackDataTruncateTime')));
123
+ }
124
+
125
+ /**
126
+ * Get attack data after a certain timestamp in array format.
127
+ *
128
+ * @param int $newerThan
129
+ * @return array
130
+ */
131
+ public function getNewestAttackDataArray($newerThan) {
132
+ $table = $this->networkTable('wfHits');
133
+ $results = $this->db->get_results('SELECT * FROM ' . $table . ' WHERE attackLogTime > ?', array(
134
+ $newerThan,
135
+ ));
136
+
137
+ $data = array();
138
+ foreach ($results as $row) {
139
+ $actionData = wfWAFUtils::json_decode($row['actionData'], true);
140
+ $data[] = array(
141
+ $row['attackLogTime'],
142
+ $row['ctime'],
143
+ wfWAFUtils::inet_ntop($row['IP']),
144
+ (array_key_exists('learningMode', $actionData) ? $actionData['learningMode'] : 0),
145
+ (array_key_exists('paramKey', $actionData) ? base64_decode($actionData['paramKey']) : false),
146
+ (array_key_exists('paramValue', $actionData) ? base64_decode($actionData['paramValue']) : false),
147
+ (array_key_exists('failedRules', $actionData) ? $actionData['failedRules'] : ''),
148
+ strpos($row['URL'], 'https') === 0 ? 1 : 0,
149
+ (array_key_exists('fullRequest', $actionData) ? base64_decode($actionData['fullRequest']) : ''),
150
+ (array_key_exists('requestMetadata', $actionData) ? $actionData['requestMetadata'] : ''),
151
+ $row['id'],
152
+ );
153
+ }
154
+ return $data;
155
+ }
156
+
157
+ /**
158
+ * I don't think this will be needed for what it's used for in the plugin.
159
+ */
160
+ public function truncateAttackData() {
161
+ $this->setConfig('lastAttackDataTruncateTime', microtime(true));
162
+ return true;
163
+ }
164
+
165
+ /**
166
+ * Insert request into wfHits.
167
+ *
168
+ * @param array $failedRules
169
+ * @param string $failedParamKey
170
+ * @param string $failedParamValue
171
+ * @param wfWAFRequestInterface $request
172
+ * @param mixed $_
173
+ * @return mixed
174
+ */
175
+ public function logAttack($failedRules, $failedParamKey, $failedParamValue, $request, $_ = null) {
176
+ $table = $this->networkTable('wfHits');
177
+
178
+ $failedRulesString = '';
179
+ $actionDescription = '';
180
+ if (is_array($failedRules)) {
181
+ /**
182
+ * @var int $index
183
+ * @var wfWAFRule|int $rule
184
+ */
185
+ foreach ($failedRules as $index => $rule) {
186
+ if ($rule instanceof wfWAFRule) {
187
+ $failedRulesString .= $rule->getRuleID() . '|';
188
+ $actionDescription .= $rule->getDescription() . ', ';
189
+ } else {
190
+ $failedRulesString .= $rule . '|';
191
+ $actionDescription .= $rule . ', ';
192
+ }
193
+ }
194
+ $failedRulesString = wfWAFUtils::substr($failedRulesString, 0, -1);
195
+ $actionDescription = wfWAFUtils::substr($actionDescription, 0, -2);
196
+ }
197
+ if (preg_match('/\blogged\b/i', $failedRulesString)) {
198
+ $statusCode = 200;
199
+ $action = 'logged:waf';
200
+ } else {
201
+ $statusCode = 403;
202
+ $action = 'blocked:waf';
203
+ }
204
+
205
+ $ua = '';
206
+ $referer = '';
207
+ $headers = $request->getHeaders();
208
+ if (is_array($headers)) {
209
+ if (array_key_exists('User-Agent', $headers)) {
210
+ $ua = $headers['User-Agent'];
211
+ }
212
+ if (array_key_exists('Referer', $headers)) {
213
+ $referer = $headers['Referer'];
214
+ }
215
+ }
216
+
217
+ $row = array(
218
+ 'attackLogTime' => microtime(true),
219
+ 'ctime' => $request->getTimestamp(),
220
+ 'IP' => wfWAFUtils::inet_pton($request->getIP()),
221
+ 'statusCode' => $statusCode,
222
+ 'URL' => $request->getProtocol() . '://' . $request->getHost() . $request->getURI(),
223
+ 'isGoogle' => 0,
224
+ 'userID' => 0,
225
+ 'newVisit' => 0,
226
+ 'referer' => $referer,
227
+ 'UA' => $ua,
228
+ 'action' => $action,
229
+ 'actionDescription' => $actionDescription,
230
+ 'actionData' => wfWAFUtils::json_encode(array(
231
+ 'failedRules' => $failedRulesString,
232
+ 'paramKey' => base64_encode($failedParamKey),
233
+ 'paramValue' => base64_encode($failedParamValue),
234
+ 'path' => base64_encode($request->getPath()),
235
+ 'fullRequest' => base64_encode($request),
236
+ 'requestMetadata' => $request->getMetadata(),
237
+ )),
238
+ );
239
+
240
+ try {
241
+ return $this->db->insert($table, $row);
242
+ } catch (wfWAFStorageEngineMySQLiException $e) { // Let the firewall block the request without logging.
243
+ error_log($e->getMessage());
244
+ return false;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Insert IP into wfBlocks.
250
+ *
251
+ * @param float $timestamp
252
+ * @param string $ip
253
+ * @param int $type
254
+ * @return mixed
255
+ */
256
+ public function blockIP($timestamp, $ip, $type = wfWAFStorageInterface::IP_BLOCKS_SINGLE) {
257
+ $blockedIPs = $this->getConfig('wfWAFBlockedIPs');
258
+ if (!$blockedIPs) {
259
+ $blockedIPs = array();
260
+ }
261
+ $blockedIPs[$ip] = array($timestamp, $type);
262
+ $this->setConfig('wfWAFBlockedIPs', $blockedIPs);
263
+ return true;
264
+ }
265
+
266
+ /**
267
+ * Check if the IP is in wfBlocks.
268
+ *
269
+ * @param string $ip
270
+ * @return bool
271
+ */
272
+ public function isIPBlocked($ip) {
273
+ $blockedIPs = $this->getConfig('wfWAFBlockedIPs');
274
+ if (!$blockedIPs) {
275
+ $blockedIPs = array();
276
+ }
277
+ return array_key_exists($ip, $blockedIPs) && is_array($blockedIPs[$ip]) && $blockedIPs[$ip][0] >= time();
278
+ }
279
+
280
+ /**
281
+ * Remove all blocked IPs.
282
+ *
283
+ * @param int $types
284
+ */
285
+ public function purgeIPBlocks($types = wfWAFStorageInterface::IP_BLOCKS_ALL) {
286
+ if ($types === wfWAFStorageInterface::IP_BLOCKS_ALL) {
287
+ $this->unsetConfig('wfWAFBlockedIPs');
288
+ } else {
289
+ $blockedIPs = $this->getConfig('wfWAFBlockedIPs');
290
+ if (!$blockedIPs) {
291
+ $blockedIPs = array();
292
+ }
293
+ foreach ($blockedIPs as $key => $values) {
294
+ list($timestamp, $type) = $values;
295
+ if (($type & $types) > 0 || $timestamp < time()) {
296
+ unset($blockedIPs[$key]);
297
+ }
298
+ }
299
+ $this->setConfig('wfWAFBlockedIPs', $blockedIPs);
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Query config item from wfConfig table.
305
+ *
306
+ * @param $key
307
+ * @param null $default
308
+ * @param string $category
309
+ * @return mixed
310
+ */
311
+ public function getConfig($key, $default = null, $category = '') {
312
+ if (!$this->data) {
313
+ $this->autoloadConfig();
314
+ }
315
+
316
+ if (array_key_exists($category, $this->data) && array_key_exists($key, $this->data[$category])) {
317
+ return $this->data[$category][$key];
318
+ }
319
+
320
+ $table = $this->getStorageTable($category);
321
+ $val = $this->db->get_var('SELECT val FROM ' . $table . ' WHERE name = ?', array(
322
+ $key,
323
+ ));
324
+ if ($val !== null) {
325
+ if (in_array($key, $this->getSerializedParams())) {
326
+ $value = @unserialize($val);
327
+ $this->data[$category][$key] = $value;
328
+ return $value;
329
+ }
330
+ $this->data[$category][$key] = $val;
331
+ return $val;
332
+ }
333
+ return $default;
334
+ }
335
+
336
+ /**
337
+ * Insert/update wfConfig table for WAF option.
338
+ *
339
+ * @param $key
340
+ * @param $value
341
+ * @param string $category
342
+ */
343
+ public function setConfig($key, $value, $category = '') {
344
+ if (!array_key_exists($category, $this->data)) {
345
+ $this->data[$category] = array();
346
+ }
347
+
348
+ $changedConfigValue = (array_key_exists($key, $this->data[$category]) && $this->data[$category][$key] != $value) ||
349
+ !array_key_exists($key, $this->data[$category]);
350
+
351
+ if (!$this->dataChanged && $changedConfigValue) {
352
+ $this->dataChanged = array($category, $key, true);
353
+ register_shutdown_function(array($this, 'saveConfig'));
354
+ }
355
+ if ($changedConfigValue) {
356
+ $this->dataToSave[$category][$key] = $value;
357
+ }
358
+
359
+ $this->data[$category][$key] = $value;
360
+ }
361
+
362
+ /**
363
+ * Delete config item from wfConfig table.
364
+ *
365
+ * @param $key
366
+ * @param string $category
367
+ */
368
+ public function unsetConfig($key, $category = '') {
369
+ unset($this->data[$category][$key]);
370
+ $table = $this->getStorageTable($category);
371
+ $this->db->delete($table, array(
372
+ 'name' => $key,
373
+ ));
374
+ }
375
+
376
+ /**
377
+ *
378
+ */
379
+ public function saveConfig() {
380
+ if ($this->uninstalled) {
381
+ return;
382
+ }
383
+
384
+ try {
385
+ foreach ($this->dataToSave as $category => $data) {
386
+ foreach ($data as $key => $value) {
387
+ if (in_array($key, $this->getSerializedParams())) {
388
+ $value = serialize($value);
389
+ }
390
+ $table = $this->getStorageTable($category);
391
+ $this->db->query("INSERT INTO {$table} (name, val, autoload) values (?, ?, 'no') ON DUPLICATE KEY UPDATE val = ?", array(
392
+ $key,
393
+ $value,
394
+ $value,
395
+ ));
396
+ }
397
+ }
398
+ } catch (wfWAFStorageEngineMySQLiException $e) {
399
+ error_log($e);
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Remove related WAF specific configuration.
405
+ */
406
+ public function uninstall() {
407
+ try {
408
+ $this->getDb()->query("DROP TABLE IF EXISTS " . $this->networkTable('wfwafconfig'));
409
+ } catch (wfWAFStorageEngineMySQLiException $e) {
410
+ error_log($e);
411
+ }
412
+ $this->uninstalled = true;
413
+ }
414
+
415
+ /**
416
+ * Pull from wfConfig.
417
+ */
418
+ public function isInLearningMode() {
419
+ if ($this->getConfig('wafStatus', '') == 'learning-mode') {
420
+ if ($this->getConfig('learningModeGracePeriodEnabled', false)) {
421
+ if ($this->getConfig('learningModeGracePeriod', 0) > time()) {
422
+ return true;
423
+ } else {
424
+ // Reached the end of the grace period, activate the WAF.
425
+ $this->setConfig('wafStatus', 'enabled');
426
+ $this->setConfig('learningModeGracePeriodEnabled', 0);
427
+ $this->unsetConfig('learningModeGracePeriod');
428
+ }
429
+ } else {
430
+ return true;
431
+ }
432
+ }
433
+ return false;
434
+ }
435
+
436
+ /**
437
+ * Pull from wfConfig.
438
+ */
439
+ public function isDisabled() {
440
+ return $this->getConfig('wafStatus', '') === 'disabled' || $this->getConfig('wafDisabled', 0);
441
+ }
442
+
443
+ /**
444
+ * Return hardcoded path maybe?
445
+ */
446
+ public function getRulesDSLCacheFile() {
447
+
448
+ }
449
+
450
+ /**
451
+ * Probably not.
452
+ */
453
+ public function isAttackDataFull() {
454
+ return false;
455
+ }
456
+
457
+ /**
458
+ *
459
+ */
460
+ public function vacuum() {
461
+ $this->purgeIPBlocks(wfWAFStorageInterface::IP_BLOCKS_ALL);
462
+ }
463
+
464
+ /**
465
+ * @return wfWAFStorageEngineDatabase
466
+ */
467
+ public function getDb() {
468
+ return $this->db;
469
+ }
470
+
471
+ /**
472
+ *
473
+ */
474
+ public function setDefaults() {
475
+ $defaults = $this->getDefaultConfiguration();
476
+ foreach ($defaults as $key => $value) {
477
+ $val = $this->getConfig($key);
478
+ if ($val === null) {
479
+ $this->setConfig($key, $value);
480
+ }
481
+ }
482
+ }
483
+
484
+ /**
485
+ *
486
+ */
487
+ public function runMigrations() {
488
+ // $currentVersion = $this->getConfig('version');
489
+ // if (!$currentVersion || version_compare($currentVersion, WFWAF_VERSION) === -1) {
490
+ //
491
+ // }
492
+
493
+ $this->getDb()->query("CREATE TABLE IF NOT EXISTS " . $this->networkTable('wfwafconfig') .
494
+ " (
495
+ `name` varchar(100) NOT NULL,
496
+ `val` longblob,
497
+ `autoload` enum('no','yes') NOT NULL DEFAULT 'yes',
498
+ PRIMARY KEY (`name`)
499
+ ) DEFAULT CHARSET=utf8
500
+ ");
501
+ }
502
+
503
+ /**
504
+ * @return array
505
+ */
506
+ public function getDefaultConfiguration() {
507
+ return array(
508
+ 'wafStatus' => 'learning-mode',
509
+ 'learningModeGracePeriodEnabled' => 1,
510
+ 'learningModeGracePeriod' => time() + (86400 * 7),
511
+ 'authKey' => wfWAFUtils::getRandomString(64),
512
+ );
513
+ }
514
+
515
+ /**
516
+ * @return array
517
+ */
518
+ public function getSerializedParams() {
519
+ return array(
520
+ 'cron',
521
+ 'whitelistedURLParams',
522
+ 'disabledRules',
523
+ 'wfWAFBlockedIPs',
524
+ 'wafRules',
525
+ );
526
+ }
527
+
528
+ /**
529
+ * @return array
530
+ */
531
+ public function getAutoloadParams() {
532
+ return array(
533
+ '' => array(
534
+ 'wafStatus',
535
+ 'learningModeGracePeriodEnabled',
536
+ 'learningModeGracePeriod',
537
+ 'authKey',
538
+ 'version',
539
+ 'advancedBlockingEnabled',
540
+ 'disabledRules',
541
+ 'patternBlocks',
542
+ 'countryBlocks',
543
+ 'otherBlocks',
544
+ 'lockouts',
545
+ 'wafRules',
546
+ 'avoid_php_input',
547
+ 'wafDisabled',
548
+ 'wfWAFBlockedIPs',
549
+ 'disableWAFBlacklistBlocking',
550
+ ),
551
+ 'livewaf' => array(
552
+ 'cron',
553
+ 'whitelistedURLParams',
554
+ 'whitelistedURLs',
555
+ ),
556
+ 'transient' => array(
557
+ 'watchedIPs',
558
+ 'blockedPrefixes',
559
+ ),
560
+ 'synced' => array(
561
+ 'timeoffset_wf',
562
+ 'apiKey',
563
+ 'isPaid',
564
+ 'siteURL',
565
+ 'homeURL',
566
+ 'whitelistedIPs',
567
+ 'howGetIPs',
568
+ 'howGetIPs_trusted_proxies',
569
+ 'other_WFNet',
570
+ 'pluginABSPATH',
571
+ 'serverIPs',
572
+ 'disableWAFIPBlocking',
573
+ 'advancedBlockingEnabled',
574
+ ),
575
+ );
576
+ }
577
+
578
+ protected function autoloadConfig() {
579
+ $params = $this->getAutoloadParams();
580
+ foreach ($params as $category => $autoloadParams) {
581
+ // Set default keys to null to prevent re-querying the table for config keypairs that aren't in the table.
582
+ foreach ($autoloadParams as $autoloadParam) {
583
+ $this->data[$category][$autoloadParam] = null;
584
+ }
585
+
586
+ $table = $this->getStorageTable($category);
587
+ $whereIn = str_repeat('?,', count($autoloadParams) - 1) . '?';
588
+ $results = $this->db->get_results('SELECT * FROM ' . $table . ' WHERE name IN (' . $whereIn . ')', $autoloadParams);
589
+ $serializedParams = $this->getSerializedParams();
590
+ foreach ($results as $row) {
591
+ if (in_array($row['name'], $serializedParams)) {
592
+ $this->data[$category][$row['name']] = @unserialize($row['val']);
593
+ } else {
594
+ $this->data[$category][$row['name']] = $row['val'];
595
+ }
596
+ }
597
+ }
598
+ }
599
+
600
+ public function getRules() {
601
+ return $this->getConfig('wafRules');
602
+ }
603
+
604
+ public function setRules($rules) {
605
+ $this->setConfig('wafRules', $rules);
606
+ }
607
+
608
+ public function needsInitialRules() {
609
+ $rules = $this->getRules();
610
+ return !$rules;
611
+ }
612
+
613
+ public function getStorageTable($category) {
614
+ switch ($category) {
615
+ case 'livewaf':
616
+ case 'transient':
617
+ $table = $this->networkTable('wfwafconfig');
618
+ break;
619
+ default:
620
+ $table = $this->networkTable('wfConfig');
621
+ break;
622
+ }
623
+ return $table;
624
+ }
625
+ }
626
+
627
+ interface wfWAFStorageEngineDatabase {
628
+
629
+ public function connect($user, $password, $database, $host, $port = null, $socket = null);
630
+
631
+ public function setCharset($charset, $collation);
632
+
633
+ public function close();
634
+
635
+ public function insert($table, $data);
636
+
637
+ public function update($table, $data, $where);
638
+
639
+ public function delete($table, $where);
640
+
641
+ public function query($sql, $data = array());
642
+
643
+ public function get_var($query = null, $data = array(), $x = 0, $y = 0);
644
+
645
+ public function get_row($query = null, $data = array(), $y = 0);
646
+
647
+ public function get_results($query = null, $data = array());
648
+ }
649
+
650
+ class wfWAFStorageEngineMySQLi implements wfWAFStorageEngineDatabase {
651
+
652
+ /**
653
+ * @var string
654
+ */
655
+ private $user;
656
+ /**
657
+ * @var string
658
+ */
659
+ private $password;
660
+ /**
661
+ * @var string
662
+ */
663
+ private $database;
664
+ /**
665
+ * @var string
666
+ */
667
+ private $host;
668
+ /**
669
+ * @var int|null
670
+ */
671
+ private $port;
672
+ /**
673
+ * @var string|null
674
+ */
675
+ private $socket;
676
+
677
+ /** @var mysqli */
678
+ private $dbh;
679
+
680
+ private $lastStatement;
681
+
682
+ public $installing = false;
683
+
684
+ /**
685
+ *
686
+ */
687
+ public function __construct() {
688
+
689
+ }
690
+
691
+ /**
692
+ * @param string $user
693
+ * @param string $password
694
+ * @param string $database
695
+ * @param string $host
696
+ * @param null|int $port
697
+ * @param mixed $socket
698
+ * @return mysqli
699
+ * @throws wfWAFStorageEngineMySQLiException
700
+ */
701
+ public function connect($user, $password, $database, $host, $port = null, $socket = null) {
702
+ $this->dbh = @mysqli_connect($host, $user, $password, $database, $port, $socket);
703
+ if (!$this->dbh) {
704
+ $error = error_get_last();
705
+ throw new wfWAFStorageEngineMySQLiException('Unable to connect to database: ' . $error['message'], $error['type']);
706
+ }
707
+
708
+ return $this->dbh;
709
+ }
710
+
711
+ public function setCharset($charset, $collation) {
712
+ $result = $this->determineCharset($charset, $collation);
713
+ $charset = $result['charset'];
714
+ $collation = $result['collation'];
715
+ $this->setConnectionCharset($charset, $collation);
716
+ }
717
+
718
+ protected function determineCharset($charset, $collation) {
719
+ if ('utf8' === $charset && $this->hasCap('utf8mb4')) {
720
+ $charset = 'utf8mb4';
721
+ }
722
+
723
+ if ('utf8mb4' === $charset && !$this->hasCap('utf8mb4')) {
724
+ $charset = 'utf8';
725
+ $collation = str_replace('utf8mb4_', 'utf8_', $collation);
726
+ }
727
+
728
+ if ('utf8mb4' === $charset) {
729
+ // _general_ is outdated, so we can upgrade it to _unicode_, instead.
730
+ if (!$collation || 'utf8_general_ci' === $collation) {
731
+ $collation = 'utf8mb4_unicode_ci';
732
+ } else {
733
+ $collation = str_replace('utf8_', 'utf8mb4_', $collation);
734
+ }
735
+ }
736
+
737
+ // _unicode_520_ is a better collation, we should use that when it's available.
738
+ if ($this->hasCap('utf8mb4_520') && 'utf8mb4_unicode_ci' === $collation) {
739
+ $collation = 'utf8mb4_unicode_520_ci';
740
+ }
741
+
742
+ return compact('charset', 'collation');
743
+ }
744
+
745
+ /**
746
+ * Determine if a database supports a particular feature.
747
+ *
748
+ * @param string $dbCap The feature to check for. Accepts 'collation',
749
+ * 'group_concat', 'subqueries', 'set_charset',
750
+ * 'utf8mb4', or 'utf8mb4_520'.
751
+ * @return int|false Whether the database feature is supported, false otherwise.
752
+ */
753
+ public function hasCap($dbCap) {
754
+ $version = $this->dbVersion();
755
+
756
+ switch (strtolower($dbCap)) {
757
+ case 'collation' : // @since 2.5.0
758
+ case 'group_concat' : // @since 2.7.0
759
+ case 'subqueries' : // @since 2.7.0
760
+ return version_compare($version, '4.1', '>=');
761
+ case 'set_charset' :
762
+ return version_compare($version, '5.0.7', '>=');
763
+ case 'utf8mb4' : // @since 4.1.0
764
+ if (version_compare($version, '5.5.3', '<')) {
765
+ return false;
766
+ }
767
+ $client_version = mysqli_get_client_info();
768
+
769
+ /*
770
+ * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
771
+ * mysqlnd has supported utf8mb4 since 5.0.9.
772
+ */
773
+ if (false !== strpos($client_version, 'mysqlnd')) {
774
+ $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version);
775
+ return version_compare($client_version, '5.0.9', '>=');
776
+ } else {
777
+ return version_compare($client_version, '5.5.3', '>=');
778
+ }
779
+ case 'utf8mb4_520' : // @since 4.6.0
780
+ return version_compare($version, '5.6', '>=');
781
+ }
782
+
783
+ return false;
784
+ }
785
+
786
+ public function setConnectionCharset($charset, $collation) {
787
+ if ($this->hasCap('collation') && !empty($charset)) {
788
+ $setCharsetSucceeded = false;
789
+
790
+ if (function_exists('mysqli_set_charset') && $this->hasCap('set_charset')) {
791
+ $setCharsetSucceeded = mysqli_set_charset($this->dbh, $charset);
792
+ }
793
+
794
+ if ($setCharsetSucceeded) {
795
+ $query = "SET NAMES {$this->escape($charset)}";
796
+ if ($collation) {
797
+ $query .= " COLLATE {$this->escape($collation)}";
798
+ }
799
+ $this->query($query);
800
+ }
801
+ }
802
+ }
803
+
804
+ /**
805
+ * Retrieves the MySQL server version.
806
+ *
807
+ * @return null|string Null on failure, version number on success.
808
+ */
809
+ public function dbVersion() {
810
+ $serverInfo = mysqli_get_server_info($this->dbh);
811
+ return preg_replace('/[^0-9.].*/', '', $serverInfo);
812
+ }
813
+
814
+ /**
815
+ *
816
+ */
817
+ public function close() {
818
+ mysqli_close($this->dbh);
819
+ }
820
+
821
+ /**
822
+ * @param string $table
823
+ * @param array $data
824
+ * @return bool|int|string
825
+ */
826
+ public function insert($table, $data) {
827
+ $sql = $this->buildInsertSQL($table, $data);
828
+ if ($stmt = $this->query($sql, $data)) {
829
+ $insertID = mysqli_insert_id($this->dbh);
830
+ $stmt->close();
831
+ return $insertID;
832
+ }
833
+ return false;
834
+ }
835
+
836
+ /**
837
+ * @param string $table
838
+ * @param array $data
839
+ * @param array $where
840
+ * @return bool|int
841
+ * @throws wfWAFStorageEngineMySQLiException
842
+ */
843
+ public function update($table, $data, $where) {
844
+ if (!$data) {
845
+ throw new wfWAFStorageEngineMySQLiException('Values to update must supplied to \wfWAFStorageEngineMySQLi::update.');
846
+ }
847
+ if (!$where) {
848
+ throw new wfWAFStorageEngineMySQLiException('A where clause must supplied to \wfWAFStorageEngineMySQLi::update.');
849
+ }
850
+ $sql = $this->buildUpdateSQL($table, $data, $where);
851
+ if ($stmt = $this->query($sql, array_merge(array_values($data), array_values($where)))) {
852
+ $affectedRows = mysqli_affected_rows($this->dbh);
853
+ $stmt->close();
854
+ return $affectedRows;
855
+ }
856
+ return false;
857
+
858
+ }
859
+
860
+ /**
861
+ * @param string $table
862
+ * @param array $where
863
+ * @return bool|int
864
+ * @throws wfWAFStorageEngineMySQLiException
865
+ */
866
+ public function delete($table, $where) {
867
+ if (!$where) {
868
+ throw new wfWAFStorageEngineMySQLiException('A where clause must supplied to \wfWAFStorageEngineMySQLi::delete.');
869
+ }
870
+ $sql = $this->buildDeleteSQL($table, $where);
871
+ if ($stmt = $this->query($sql, $where)) {
872
+ $affectedRows = mysqli_affected_rows($this->dbh);
873
+ $stmt->close();
874
+ return $affectedRows;
875
+ }
876
+ return false;
877
+ }
878
+
879
+ /**
880
+ * @param $sql
881
+ * @param array $data
882
+ * @return mysqli_stmt
883
+ * @throws wfWAFStorageEngineMySQLiException
884
+ */
885
+ public function query($sql, $data = array()) {
886
+ if ($this->installing) {
887
+ return false;
888
+ }
889
+
890
+ $stmt = mysqli_prepare($this->dbh, $sql);
891
+ if (!$stmt) {
892
+ throw new wfWAFStorageEngineMySQLiException(
893
+ sprintf('MySQL error[%d]: %s', mysqli_errno($this->dbh), mysqli_error($this->dbh)),
894
+ mysqli_errno($this->dbh)
895
+ );
896
+ }
897
+
898
+ $bindFormats = '';
899
+ $bindData = array();
900
+ $bindCounter = 0;
901
+ foreach ($data as $value) {
902
+ switch (gettype($value)) {
903
+ case 'integer':
904
+ case 'boolean':
905
+ $bindFormats .= 'i';
906
+ ${"bindVar{$bindCounter}"} = (int) $value;
907
+ $bindData[] = &${"bindVar{$bindCounter}"};
908
+ break;
909
+
910
+ case 'string':
911
+ $bindFormats .= 's';
912
+ ${"bindVar{$bindCounter}"} = $value;
913
+ $bindData[] = &${"bindVar{$bindCounter}"};
914
+ break;
915
+
916
+ case 'double':
917
+ case 'float':
918
+ $bindFormats .= 'd';
919
+ ${"bindVar{$bindCounter}"} = $value;
920
+ $bindData[] = &${"bindVar{$bindCounter}"};
921
+ break;
922
+
923
+ default:
924
+ $bindFormats .= 'b';
925
+ ${"bindVar{$bindCounter}"} = $value;
926
+ $bindData[] = &${"bindVar{$bindCounter}"};
927
+ break;
928
+ }
929
+ $bindCounter++;
930
+ }
931
+
932
+ if ($bindData) {
933
+ array_unshift($bindData, $bindFormats);
934
+ call_user_func_array(array($stmt, 'bind_param'), $bindData);
935
+ }
936
+
937
+ $stmt->execute();
938
+ if ($stmt->errno > 0) {
939
+ throw new wfWAFStorageEngineMySQLiException('MySQL error [' . $stmt->errno . ']: ' . $stmt->error, $stmt->errno);
940
+ }
941
+
942
+ return $stmt;
943
+ }
944
+
945
+ /**
946
+ * @param mysqli_stmt $stmt
947
+ * @return array
948
+ */
949
+ public function statementToArray($stmt) {
950
+ if (!$stmt) {
951
+ return array();
952
+ }
953
+
954
+ $result = $stmt->get_result();
955
+
956
+ $return = array();
957
+ while ($row = $result->fetch_array(MYSQLI_BOTH)) {
958
+ $return[] = $row;
959
+ }
960
+ return $return;
961
+ }
962
+
963
+ /**
964
+ * @param string $query
965
+ * @param array $data
966
+ * @param int $x
967
+ * @param int $y
968
+ * @return null|mixed
969
+ */
970
+ public function get_var($query = null, $data = array(), $x = 0, $y = 0) {
971
+ $this->lastStatement = $this->query($query, $data);
972
+ $results = $this->statementToArray($this->lastStatement);
973
+
974
+ if (isset($results[$y][$x])) {
975
+ return $results[$y][$x];
976
+ }
977
+
978
+ return null;
979
+ }
980
+
981
+ /**
982
+ * @param string $query
983
+ * @param array $data
984
+ * @param int $y
985
+ * @return mixed|null
986
+ */
987
+ public function get_row($query = null, $data = array(), $y = 0) {
988
+ $stmt = $this->query($query, $data);
989
+ $results = $this->statementToArray($stmt);
990
+
991
+ if (isset($results[$y])) {
992
+ return $results[$y];
993
+ }
994
+
995
+ return null;
996
+ }
997
+
998
+ /**
999
+ * @param string $query
1000
+ * @param array $data
1001
+ * @return array
1002
+ */
1003
+ public function get_results($query = null, $data = array()) {
1004
+ $stmt = $this->query($query, $data);
1005
+ return $this->statementToArray($stmt);
1006
+ }
1007
+
1008
+ /**
1009
+ * @param mixed $value
1010
+ * @return string
1011
+ */
1012
+ public function escape($value) {
1013
+ return sprintf("'%s'", mysqli_real_escape_string($this->dbh, $value));
1014
+ }
1015
+
1016
+ /**
1017
+ * @param string $table
1018
+ * @param array $data
1019
+ * @return string
1020
+ */
1021
+ protected function buildInsertSQL($table, $data) {
1022
+ $columns = array();
1023
+ $values = array();
1024
+ foreach ($data as $column => $value) {
1025
+ $columns[] = $this->sanitizeColumn($column);
1026
+ $values[] = '?';
1027
+ }
1028
+ $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $table, join(',', $columns), join(',', $values));
1029
+ return $sql;
1030
+ }
1031
+
1032
+ /**
1033
+ * @param string $column
1034
+ * @return mixed
1035
+ */
1036
+ protected function sanitizeColumn($column) {
1037
+ return preg_replace('/[^a-zA-Z0-9_]/i', '', $column);
1038
+ }
1039
+
1040
+ /**
1041
+ * @return mixed
1042
+ */
1043
+ public function getLastStatement() {
1044
+ return $this->lastStatement;
1045
+ }
1046
+
1047
+ /**
1048
+ * @param string $table
1049
+ * @param array $where
1050
+ * @return string
1051
+ */
1052
+ protected function buildDeleteSQL($table, $where) {
1053
+ $sql = sprintf('DELETE FROM %s %s', $table, $this->buildWhereClause($where));
1054
+ return $sql;
1055
+ }
1056
+
1057
+ /**
1058
+ * @param string $table
1059
+ * @param array $data
1060
+ * @param array $where
1061
+ * @return string
1062
+ */
1063
+ protected function buildUpdateSQL($table, $data, $where) {
1064
+ if (!is_array($data)) {
1065
+ throw new InvalidArgumentException('Argument 2 expected to be array. ' . gettype($data) . ' given.');
1066
+ }
1067
+ if (count($data) === 0) {
1068
+ throw new InvalidArgumentException('Argument 2 cannot be empty.');
1069
+ }
1070
+ if (!is_array($where)) {
1071
+ throw new InvalidArgumentException('Argument 3 expected to be array. ' . gettype($where) . ' given.');
1072
+ }
1073
+ if (count($where) === 0) {
1074
+ throw new InvalidArgumentException('Argument 3 cannot be empty.');
1075
+ }
1076
+
1077
+ return sprintf('UPDATE %s SET %s %s', $table, $this->buildUpdateClause($data), $this->buildWhereClause($where));
1078
+ }
1079
+
1080
+ /**
1081
+ * @param array $where
1082
+ * @return string
1083
+ */
1084
+ protected function buildWhereClause($where) {
1085
+ if (!is_array($where)) {
1086
+ throw new InvalidArgumentException('Argument 1 expected to be array. ' . gettype($where) . ' given.');
1087
+ }
1088
+ if (!$where) {
1089
+ return '';
1090
+ }
1091
+ $sql = 'WHERE ';
1092
+ foreach ($where as $column => $value) {
1093
+ $sql .= $this->sanitizeColumn($column) . ' = ? AND ';
1094
+ }
1095
+ return wfWAFUtils::substr($sql, 0, -5);
1096
+ }
1097
+
1098
+ /**
1099
+ * @param array $data
1100
+ * @return string
1101
+ */
1102
+ protected function buildUpdateClause($data) {
1103
+ if (!is_array($data)) {
1104
+ throw new InvalidArgumentException('Argument 1 expected to be array. ' . gettype($data) . ' given.');
1105
+ }
1106
+ if (!$data) {
1107
+ throw new InvalidArgumentException('Argument 1 cannot be an empty array.');
1108
+ }
1109
+ $sql = '';
1110
+ foreach ($data as $column => $value) {
1111
+ $sql .= $this->sanitizeColumn($column) . ' = ?, ';
1112
+ }
1113
+ return wfWAFUtils::substr($sql, 0, -2);
1114
+ }
1115
+ }
1116
+
1117
+
1118
+ class wfWAFStorageEngineMySQLiException extends wfWAFException {
1119
+
1120
+ }
vendor/wordfence/wf-waf/src/lib/utils.php CHANGED
@@ -531,7 +531,7 @@ class wfWAFUtils {
531
  $args = func_get_args();
532
  return self::callMBSafeStrFunction('strrpos', $args);
533
  }
534
-
535
  /**
536
  * @param string $val An ini byte size value (e.g., 20M)
537
  * @return int
@@ -541,7 +541,7 @@ class wfWAFUtils {
541
  if (preg_match('/^\d+$/', $val)) {
542
  return (int) $val;
543
  }
544
-
545
  $last = strtolower(substr($val, -1));
546
  $val = (int) substr($val, 0, -1);
547
  switch ($last) {
@@ -552,10 +552,10 @@ class wfWAFUtils {
552
  case 'k':
553
  $val *= 1024;
554
  }
555
-
556
  return $val;
557
  }
558
-
559
  public static function reverseLookup($IP) {
560
  $IPn = self::inet_pton($IP);
561
  // This function works for IPv4 or IPv6
@@ -569,7 +569,7 @@ class wfWAFUtils {
569
  } else if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
570
  $ptr = implode(".", array_reverse(str_split(bin2hex($IPn)))) . ".ip6.arpa";
571
  }
572
-
573
  if ($ptr && function_exists('dns_get_record')) {
574
  $host = @dns_get_record($ptr, DNS_PTR);
575
  if ($host) {
@@ -582,28 +582,28 @@ class wfWAFUtils {
582
  }
583
  return $host;
584
  }
585
-
586
  public static function patternToRegex($pattern, $mod = 'i', $sep = '/') {
587
  $pattern = preg_quote(trim($pattern), $sep);
588
  $pattern = str_replace(' ', '\s', $pattern);
589
  return $sep . '^' . str_replace('\*', '.*', $pattern) . '$' . $sep . $mod;
590
  }
591
-
592
  public static function isUABlocked($uaPattern, $ua) { // takes a pattern using asterisks as wildcards, turns it into regex and checks it against the visitor UA returning true if blocked
593
  return fnmatch($uaPattern, $ua, FNM_CASEFOLD);
594
  }
595
-
596
  public static function isRefererBlocked($refPattern, $referer) {
597
  return fnmatch($refPattern, $referer, FNM_CASEFOLD);
598
  }
599
-
600
  public static function extractBareURI($URL) {
601
  $URL = preg_replace('/^https?:\/\/[^\/]+/i', '', $URL); //strip of method and host
602
  $URL = preg_replace('/\#.*$/', '', $URL); //strip off fragment
603
  $URL = preg_replace('/\?.*$/', '', $URL); //strip off query string
604
  return $URL;
605
  }
606
-
607
  public static function extractHostname($str) {
608
  if (preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)) {
609
  return strtolower($matches[1]);
@@ -612,29 +612,29 @@ class wfWAFUtils {
612
  return false;
613
  }
614
  }
615
-
616
  public static function redirect($location, $status = 302) {
617
  $is_apache = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false);
618
  $is_IIS = !$is_apache && (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false);
619
-
620
  self::doNotCache();
621
-
622
  if (!$is_IIS && PHP_SAPI != 'cgi-fcgi') {
623
  self::statusHeader($status); // This causes problems on IIS and some FastCGI setups
624
  }
625
-
626
  header("Location: {$location}", true, $status);
627
  exit;
628
  }
629
-
630
  public static function statusHeader($code) {
631
  $code = abs(intval($code));
632
-
633
  $statusCodes = array(
634
  100 => 'Continue',
635
  101 => 'Switching Protocols',
636
  102 => 'Processing',
637
-
638
  200 => 'OK',
639
  201 => 'Created',
640
  202 => 'Accepted',
@@ -644,7 +644,7 @@ class wfWAFUtils {
644
  206 => 'Partial Content',
645
  207 => 'Multi-Status',
646
  226 => 'IM Used',
647
-
648
  300 => 'Multiple Choices',
649
  301 => 'Moved Permanently',
650
  302 => 'Found',
@@ -654,7 +654,7 @@ class wfWAFUtils {
654
  306 => 'Reserved',
655
  307 => 'Temporary Redirect',
656
  308 => 'Permanent Redirect',
657
-
658
  400 => 'Bad Request',
659
  401 => 'Unauthorized',
660
  402 => 'Payment Required',
@@ -683,7 +683,7 @@ class wfWAFUtils {
683
  429 => 'Too Many Requests',
684
  431 => 'Request Header Fields Too Large',
685
  451 => 'Unavailable For Legal Reasons',
686
-
687
  500 => 'Internal Server Error',
688
  501 => 'Not Implemented',
689
  502 => 'Bad Gateway',
@@ -695,18 +695,18 @@ class wfWAFUtils {
695
  510 => 'Not Extended',
696
  511 => 'Network Authentication Required',
697
  );
698
-
699
  $description = (isset($statusCodes[$code]) ? $statusCodes[$code] : '');
700
-
701
  $protocol = $_SERVER['SERVER_PROTOCOL'];
702
  if (!in_array($protocol, array( 'HTTP/1.1', 'HTTP/2', 'HTTP/2.0'))) {
703
  $protocol = 'HTTP/1.0';
704
  }
705
-
706
  $header = "{$protocol} {$code} {$description}";
707
  @header($header, true, $code);
708
  }
709
-
710
  public static function doNotCache() {
711
  header("Pragma: no-cache");
712
  header("Cache-Control: no-cache, must-revalidate, private");
@@ -716,7 +716,7 @@ class wfWAFUtils {
716
  if (!defined('DONOTCDN')) { define('DONOTCDN', true); }
717
  if (!defined('DONOTCACHEOBJECT')) { define('DONOTCACHEOBJECT', true); }
718
  }
719
-
720
  /**
721
  * Check if an IP address is in a network block
722
  *
@@ -726,18 +726,18 @@ class wfWAFUtils {
726
  */
727
  public static function subnetContainsIP($subnet, $ip) {
728
  list($network, $prefix) = array_pad(explode('/', $subnet, 2), 2, null);
729
-
730
  if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
731
  // If no prefix was supplied, 32 is implied for IPv4
732
  if ($prefix === null) {
733
  $prefix = 32;
734
  }
735
-
736
  // Validate the IPv4 network prefix
737
  if ($prefix < 0 || $prefix > 32) {
738
  return false;
739
  }
740
-
741
  // Increase the IPv4 network prefix to work in the IPv6 address space
742
  $prefix += 96;
743
  } else {
@@ -745,13 +745,13 @@ class wfWAFUtils {
745
  if ($prefix === null) {
746
  $prefix = 128;
747
  }
748
-
749
  // Validate the IPv6 network prefix
750
  if ($prefix < 1 || $prefix > 128) {
751
  return false;
752
  }
753
  }
754
-
755
  $bin_network = wfWAFUtils::substr(self::inet_pton($network), 0, ceil($prefix / 8));
756
  $bin_ip = wfWAFUtils::substr(self::inet_pton($ip), 0, ceil($prefix / 8));
757
  if ($prefix % 8 != 0) { //Adjust the last relevant character to fit the mask length since the character's bits are split over it
@@ -760,13 +760,13 @@ class wfWAFUtils {
760
  $bin_network[$pos] = ($bin_network[$pos] & $adjustment);
761
  $bin_ip[$pos] = ($bin_ip[$pos] & $adjustment);
762
  }
763
-
764
  return ($bin_network === $bin_ip);
765
  }
766
-
767
  /**
768
  * Behaves exactly like PHP's parse_url but uses WP's compatibility fixes for early PHP 5 versions.
769
- *
770
  * @param string $url
771
  * @param int $component
772
  * @return mixed
@@ -774,7 +774,7 @@ class wfWAFUtils {
774
  public static function parse_url($url, $component = -1) {
775
  $to_unset = array();
776
  $url = strval($url);
777
-
778
  if (substr($url, 0, 2) === '//') {
779
  $to_unset[] = 'scheme';
780
  $url = 'placeholder:' . $url;
@@ -784,22 +784,22 @@ class wfWAFUtils {
784
  $to_unset[] = 'host';
785
  $url = 'placeholder://placeholder' . $url;
786
  }
787
-
788
  $parts = @parse_url($url);
789
-
790
  if ($parts === false) { // Parsing failure
791
  return $parts;
792
  }
793
-
794
  // Remove the placeholder values
795
  foreach ($to_unset as $key) {
796
  unset($parts[$key]);
797
  }
798
-
799
  if ($component === -1) {
800
  return $parts;
801
  }
802
-
803
  $translation = array(
804
  PHP_URL_SCHEME => 'scheme',
805
  PHP_URL_HOST => 'host',
@@ -810,38 +810,38 @@ class wfWAFUtils {
810
  PHP_URL_QUERY => 'query',
811
  PHP_URL_FRAGMENT => 'fragment',
812
  );
813
-
814
  $key = false;
815
  if (isset($translation[$component])) {
816
  $key = $translation[$component];
817
  }
818
-
819
  if ($key !== false && is_array($parts) && isset($parts[$key])) {
820
  return $parts[$key];
821
  }
822
-
823
  return null;
824
  }
825
-
826
  /**
827
  * Validates the URL, supporting both scheme-relative and path-relative formats.
828
- *
829
  * @param $url
830
  * @return mixed
831
  */
832
  public static function validate_url($url) {
833
  $url = strval($url);
834
-
835
  if (substr($url, 0, 2) === '//') {
836
  $url = 'placeholder:' . $url;
837
  }
838
  elseif (substr($url, 0, 1) === '/') {
839
  $url = 'placeholder://placeholder' . $url;
840
  }
841
-
842
  return filter_var($url, FILTER_VALIDATE_URL);
843
  }
844
-
845
  public static function rawPOSTBody() {
846
  // phpcs:ignore PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
847
  global $HTTP_RAW_POST_DATA;
@@ -874,11 +874,11 @@ class wfWAFUtils {
874
  }
875
  return $data;
876
  }
877
-
878
  /**
879
  * Returns the current timestamp, adjusted as needed to get close to what we consider a true timestamp. We use this
880
  * because a significant number of servers are using a drastically incorrect time.
881
- *
882
  * @return int
883
  */
884
  public static function normalizedTime() {
@@ -895,5 +895,111 @@ class wfWAFUtils {
895
  }
896
  return time() + $offset;
897
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898
  }
899
  }
531
  $args = func_get_args();
532
  return self::callMBSafeStrFunction('strrpos', $args);
533
  }
534
+
535
  /**
536
  * @param string $val An ini byte size value (e.g., 20M)
537
  * @return int
541
  if (preg_match('/^\d+$/', $val)) {
542
  return (int) $val;
543
  }
544
+
545
  $last = strtolower(substr($val, -1));
546
  $val = (int) substr($val, 0, -1);
547
  switch ($last) {
552
  case 'k':
553
  $val *= 1024;
554
  }
555
+
556
  return $val;
557
  }
558
+
559
  public static function reverseLookup($IP) {
560
  $IPn = self::inet_pton($IP);
561
  // This function works for IPv4 or IPv6
569
  } else if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
570
  $ptr = implode(".", array_reverse(str_split(bin2hex($IPn)))) . ".ip6.arpa";
571
  }
572
+
573
  if ($ptr && function_exists('dns_get_record')) {
574
  $host = @dns_get_record($ptr, DNS_PTR);
575
  if ($host) {
582
  }
583
  return $host;
584
  }
585
+
586
  public static function patternToRegex($pattern, $mod = 'i', $sep = '/') {
587
  $pattern = preg_quote(trim($pattern), $sep);
588
  $pattern = str_replace(' ', '\s', $pattern);
589
  return $sep . '^' . str_replace('\*', '.*', $pattern) . '$' . $sep . $mod;
590
  }
591
+
592
  public static function isUABlocked($uaPattern, $ua) { // takes a pattern using asterisks as wildcards, turns it into regex and checks it against the visitor UA returning true if blocked
593
  return fnmatch($uaPattern, $ua, FNM_CASEFOLD);
594
  }
595
+
596
  public static function isRefererBlocked($refPattern, $referer) {
597
  return fnmatch($refPattern, $referer, FNM_CASEFOLD);
598
  }
599
+
600
  public static function extractBareURI($URL) {
601
  $URL = preg_replace('/^https?:\/\/[^\/]+/i', '', $URL); //strip of method and host
602
  $URL = preg_replace('/\#.*$/', '', $URL); //strip off fragment
603
  $URL = preg_replace('/\?.*$/', '', $URL); //strip off query string
604
  return $URL;
605
  }
606
+
607
  public static function extractHostname($str) {
608
  if (preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)) {
609
  return strtolower($matches[1]);
612
  return false;
613
  }
614
  }
615
+
616
  public static function redirect($location, $status = 302) {
617
  $is_apache = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false);
618
  $is_IIS = !$is_apache && (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false);
619
+
620
  self::doNotCache();
621
+
622
  if (!$is_IIS && PHP_SAPI != 'cgi-fcgi') {
623
  self::statusHeader($status); // This causes problems on IIS and some FastCGI setups
624
  }
625
+
626
  header("Location: {$location}", true, $status);
627
  exit;
628
  }
629
+
630
  public static function statusHeader($code) {
631
  $code = abs(intval($code));
632
+
633
  $statusCodes = array(
634
  100 => 'Continue',
635
  101 => 'Switching Protocols',
636
  102 => 'Processing',
637
+
638
  200 => 'OK',
639
  201 => 'Created',
640
  202 => 'Accepted',
644
  206 => 'Partial Content',
645
  207 => 'Multi-Status',
646
  226 => 'IM Used',
647
+
648
  300 => 'Multiple Choices',
649
  301 => 'Moved Permanently',
650
  302 => 'Found',
654
  306 => 'Reserved',
655
  307 => 'Temporary Redirect',
656
  308 => 'Permanent Redirect',
657
+
658
  400 => 'Bad Request',
659
  401 => 'Unauthorized',
660
  402 => 'Payment Required',
683
  429 => 'Too Many Requests',
684
  431 => 'Request Header Fields Too Large',
685
  451 => 'Unavailable For Legal Reasons',
686
+
687
  500 => 'Internal Server Error',
688
  501 => 'Not Implemented',
689
  502 => 'Bad Gateway',
695
  510 => 'Not Extended',
696
  511 => 'Network Authentication Required',
697
  );
698
+
699
  $description = (isset($statusCodes[$code]) ? $statusCodes[$code] : '');
700
+
701
  $protocol = $_SERVER['SERVER_PROTOCOL'];
702
  if (!in_array($protocol, array( 'HTTP/1.1', 'HTTP/2', 'HTTP/2.0'))) {
703
  $protocol = 'HTTP/1.0';
704
  }
705
+
706
  $header = "{$protocol} {$code} {$description}";
707
  @header($header, true, $code);
708
  }
709
+
710
  public static function doNotCache() {
711
  header("Pragma: no-cache");
712
  header("Cache-Control: no-cache, must-revalidate, private");
716
  if (!defined('DONOTCDN')) { define('DONOTCDN', true); }
717
  if (!defined('DONOTCACHEOBJECT')) { define('DONOTCACHEOBJECT', true); }
718
  }
719
+
720
  /**
721
  * Check if an IP address is in a network block
722
  *
726
  */
727
  public static function subnetContainsIP($subnet, $ip) {
728
  list($network, $prefix) = array_pad(explode('/', $subnet, 2), 2, null);
729
+
730
  if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
731
  // If no prefix was supplied, 32 is implied for IPv4
732
  if ($prefix === null) {
733
  $prefix = 32;
734
  }
735
+
736
  // Validate the IPv4 network prefix
737
  if ($prefix < 0 || $prefix > 32) {
738
  return false;
739
  }
740
+
741
  // Increase the IPv4 network prefix to work in the IPv6 address space
742
  $prefix += 96;
743
  } else {
745
  if ($prefix === null) {
746
  $prefix = 128;
747
  }
748
+
749
  // Validate the IPv6 network prefix
750
  if ($prefix < 1 || $prefix > 128) {
751
  return false;
752
  }
753
  }
754
+
755
  $bin_network = wfWAFUtils::substr(self::inet_pton($network), 0, ceil($prefix / 8));
756
  $bin_ip = wfWAFUtils::substr(self::inet_pton($ip), 0, ceil($prefix / 8));
757
  if ($prefix % 8 != 0) { //Adjust the last relevant character to fit the mask length since the character's bits are split over it
760
  $bin_network[$pos] = ($bin_network[$pos] & $adjustment);
761
  $bin_ip[$pos] = ($bin_ip[$pos] & $adjustment);
762
  }
763
+
764
  return ($bin_network === $bin_ip);
765
  }
766
+
767
  /**
768
  * Behaves exactly like PHP's parse_url but uses WP's compatibility fixes for early PHP 5 versions.
769
+ *
770
  * @param string $url
771
  * @param int $component
772
  * @return mixed
774
  public static function parse_url($url, $component = -1) {
775
  $to_unset = array();
776
  $url = strval($url);
777
+
778
  if (substr($url, 0, 2) === '//') {
779
  $to_unset[] = 'scheme';
780
  $url = 'placeholder:' . $url;
784
  $to_unset[] = 'host';
785
  $url = 'placeholder://placeholder' . $url;
786
  }
787
+
788
  $parts = @parse_url($url);
789
+
790
  if ($parts === false) { // Parsing failure
791
  return $parts;
792
  }
793
+
794
  // Remove the placeholder values
795
  foreach ($to_unset as $key) {
796
  unset($parts[$key]);
797
  }
798
+
799
  if ($component === -1) {
800
  return $parts;
801
  }
802
+
803
  $translation = array(
804
  PHP_URL_SCHEME => 'scheme',
805
  PHP_URL_HOST => 'host',
810
  PHP_URL_QUERY => 'query',
811
  PHP_URL_FRAGMENT => 'fragment',
812
  );
813
+
814
  $key = false;
815
  if (isset($translation[$component])) {
816
  $key = $translation[$component];
817
  }
818
+
819
  if ($key !== false && is_array($parts) && isset($parts[$key])) {
820
  return $parts[$key];
821
  }
822
+
823
  return null;
824
  }
825
+
826
  /**
827
  * Validates the URL, supporting both scheme-relative and path-relative formats.
828
+ *
829
  * @param $url
830
  * @return mixed
831
  */
832
  public static function validate_url($url) {
833
  $url = strval($url);
834
+
835
  if (substr($url, 0, 2) === '//') {
836
  $url = 'placeholder:' . $url;
837
  }
838
  elseif (substr($url, 0, 1) === '/') {
839
  $url = 'placeholder://placeholder' . $url;
840
  }
841
+
842
  return filter_var($url, FILTER_VALIDATE_URL);
843
  }
844
+
845
  public static function rawPOSTBody() {
846
  // phpcs:ignore PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
847
  global $HTTP_RAW_POST_DATA;
874
  }
875
  return $data;
876
  }
877
+
878
  /**
879
  * Returns the current timestamp, adjusted as needed to get close to what we consider a true timestamp. We use this
880
  * because a significant number of servers are using a drastically incorrect time.
881
+ *
882
  * @return int
883
  */
884
  public static function normalizedTime() {
895
  }
896
  return time() + $offset;
897
  }
898
+
899
+ /**
900
+ * @param $file
901
+ * @return array|bool
902
+ */
903
+ public static function extractCredentialsWPConfig($file) {
904
+ $configContents = file_get_contents($file);
905
+ $tokens = token_get_all($configContents);
906
+ $tokens = array_values(array_filter($tokens, 'wfWAFUtils::_removeUnneededTokens'));
907
+
908
+ $parsedConstants = array();
909
+ $parsedVariables = array();
910
+ for ($i = 0; $i < count($tokens); $i++) {
911
+ $token = $tokens[$i];
912
+ if (is_array($token)) {
913
+ if (token_name($token[0]) === 'T_STRING' && strtolower($token[1]) === 'define') {
914
+ $startParenToken = $tokens[$i + 1];
915
+ $constantNameToken = $tokens[$i + 2];
916
+ $commaToken = $tokens[$i + 3];
917
+ $constantValueToken = $tokens[$i + 4];
918
+ $endParenToken = $tokens[$i + 5];
919
+ if (
920
+ !is_array($startParenToken) && $startParenToken === '(' &&
921
+ is_array($constantNameToken) && token_name($constantNameToken[0]) === 'T_CONSTANT_ENCAPSED_STRING' &&
922
+ !is_array($commaToken) && $commaToken === ',' &&
923
+ is_array($constantValueToken) && token_name($constantValueToken[0]) === 'T_CONSTANT_ENCAPSED_STRING' &&
924
+ !is_array($endParenToken) && $endParenToken === ')'
925
+ ) {
926
+ $parsedConstants[self::substr($constantNameToken[1], 1, -1)] = self::substr($constantValueToken[1], 1, -1);
927
+ }
928
+ }
929
+ if (token_name($token[0]) === 'T_VARIABLE') {
930
+ $assignmentToken = $tokens[$i + 1];
931
+ $variableValueToken = $tokens[$i + 2];
932
+ if (
933
+ !is_array($assignmentToken) && $assignmentToken === '=' &&
934
+ is_array($variableValueToken) && token_name($variableValueToken[0]) === 'T_CONSTANT_ENCAPSED_STRING'
935
+ ) {
936
+ $parsedVariables[$token[1]] = self::substr($variableValueToken[1], 1, -1);
937
+ }
938
+ }
939
+ }
940
+ }
941
+
942
+ $constants = array(
943
+ 'user' => 'DB_USER',
944
+ 'pass' => 'DB_PASSWORD',
945
+ 'database' => 'DB_NAME',
946
+ 'host' => 'DB_HOST',
947
+ 'charset' => 'DB_CHARSET',
948
+ 'collation' => 'DB_COLLATE',
949
+ );
950
+ $return = array();
951
+ foreach ($constants as $key => $constant) {
952
+ if (array_key_exists($constant, $parsedConstants)) {
953
+ $return[$key] = $parsedConstants[$constant];
954
+ } else {
955
+ return false;
956
+ }
957
+ }
958
+
959
+ /**
960
+ * @see \wpdb::parse_db_host
961
+ */
962
+ $socketPos = self::strpos($return['host'], ':/');
963
+ if ($socketPos !== false) {
964
+ $return['socket'] = self::substr($return['host'], $socketPos + 1);
965
+ $return['host'] = self::substr($return['host'], 0, $socketPos);
966
+ }
967
+
968
+ if ( self::substr_count( $return['host'], ':' ) > 1 ) {
969
+ $pattern = '#^(?:\[)?(?P<host>[0-9a-fA-F:]+)(?:\]:(?P<port>[\d]+))?#';
970
+ $return['ipv6'] = true;
971
+ } else {
972
+ $pattern = '#^(?P<host>[^:/]*)(?::(?P<port>[\d]+))?#';
973
+ }
974
+
975
+ $matches = array();
976
+ $result = preg_match($pattern, $return['host'], $matches);
977
+
978
+ if (1 !== $result) {
979
+ return false;
980
+ }
981
+
982
+ foreach (array('host', 'port') as $component) {
983
+ if (!empty($matches[$component])) {
984
+ $return[$component] = $matches[$component];
985
+ }
986
+ }
987
+
988
+ if (array_key_exists('$table_prefix', $parsedVariables)) {
989
+ $return['tablePrefix'] = $parsedVariables['$table_prefix'];
990
+ } else {
991
+ return false;
992
+ }
993
+ return $return;
994
+ }
995
+
996
+ protected static function _removeUnneededTokens($token) {
997
+ if (is_array($token)) {
998
+ return !in_array(token_name($token[0]), array(
999
+ 'T_DOC_COMMENT', 'T_WHITESPACE'
1000
+ ));
1001
+ }
1002
+ return true;
1003
+ }
1004
  }
1005
  }
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -9,9 +9,9 @@ class wfWAF {
9
  * @var wfWAF
10
  */
11
  private static $instance;
12
- private $blacklistedParams;
13
- private $whitelistedParams;
14
- private $variables = array();
15
 
16
  /**
17
  * @return wfWAF
@@ -84,11 +84,10 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
84
  * @param wfWAFEventBus $eventBus
85
  * @param string|null $rulesFile
86
  */
87
- public function __construct($request, $storageEngine, $eventBus = null, $rulesFile = null) {
88
  $this->setRequest($request);
89
  $this->setStorageEngine($storageEngine);
90
  $this->setEventBus($eventBus ? $eventBus : new wfWAFEventBus);
91
- $this->setCompiledRulesFile($rulesFile === null ? WFWAF_PATH . 'rules.php' : $rulesFile);
92
  }
93
 
94
  public function isReadOnly() {
@@ -320,14 +319,38 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
320
  *
321
  */
322
  public function loadRules() {
323
- if (file_exists($this->getCompiledRulesFile())) {
 
324
  // Acquire lock on this file so we're not including it while it's being written in another process.
325
- $handle = fopen($this->getCompiledRulesFile(), 'r');
326
  flock($handle, LOCK_SH);
327
  /** @noinspection PhpIncludeInspection */
328
- include $this->getCompiledRulesFile();
329
  flock($handle, LOCK_UN);
330
  fclose($handle);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  }
332
  }
333
 
@@ -377,9 +400,11 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
377
  'failedComparison' => $failedComparison,
378
  );
379
  }
380
- $this->debug[] = sprintf("%s tripped %s for %s->%s('%s'). Score %d/%d", $paramKey, $action,
381
- $category, $failedComparison->getAction(), $failedComparison->getExpected(),
382
- $this->scores[$paramKey][$category], $this->failScores[$category]);
 
 
383
  }
384
  }
385
  }
@@ -483,6 +508,13 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
483
  foreach ($movedKeys as $key => $category) {
484
  $value = $this->getStorageEngine()->getConfig($key, null, '');
485
  $this->getStorageEngine()->setConfig($key, $value, $category);
 
 
 
 
 
 
 
486
  $this->getStorageEngine()->unsetConfig($key, '');
487
  }
488
  }
@@ -618,6 +650,10 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
618
  }
619
 
620
  $authKey = $this->getStorageEngine()->getConfig('authKey');
 
 
 
 
621
  $json = wfWAFUtils::json_encode($signatures);
622
  $paddedKey = wfWAFUtils::substr(str_repeat($authKey, ceil(strlen($json) / strlen($authKey))), 0, strlen($json));
623
  $payload = $json ^ $paddedKey;
@@ -644,6 +680,10 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
644
 
645
  //Grab the list of words
646
  $authKey = $this->getStorageEngine()->getConfig('authKey');
 
 
 
 
647
  $encoded = base64_decode($encoded);
648
  $paddedKey = wfWAFUtils::substr(str_repeat($authKey, ceil(strlen($encoded) / strlen($authKey))), 0, strlen($encoded));
649
  $json = $encoded ^ $paddedKey;
@@ -697,6 +737,10 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
697
  }
698
 
699
  $authKey = $this->getStorageEngine()->getConfig('authKey');
 
 
 
 
700
  $json = wfWAFUtils::json_encode($commonStrings);
701
  $paddedKey = wfWAFUtils::substr(str_repeat($authKey, ceil(strlen($json) / strlen($authKey))), 0, strlen($json));
702
  $payload = $json ^ $paddedKey;
@@ -723,11 +767,15 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
723
  $rules = $parser->parse();
724
  }
725
 
726
- if (!is_writable($this->getCompiledRulesFile())) {
727
- throw new wfWAFBuildRulesException('Rules file not writable.');
728
- }
729
-
730
- wfWAFStorageFile::atomicFilePutContents($this->getCompiledRulesFile(), sprintf(<<<PHP
 
 
 
 
731
  <?php
732
  if (!defined('WFWAF_VERSION')) {
733
  exit('Access denied');
@@ -743,6 +791,10 @@ PHP
743
  wfWAFStorageFile::atomicFilePutContents($this->getStorageEngine()->getRulesDSLCacheFile(), $ruleString, 'rules');
744
  }
745
 
 
 
 
 
746
  if ($updateLastUpdatedTimestamp) {
747
  $this->getStorageEngine()->setConfig('rulesLastUpdated', is_int($updateLastUpdatedTimestamp) ? $updateLastUpdatedTimestamp : time(), 'transient');
748
  }
@@ -754,7 +806,7 @@ PHP
754
  }
755
 
756
  /**
757
- * @param string $rules
758
  * @return string
759
  * @throws wfWAFException
760
  */
@@ -1387,7 +1439,6 @@ HTML
1387
  *
1388
  */
1389
  public function uninstall() {
1390
- @unlink($this->getCompiledRulesFile());
1391
  $this->getStorageEngine()->uninstall();
1392
  }
1393
 
9
  * @var wfWAF
10
  */
11
  private static $instance;
12
+ protected $blacklistedParams;
13
+ protected $whitelistedParams;
14
+ protected $variables = array();
15
 
16
  /**
17
  * @return wfWAF
84
  * @param wfWAFEventBus $eventBus
85
  * @param string|null $rulesFile
86
  */
87
+ public function __construct($request, $storageEngine, $eventBus = null) {
88
  $this->setRequest($request);
89
  $this->setStorageEngine($storageEngine);
90
  $this->setEventBus($eventBus ? $eventBus : new wfWAFEventBus);
 
91
  }
92
 
93
  public function isReadOnly() {
319
  *
320
  */
321
  public function loadRules() {
322
+ $storageEngine = $this->getStorageEngine();
323
+ if ($storageEngine instanceof wfWAFStorageFile) {
324
  // Acquire lock on this file so we're not including it while it's being written in another process.
325
+ $handle = fopen($storageEngine->getRulesFile(), 'r');
326
  flock($handle, LOCK_SH);
327
  /** @noinspection PhpIncludeInspection */
328
+ include $storageEngine->getRulesFile();
329
  flock($handle, LOCK_UN);
330
  fclose($handle);
331
+ } else {
332
+ $wafRules = $storageEngine->getRules();
333
+ if (is_array($wafRules)) {
334
+ if (array_key_exists('rules', $wafRules)) {
335
+ /** @var wfWAFRule $rule */
336
+ foreach ($wafRules['rules'] as $rule) {
337
+ $rule->setWAF($this);
338
+ $this->rules[intval($rule->getRuleID())] = $rule;
339
+ }
340
+ }
341
+
342
+ $properties = array(
343
+ 'failScores',
344
+ 'variables',
345
+ 'whitelistedParams',
346
+ 'blacklistedParams',
347
+ );
348
+ foreach ($properties as $property) {
349
+ if (array_key_exists($property, $wafRules)) {
350
+ $this->{$property} = $wafRules[$property];
351
+ }
352
+ }
353
+ }
354
  }
355
  }
356
 
400
  'failedComparison' => $failedComparison,
401
  );
402
  }
403
+ if (defined('WFWAF_DEBUG') && WFWAF_DEBUG) {
404
+ $this->debug[] = sprintf("%s tripped %s for %s->%s('%s'). Score %d/%d", $paramKey, $action,
405
+ $category, $failedComparison->getAction(), $failedComparison->getExpected(),
406
+ $this->scores[$paramKey][$category], $this->failScores[$category]);
407
+ }
408
  }
409
  }
410
  }
508
  foreach ($movedKeys as $key => $category) {
509
  $value = $this->getStorageEngine()->getConfig($key, null, '');
510
  $this->getStorageEngine()->setConfig($key, $value, $category);
511
+
512
+ if ($this->getStorageEngine() instanceof wfWAFStorageMySQL &&
513
+ $this->getStorageEngine()->getStorageTable($category) === $this->getStorageEngine()->getStorageTable('')
514
+ ) {
515
+ continue;
516
+ }
517
+
518
  $this->getStorageEngine()->unsetConfig($key, '');
519
  }
520
  }
650
  }
651
 
652
  $authKey = $this->getStorageEngine()->getConfig('authKey');
653
+ if (strlen($authKey) === 0) {
654
+ return;
655
+ }
656
+
657
  $json = wfWAFUtils::json_encode($signatures);
658
  $paddedKey = wfWAFUtils::substr(str_repeat($authKey, ceil(strlen($json) / strlen($authKey))), 0, strlen($json));
659
  $payload = $json ^ $paddedKey;
680
 
681
  //Grab the list of words
682
  $authKey = $this->getStorageEngine()->getConfig('authKey');
683
+ if (strlen($authKey) === 0) {
684
+ return array();
685
+ }
686
+
687
  $encoded = base64_decode($encoded);
688
  $paddedKey = wfWAFUtils::substr(str_repeat($authKey, ceil(strlen($encoded) / strlen($authKey))), 0, strlen($encoded));
689
  $json = $encoded ^ $paddedKey;
737
  }
738
 
739
  $authKey = $this->getStorageEngine()->getConfig('authKey');
740
+ if (strlen($authKey) === 0) {
741
+ return;
742
+ }
743
+
744
  $json = wfWAFUtils::json_encode($commonStrings);
745
  $paddedKey = wfWAFUtils::substr(str_repeat($authKey, ceil(strlen($json) / strlen($authKey))), 0, strlen($json));
746
  $payload = $json ^ $paddedKey;
767
  $rules = $parser->parse();
768
  }
769
 
770
+ $storageEngine = $this->getStorageEngine();
771
+ if ($storageEngine instanceof wfWAFStorageFile) {
772
+ if ((!is_file($storageEngine->getRulesFile()) && !is_writeable(dirname($storageEngine->getRulesFile()))) ||
773
+ (is_file($storageEngine->getRulesFile()) && !is_writable($storageEngine->getRulesFile()))
774
+ ) {
775
+ throw new wfWAFBuildRulesException('Rules file not writable.');
776
+ }
777
+
778
+ wfWAFStorageFile::atomicFilePutContents($storageEngine->getRulesFile(), sprintf(<<<PHP
779
  <?php
780
  if (!defined('WFWAF_VERSION')) {
781
  exit('Access denied');
791
  wfWAFStorageFile::atomicFilePutContents($this->getStorageEngine()->getRulesDSLCacheFile(), $ruleString, 'rules');
792
  }
793
 
794
+ } else {
795
+ $this->getStorageEngine()->setRules($rules);
796
+ }
797
+
798
  if ($updateLastUpdatedTimestamp) {
799
  $this->getStorageEngine()->setConfig('rulesLastUpdated', is_int($updateLastUpdatedTimestamp) ? $updateLastUpdatedTimestamp : time(), 'transient');
800
  }
806
  }
807
 
808
  /**
809
+ * @param string|array $rules
810
  * @return string
811
  * @throws wfWAFException
812
  */
1439
  *
1440
  */
1441
  public function uninstall() {
 
1442
  $this->getStorageEngine()->uninstall();
1443
  }
1444
 
views/waf/option-rules.php CHANGED
@@ -41,6 +41,9 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
41
  catch (wfWAFStorageFileException $e) {
42
  error_log($e->getMessage());
43
  }
 
 
 
44
  if (!empty($lastUpdated)): ?>
45
  var lastUpdated = <?php echo (int) $lastUpdated ?>;
46
  WFAD.renderWAFRulesLastUpdated(new Date(lastUpdated * 1000));
41
  catch (wfWAFStorageFileException $e) {
42
  error_log($e->getMessage());
43
  }
44
+ catch (wfWAFStorageEngineMySQLiException $e) {
45
+ error_log($e->getMessage());
46
+ }
47
  if (!empty($lastUpdated)): ?>
48
  var lastUpdated = <?php echo (int) $lastUpdated ?>;
49
  WFAD.renderWAFRulesLastUpdated(new Date(lastUpdated * 1000));
views/waf/waf-install.php CHANGED
@@ -17,7 +17,7 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
17
  <div class="wf-modal-content">
18
  <?php
19
  $currentAutoPrependFile = ini_get('auto_prepend_file');
20
- if (empty($currentAutoPrependFile)):
21
  ?>
22
  <p><?php _e('To make your site as secure as possible, the Wordfence Web Application Firewall is designed to run via a PHP setting called <code>auto_prepend_file</code>, which ensures it runs before any potentially vulnerable code runs.', 'wordfence'); ?></p>
23
  <?php else: ?>
17
  <div class="wf-modal-content">
18
  <?php
19
  $currentAutoPrependFile = ini_get('auto_prepend_file');
20
+ if (empty($currentAutoPrependFile) || WF_IS_WP_ENGINE):
21
  ?>
22
  <p><?php _e('To make your site as secure as possible, the Wordfence Web Application Firewall is designed to run via a PHP setting called <code>auto_prepend_file</code>, which ensures it runs before any potentially vulnerable code runs.', 'wordfence'); ?></p>
23
  <?php else: ?>
views/waf/waf-uninstall.php CHANGED
@@ -16,7 +16,12 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
16
  </div>
17
  <div class="wf-modal-content">
18
  <?php
19
- $currentAutoPrependFile = ini_get('auto_prepend_file');
 
 
 
 
 
20
  ?>
21
  <p><?php _e('Extended Protection Mode of the Wordfence Web Application Firewall uses the PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially vulnerable code runs. This PHP setting currently refers to the Wordfence file at:', 'wordfence'); ?></p>
22
  <pre class='wf-pre'><?php echo esc_html($currentAutoPrependFile); ?></pre>
16
  </div>
17
  <div class="wf-modal-content">
18
  <?php
19
+ if (WF_IS_WP_ENGINE) {
20
+ $currentAutoPrependFile = wordfence::getWAFBootstrapPath();
21
+ } else {
22
+ $currentAutoPrependFile = ini_get('auto_prepend_file');
23
+ }
24
+
25
  ?>
26
  <p><?php _e('Extended Protection Mode of the Wordfence Web Application Firewall uses the PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially vulnerable code runs. This PHP setting currently refers to the Wordfence file at:', 'wordfence'); ?></p>
27
  <pre class='wf-pre'><?php echo esc_html($currentAutoPrependFile); ?></pre>
waf/bootstrap.php CHANGED
@@ -9,6 +9,10 @@ if (!defined('WFWAF_RUN_COMPLETE')) {
9
  if (!defined('WFWAF_AUTO_PREPEND')) {
10
  define('WFWAF_AUTO_PREPEND', true);
11
  }
 
 
 
 
12
 
13
  require_once(dirname(__FILE__) . '/wfWAFUserIPRange.php');
14
  require_once(dirname(__FILE__) . '/wfWAFIPBlocksController.php');
@@ -35,6 +39,7 @@ class wfWAFWordPressRequest extends wfWAFRequest {
35
  if (isset($theIP)) {
36
  return $theIP;
37
  }
 
38
  $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs', null, 'synced');
39
  if ($howGet) {
40
  if (is_string($howGet) && is_array($_SERVER) && array_key_exists($howGet, $_SERVER)) {
@@ -695,34 +700,89 @@ if (!is_dir(WFWAF_LOG_PATH)) {
695
  wfWAFWordPress::writeHtaccess();
696
  }
697
 
698
- wfWAF::setSharedStorageEngine(new wfWAFStorageFile(WFWAF_LOG_PATH . 'attack-data.php', WFWAF_LOG_PATH . 'ips.php', WFWAF_LOG_PATH . 'config.php', WFWAF_LOG_PATH . 'wafRules.rules'));
699
- wfWAF::setInstance(new wfWAFWordPress(
700
- wfWAFWordPressRequest::createFromGlobals(),
701
- wfWAF::getSharedStorageEngine()
702
- ));
703
- wfWAF::getInstance()->getEventBus()->attach(new wfWAFWordPressObserver);
704
 
705
  try {
706
- $rulesFiles = array(
707
- WFWAF_LOG_PATH . 'rules.php',
708
- // WFWAF_PATH . 'rules.php',
709
- );
710
- foreach ($rulesFiles as $rulesFile) {
711
- if (!file_exists($rulesFile) && !wfWAF::getInstance()->isReadOnly()) {
712
- @touch($rulesFile);
713
- }
714
- @chmod($rulesFile, (wfWAFWordPress::permissions() | 0444));
715
- if (is_writable($rulesFile)) {
716
- wfWAF::getInstance()->setCompiledRulesFile($rulesFile);
717
- break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
718
  }
719
  }
720
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  if (!wfWAF::getInstance()->isReadOnly()) {
722
- if (!file_exists(wfWAF::getInstance()->getCompiledRulesFile()) || !filesize(wfWAF::getInstance()->getCompiledRulesFile())) {
723
  try {
724
- if (is_writable(wfWAF::getInstance()->getCompiledRulesFile()) &&
725
- wfWAF::getInstance()->getStorageEngine()->getConfig('apiKey', null, 'synced') !== null &&
726
  wfWAF::getInstance()->getStorageEngine()->getConfig('createInitialRulesDelay', null, 'transient') < time()
727
  ) {
728
  $event = new wfWAFCronFetchRulesEvent(time() - 60);
@@ -764,6 +824,10 @@ try {
764
  // Let this request through for now
765
  error_log($e->getMessage());
766
 
 
 
 
 
767
  } catch (wfWAFStorageFileException $e) {
768
  // We need to choose another storage engine here.
769
  }
9
  if (!defined('WFWAF_AUTO_PREPEND')) {
10
  define('WFWAF_AUTO_PREPEND', true);
11
  }
12
+ if (!defined('WF_IS_WP_ENGINE')) {
13
+ define('WF_IS_WP_ENGINE', isset($_SERVER['IS_WPE']));
14
+ }
15
+
16
 
17
  require_once(dirname(__FILE__) . '/wfWAFUserIPRange.php');
18
  require_once(dirname(__FILE__) . '/wfWAFIPBlocksController.php');
39
  if (isset($theIP)) {
40
  return $theIP;
41
  }
42
+ $ips = array();
43
  $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs', null, 'synced');
44
  if ($howGet) {
45
  if (is_string($howGet) && is_array($_SERVER) && array_key_exists($howGet, $_SERVER)) {
700
  wfWAFWordPress::writeHtaccess();
701
  }
702
 
 
 
 
 
 
 
703
 
704
  try {
705
+
706
+ if (!defined('WFWAF_STORAGE_ENGINE') && WF_IS_WP_ENGINE) {
707
+ define('WFWAF_STORAGE_ENGINE', 'mysqli');
708
+ }
709
+
710
+ if (defined('WFWAF_STORAGE_ENGINE')) {
711
+ switch (WFWAF_STORAGE_ENGINE) {
712
+ case 'mysqli':
713
+ // Find the wp-config.php
714
+ if (file_exists(dirname(WFWAF_LOG_PATH) . '/../wp-config.php')) {
715
+ $wfWAFDBCredentials = wfWAFUtils::extractCredentialsWPConfig(WFWAF_LOG_PATH . '/../../wp-config.php');
716
+ } else if (file_exists(dirname(WFWAF_LOG_PATH) . '/../../wp-config.php')) {
717
+ $wfWAFDBCredentials = wfWAFUtils::extractCredentialsWPConfig(WFWAF_LOG_PATH . '/../../../wp-config.php');
718
+ }
719
+
720
+ if (!empty($wfWAFDBCredentials)) {
721
+ $wfWAFStorageEngine = new wfWAFStorageMySQL(new wfWAFStorageEngineMySQLi(), $wfWAFDBCredentials['tablePrefix']);
722
+ $wfWAFStorageEngine->getDb()->connect(
723
+ $wfWAFDBCredentials['user'],
724
+ $wfWAFDBCredentials['pass'],
725
+ $wfWAFDBCredentials['database'],
726
+ !empty($wfWAFDBCredentials['ipv6']) ? '[' . $wfWAFDBCredentials['host'] . ']' : $wfWAFDBCredentials['host'],
727
+ !empty($wfWAFDBCredentials['port']) ? $wfWAFDBCredentials['port'] : null,
728
+ !empty($wfWAFDBCredentials['socket']) ? $wfWAFDBCredentials['socket'] : null
729
+ );
730
+ if (array_key_exists('charset', $wfWAFDBCredentials)) {
731
+ $wfWAFStorageEngine->getDb()
732
+ ->setCharset($wfWAFDBCredentials['charset'],
733
+ !empty($wfWAFDBCredentials['collation']) ? $wfWAFDBCredentials['collation'] : '');
734
+ }
735
+ if (function_exists('get_option')) {
736
+ $wfWAFStorageEngine->installing = !get_option('wordfenceActivated');
737
+ $wfWAFStorageEngine->getDb()->installing = $wfWAFStorageEngine->installing;
738
+ }
739
+
740
+ } else {
741
+ unset($wfWAFDBCredentials);
742
+ }
743
+
744
+ break;
745
  }
746
  }
747
+
748
+ if (empty($wfWAFStorageEngine)) {
749
+ $wfWAFStorageEngine = new wfWAFStorageFile(
750
+ WFWAF_LOG_PATH . 'attack-data.php',
751
+ WFWAF_LOG_PATH . 'ips.php',
752
+ WFWAF_LOG_PATH . 'config.php',
753
+ WFWAF_LOG_PATH . 'rules.php',
754
+ WFWAF_LOG_PATH . 'wafRules.rules'
755
+ );
756
+ }
757
+
758
+ wfWAF::setSharedStorageEngine($wfWAFStorageEngine);
759
+ wfWAF::setInstance(new wfWAFWordPress(wfWAFWordPressRequest::createFromGlobals(), wfWAF::getSharedStorageEngine()));
760
+ wfWAF::getInstance()->getEventBus()->attach(new wfWAFWordPressObserver);
761
+
762
+ if ($wfWAFStorageEngine instanceof wfWAFStorageFile) {
763
+ $rulesFiles = array(
764
+ WFWAF_LOG_PATH . 'rules.php',
765
+ // WFWAF_PATH . 'rules.php',
766
+ );
767
+ foreach ($rulesFiles as $rulesFile) {
768
+ if (!file_exists($rulesFile) && !wfWAF::getInstance()->isReadOnly()) {
769
+ @touch($rulesFile);
770
+ }
771
+ @chmod($rulesFile, (wfWAFWordPress::permissions() | 0444));
772
+ if (is_writable($rulesFile)) {
773
+ wfWAF::getInstance()->setCompiledRulesFile($rulesFile);
774
+ break;
775
+ }
776
+ }
777
+ } else if ($wfWAFStorageEngine instanceof wfWAFStorageMySQL) {
778
+ $wfWAFStorageEngine->runMigrations();
779
+ $wfWAFStorageEngine->setDefaults();
780
+ }
781
+
782
  if (!wfWAF::getInstance()->isReadOnly()) {
783
+ if (wfWAF::getInstance()->getStorageEngine()->needsInitialRules()) {
784
  try {
785
+ if (wfWAF::getInstance()->getStorageEngine()->getConfig('apiKey', null, 'synced') !== null &&
 
786
  wfWAF::getInstance()->getStorageEngine()->getConfig('createInitialRulesDelay', null, 'transient') < time()
787
  ) {
788
  $event = new wfWAFCronFetchRulesEvent(time() - 60);
824
  // Let this request through for now
825
  error_log($e->getMessage());
826
 
827
+ } catch (wfWAFStorageEngineMySQLiException $e) {
828
+ // Let this request through for now
829
+ error_log($e->getMessage());
830
+
831
  } catch (wfWAFStorageFileException $e) {
832
  // We need to choose another storage engine here.
833
  }
waf/wfWAFIPBlocksController.php CHANGED
@@ -46,7 +46,7 @@ class wfWAFIPBlocksController
46
  }
47
 
48
  public static function synchronizeConfigSettings() {
49
- if (!class_exists('wfConfig') || !wfConfig::tableExists()) { // Ensure this is only called when WordPress and the plugin are fully loaded
50
  return;
51
  }
52
 
46
  }
47
 
48
  public static function synchronizeConfigSettings() {
49
+ if (!class_exists('wfConfig') || !wfConfig::tableExists() || !wfWAF::getInstance()) { // Ensure this is only called when WordPress and the plugin are fully loaded
50
  return;
51
  }
52
 
wordfence.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
- Version: 7.3.6
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
@@ -15,8 +15,8 @@ if(defined('WP_INSTALLING') && WP_INSTALLING){
15
  if (!defined('ABSPATH')) {
16
  exit;
17
  }
18
- define('WORDFENCE_VERSION', '7.3.6');
19
- define('WORDFENCE_BUILD_NUMBER', '1564590761');
20
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
21
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
22
 
@@ -34,7 +34,9 @@ if (!defined('WORDFENCE_FCPATH')) {
34
  /** @noinspection PhpConstantReassignmentInspection */
35
  define('WORDFENCE_PATH', trailingslashit(dirname(WORDFENCE_FCPATH)));
36
  }
37
-
 
 
38
 
39
  if(get_option('wordfenceActivated') != 1){
40
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
+ Version: 7.4.0
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
15
  if (!defined('ABSPATH')) {
16
  exit;
17
  }
18
+ define('WORDFENCE_VERSION', '7.4.0');
19
+ define('WORDFENCE_BUILD_NUMBER', '1566486436');
20
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
21
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
22
 
34
  /** @noinspection PhpConstantReassignmentInspection */
35
  define('WORDFENCE_PATH', trailingslashit(dirname(WORDFENCE_FCPATH)));
36
  }
37
+ if (!defined('WF_IS_WP_ENGINE')) {
38
+ define('WF_IS_WP_ENGINE', isset($_SERVER['IS_WPE']));
39
+ }
40
 
41
  if(get_option('wordfenceActivated') != 1){
42
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }