Wordfence Security – Firewall & Malware Scan - Version 7.1.3

Version Description

  • Improvement: Improved the performance of our config table status check.
  • Improvement: The IP address of the user activating Wordfence is now used by the breached password check until an admin successfully logs in.
  • Improvement: Added several new error displays for scan failures to help diagnose and fix issues.
  • Improvement: Added the block duration to alerts generated when an IP is blocked.
  • Improvement: A text version of scan results is now included in the activity log email.
  • Improvement: The WAF install/uninstall process no longer asks to backup files that do not exist.
  • Change: Began a phased rollout of moving brute force queries to be https-only.
  • Change: Added the initial deprecation notice for PHP 5.2.
  • Change: Suppressed a script tag on the diagnostics page from being output in the email version.
  • Fix: Addressed an issue where plugins that return a null user during authentication would cause a PHP notice to be logged.
  • Fix: Fixed an issue where plugins that use non-standard version formatting could end up with a inaccurate vulnerability status.
  • Fix: Added a workaround for web email clients that erroneously encode some URL characters (e.g., #).
Download this release

Release Info

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

Code changes from version 7.1.2 to 7.1.3

Files changed (92) hide show
  1. css/{activity-report-widget.1522855379.css → activity-report-widget.1523983877.css} +0 -0
  2. css/{diff.1522855379.css → diff.1523983877.css} +0 -0
  3. css/{dt_table.1522855379.css → dt_table.1523983877.css} +0 -0
  4. css/{fullLog.1522855379.css → fullLog.1523983877.css} +0 -0
  5. css/{iptraf.1522855379.css → iptraf.1523983877.css} +0 -0
  6. css/{jquery-ui-timepicker-addon.1522855379.css → jquery-ui-timepicker-addon.1523983877.css} +0 -0
  7. css/{jquery-ui.min.1522855379.css → jquery-ui.min.1523983877.css} +0 -0
  8. css/{jquery-ui.structure.min.1522855379.css → jquery-ui.structure.min.1523983877.css} +0 -0
  9. css/{jquery-ui.theme.min.1522855379.css → jquery-ui.theme.min.1523983877.css} +0 -0
  10. css/{main.1522855379.css → main.1523983877.css} +0 -0
  11. css/{phpinfo.1522855379.css → phpinfo.1523983877.css} +0 -0
  12. css/{wf-adminbar.1522855379.css → wf-adminbar.1523983877.css} +0 -0
  13. css/{wf-colorbox.1522855379.css → wf-colorbox.1523983877.css} +0 -0
  14. css/{wf-font-awesome.1522855379.css → wf-font-awesome.1523983877.css} +0 -0
  15. css/{wf-ionicons.1522855379.css → wf-ionicons.1523983877.css} +0 -0
  16. css/{wf-onboarding.1522855379.css → wf-onboarding.1523983877.css} +0 -0
  17. css/{wfselect2.min.1522855379.css → wfselect2.min.1523983877.css} +0 -0
  18. css/{wordfenceBox.1522855379.css → wordfenceBox.1523983877.css} +0 -0
  19. js/{Chart.bundle.min.1522855379.js → Chart.bundle.min.1523983877.js} +0 -0
  20. js/{admin.1522855379.js → admin.1523983877.js} +13 -2
  21. js/{admin.ajaxWatcher.1522855379.js → admin.ajaxWatcher.1523983877.js} +0 -0
  22. js/{admin.liveTraffic.1522855379.js → admin.liveTraffic.1523983877.js} +0 -0
  23. js/{date.1522855379.js → date.1523983877.js} +0 -0
  24. js/{jquery-ui-timepicker-addon.1522855379.js → jquery-ui-timepicker-addon.1523983877.js} +0 -0
  25. js/{jquery.colorbox-min.1522855379.js → jquery.colorbox-min.1523983877.js} +0 -0
  26. js/{jquery.colorbox.1522855379.js → jquery.colorbox.1523983877.js} +0 -0
  27. js/{jquery.dataTables.min.1522855379.js → jquery.dataTables.min.1523983877.js} +0 -0
  28. js/{jquery.qrcode.min.1522855379.js → jquery.qrcode.min.1523983877.js} +0 -0
  29. js/{jquery.tmpl.min.1522855379.js → jquery.tmpl.min.1523983877.js} +0 -0
  30. js/{jquery.tools.min.1522855379.js → jquery.tools.min.1523983877.js} +0 -0
  31. js/{knockout-3.3.0.1522855379.js → knockout-3.3.0.1523983877.js} +0 -0
  32. js/{perf.1522855379.js → perf.1523983877.js} +0 -0
  33. js/{wfdashboard.1522855379.js → wfdashboard.1523983877.js} +0 -0
  34. js/{wfdropdown.1522855379.js → wfdropdown.1523983877.js} +0 -0
  35. js/{wfglobal.1522855379.js → wfglobal.1523983877.js} +0 -0
  36. js/{wfpopover.1522855379.js → wfpopover.1523983877.js} +0 -0
  37. js/{wfselect2.min.1522855379.js → wfselect2.min.1523983877.js} +0 -0
  38. lib/menu_dashboard_options.php +1 -1
  39. lib/menu_firewall_blocking_options.php +1 -1
  40. lib/menu_firewall_waf_options.php +1 -1
  41. lib/menu_options.php +1 -1
  42. lib/menu_scanner_options.php +1 -1
  43. lib/menu_tools_diagnostic.php +2 -0
  44. lib/wfAPI.php +18 -6
  45. lib/wfConfig.php +24 -8
  46. lib/wfCredentialsController.php +20 -2
  47. lib/wfDiagnostic.php +2 -2
  48. lib/wfIssues.php +24 -0
  49. lib/wfLog.php +8 -3
  50. lib/wfScan.php +109 -65
  51. lib/wfScanEngine.php +75 -21
  52. lib/wfSupportController.php +10 -0
  53. lib/wfUtils.php +0 -39
  54. lib/wfVersionCheckController.php +291 -0
  55. lib/wordfenceClass.php +135 -11
  56. lib/wordfenceConstants.php +2 -0
  57. readme.txt +16 -1
  58. views/blocking/block-list.php +1 -1
  59. views/scanner/issue-base.php +103 -0
  60. views/scanner/issue-checkGSB.php +6 -0
  61. views/scanner/issue-checkHowGetIPs.php +4 -0
  62. views/scanner/issue-checkSpamIP.php +4 -0
  63. views/scanner/issue-commentBadURL.php +13 -1
  64. views/scanner/issue-configReadable.php +7 -1
  65. views/scanner/issue-coreUnknown.php +5 -1
  66. views/scanner/issue-database.php +7 -0
  67. views/scanner/issue-diskSpace.php +7 -1
  68. views/scanner/issue-dnsChange.php +8 -1
  69. views/scanner/issue-easyPassword.php +9 -1
  70. views/scanner/issue-file.php +11 -1
  71. views/scanner/issue-geoipSupport.php +5 -1
  72. views/scanner/issue-knownfile.php +10 -1
  73. views/scanner/issue-optionBadURL.php +11 -0
  74. views/scanner/issue-postBadTitle.php +12 -1
  75. views/scanner/issue-postBadURL.php +13 -1
  76. views/scanner/issue-publiclyAccessible.php +7 -1
  77. views/scanner/issue-spamvertizeCheck.php +4 -0
  78. views/scanner/issue-suspiciousAdminUsers.php +5 -1
  79. views/scanner/issue-timelimit.php +5 -1
  80. views/scanner/issue-wfPluginAbandoned.php +14 -1
  81. views/scanner/issue-wfPluginRemoved.php +11 -1
  82. views/scanner/issue-wfPluginUpgrade.php +14 -1
  83. views/scanner/issue-wfPluginVulnerable.php +12 -1
  84. views/scanner/issue-wfThemeUpgrade.php +13 -1
  85. views/scanner/issue-wfUpgrade.php +11 -1
  86. views/scanner/issue-wpscan_directoryList.php +7 -1
  87. views/scanner/issue-wpscan_fullPathDiscl.php +7 -1
  88. views/scanner/scan-failed.php +1 -1
  89. views/waf/options-group-basic-firewall.php +10 -3
  90. views/waf/waf-install.php +12 -4
  91. views/waf/waf-uninstall.php +12 -4
  92. wordfence.php +3 -3
css/{activity-report-widget.1522855379.css → activity-report-widget.1523983877.css} RENAMED
File without changes
css/{diff.1522855379.css → diff.1523983877.css} RENAMED
File without changes
css/{dt_table.1522855379.css → dt_table.1523983877.css} RENAMED
File without changes
css/{fullLog.1522855379.css → fullLog.1523983877.css} RENAMED
File without changes
css/{iptraf.1522855379.css → iptraf.1523983877.css} RENAMED
File without changes
css/{jquery-ui-timepicker-addon.1522855379.css → jquery-ui-timepicker-addon.1523983877.css} RENAMED
File without changes
css/{jquery-ui.min.1522855379.css → jquery-ui.min.1523983877.css} RENAMED
File without changes
css/{jquery-ui.structure.min.1522855379.css → jquery-ui.structure.min.1523983877.css} RENAMED
File without changes
css/{jquery-ui.theme.min.1522855379.css → jquery-ui.theme.min.1523983877.css} RENAMED
File without changes
css/{main.1522855379.css → main.1523983877.css} RENAMED
File without changes
css/{phpinfo.1522855379.css → phpinfo.1523983877.css} RENAMED
File without changes
css/{wf-adminbar.1522855379.css → wf-adminbar.1523983877.css} RENAMED
File without changes
css/{wf-colorbox.1522855379.css → wf-colorbox.1523983877.css} RENAMED
File without changes
css/{wf-font-awesome.1522855379.css → wf-font-awesome.1523983877.css} RENAMED
File without changes
css/{wf-ionicons.1522855379.css → wf-ionicons.1523983877.css} RENAMED
File without changes
css/{wf-onboarding.1522855379.css → wf-onboarding.1523983877.css} RENAMED
File without changes
css/{wfselect2.min.1522855379.css → wfselect2.min.1523983877.css} RENAMED
File without changes
css/{wordfenceBox.1522855379.css → wordfenceBox.1523983877.css} RENAMED
File without changes
js/{Chart.bundle.min.1522855379.js → Chart.bundle.min.1523983877.js} RENAMED
File without changes
js/{admin.1522855379.js → admin.1523983877.js} RENAMED
@@ -128,7 +128,7 @@
128
  $(window).trigger('wfTabChange', [tab.data('target')]);
129
  });
130
  if (window.location.hash) {
131
- var hashes = window.location.hash.split('#');
132
  var hash = hashes[hashes.length - 1];
133
  for (var i = 0; i < tabs.length; i++) {
134
  if (hash == jQuery(tabs[i]).closest('.wf-tab').data('target')) {
@@ -140,7 +140,7 @@
140
  jQuery(tabs[0]).trigger('click');
141
  }
142
  jQuery(window).on('hashchange', function () {
143
- var hashes = window.location.hash.split('#');
144
  var hash = hashes[hashes.length - 1];
145
  for (var i = 0; i < tabs.length; i++) {
146
  if (hash == jQuery(tabs[i]).closest('.wf-tab').data('target')) {
@@ -2226,6 +2226,17 @@
2226
  makeViewOptionLink: function(option, siteID) {
2227
  return WordfenceAdminVars.siteBaseURL + '?_wfsf=viewOption&nonce=' + this.nonce + '&option=' + encodeURIComponent(option) + '&site_id=' + encodeURIComponent(siteID);
2228
  },
 
 
 
 
 
 
 
 
 
 
 
2229
  makeTimeAgo: function(t) {
2230
  var months = Math.floor(t / (86400 * 30));
2231
  var days = Math.floor(t / 86400);
128
  $(window).trigger('wfTabChange', [tab.data('target')]);
129
  });
130
  if (window.location.hash) {
131
+ var hashes = WFAD.parseHashes();
132
  var hash = hashes[hashes.length - 1];
133
  for (var i = 0; i < tabs.length; i++) {
134
  if (hash == jQuery(tabs[i]).closest('.wf-tab').data('target')) {
140
  jQuery(tabs[0]).trigger('click');
141
  }
142
  jQuery(window).on('hashchange', function () {
143
+ var hashes = WFAD.parseHashes();
144
  var hash = hashes[hashes.length - 1];
145
  for (var i = 0; i < tabs.length; i++) {
146
  if (hash == jQuery(tabs[i]).closest('.wf-tab').data('target')) {
2226
  makeViewOptionLink: function(option, siteID) {
2227
  return WordfenceAdminVars.siteBaseURL + '?_wfsf=viewOption&nonce=' + this.nonce + '&option=' + encodeURIComponent(option) + '&site_id=' + encodeURIComponent(siteID);
2228
  },
2229
+ parseHashes: function() {
2230
+ var hashes = window.location.hash.replace('%23', '#');
2231
+ var splitHashes = hashes.split('#');
2232
+ var result = [];
2233
+ for (var i = 0; i < splitHashes.length; i++) {
2234
+ if (splitHashes[i].length > 0) {
2235
+ result.push(splitHashes[i]);
2236
+ }
2237
+ }
2238
+ return result;
2239
+ },
2240
  makeTimeAgo: function(t) {
2241
  var months = Math.floor(t / (86400 * 30));
2242
  var days = Math.floor(t / 86400);
js/{admin.ajaxWatcher.1522855379.js → admin.ajaxWatcher.1523983877.js} RENAMED
File without changes
js/{admin.liveTraffic.1522855379.js → admin.liveTraffic.1523983877.js} RENAMED
File without changes
js/{date.1522855379.js → date.1523983877.js} RENAMED
File without changes
js/{jquery-ui-timepicker-addon.1522855379.js → jquery-ui-timepicker-addon.1523983877.js} RENAMED
File without changes
js/{jquery.colorbox-min.1522855379.js → jquery.colorbox-min.1523983877.js} RENAMED
File without changes
js/{jquery.colorbox.1522855379.js → jquery.colorbox.1523983877.js} RENAMED
File without changes
js/{jquery.dataTables.min.1522855379.js → jquery.dataTables.min.1523983877.js} RENAMED
File without changes
js/{jquery.qrcode.min.1522855379.js → jquery.qrcode.min.1523983877.js} RENAMED
File without changes
js/{jquery.tmpl.min.1522855379.js → jquery.tmpl.min.1523983877.js} RENAMED
File without changes
js/{jquery.tools.min.1522855379.js → jquery.tools.min.1523983877.js} RENAMED
File without changes
js/{knockout-3.3.0.1522855379.js → knockout-3.3.0.1523983877.js} RENAMED
File without changes
js/{perf.1522855379.js → perf.1523983877.js} RENAMED
File without changes
js/{wfdashboard.1522855379.js → wfdashboard.1523983877.js} RENAMED
File without changes
js/{wfdropdown.1522855379.js → wfdropdown.1523983877.js} RENAMED
File without changes
js/{wfglobal.1522855379.js → wfglobal.1523983877.js} RENAMED
File without changes
js/{wfpopover.1522855379.js → wfpopover.1523983877.js} RENAMED
File without changes
js/{wfselect2.min.1522855379.js → wfselect2.min.1523983877.js} RENAMED
File without changes
lib/menu_dashboard_options.php CHANGED
@@ -12,7 +12,7 @@ $d = new wfDashboard();
12
 
13
  //Hash-based option block linking
14
  if (window.location.hash) {
15
- var hashes = window.location.hash.split('#');
16
  var hash = hashes[hashes.length - 1];
17
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
18
  if (block) {
12
 
13
  //Hash-based option block linking
14
  if (window.location.hash) {
15
+ var hashes = WFAD.parseHashes();
16
  var hash = hashes[hashes.length - 1];
17
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
18
  if (block) {
lib/menu_firewall_blocking_options.php CHANGED
@@ -13,7 +13,7 @@ if (isset($_GET['source']) && wfPage::isValidPage($_GET['source'])) {
13
 
14
  //Hash-based option block linking
15
  if (window.location.hash) {
16
- var hashes = window.location.hash.split('#');
17
  var hash = hashes[hashes.length - 1];
18
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
19
  if (block) {
13
 
14
  //Hash-based option block linking
15
  if (window.location.hash) {
16
+ var hashes = WFAD.parseHashes();
17
  var hash = hashes[hashes.length - 1];
18
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
19
  if (block) {
lib/menu_firewall_waf_options.php CHANGED
@@ -26,7 +26,7 @@ if (isset($_GET['source']) && wfPage::isValidPage($_GET['source'])) {
26
 
27
  //Hash-based option block linking
28
  if (window.location.hash) {
29
- var hashes = window.location.hash.split('#');
30
  var hash = hashes[hashes.length - 1];
31
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
32
  if (block.length) {
26
 
27
  //Hash-based option block linking
28
  if (window.location.hash) {
29
+ var hashes = WFAD.parseHashes();
30
  var hash = hashes[hashes.length - 1];
31
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
32
  if (block.length) {
lib/menu_options.php CHANGED
@@ -27,7 +27,7 @@ if (isset($_GET['source']) && wfPage::isValidPage($_GET['source'])) {
27
 
28
  //Hash-based option block linking
29
  if (window.location.hash) {
30
- var hashes = window.location.hash.split('#');
31
  var hash = hashes[hashes.length - 1];
32
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
33
  if (block.length) {
27
 
28
  //Hash-based option block linking
29
  if (window.location.hash) {
30
+ var hashes = WFAD.parseHashes();
31
  var hash = hashes[hashes.length - 1];
32
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
33
  if (block.length) {
lib/menu_scanner_options.php CHANGED
@@ -15,7 +15,7 @@ if (isset($_GET['source']) && wfPage::isValidPage($_GET['source'])) {
15
 
16
  //Hash-based option block linking
17
  if (window.location.hash) {
18
- var hashes = window.location.hash.split('#');
19
  var hash = hashes[hashes.length - 1];
20
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
21
  if (block) {
15
 
16
  //Hash-based option block linking
17
  if (window.location.hash) {
18
+ var hashes = WFAD.parseHashes();
19
  var hash = hashes[hashes.length - 1];
20
  var block = $('.wf-block[data-persistence-key="' + hash + '"]');
21
  if (block) {
lib/menu_tools_diagnostic.php CHANGED
@@ -17,6 +17,7 @@ if (!isset($sendingDiagnosticEmail)) {
17
  $sendingDiagnosticEmail = false;
18
  }
19
  ?>
 
20
  <script type="application/javascript">
21
  (function($) {
22
  $(function() {
@@ -24,6 +25,7 @@ if (!isset($sendingDiagnosticEmail)) {
24
  });
25
  })(jQuery);
26
  </script>
 
27
  <div id="wf-diagnostics">
28
  <?php if (!$sendingDiagnosticEmail): ?>
29
  <div class="wf-diagnostics-wrapper">
17
  $sendingDiagnosticEmail = false;
18
  }
19
  ?>
20
+ <?php if (!$sendingDiagnosticEmail): ?>
21
  <script type="application/javascript">
22
  (function($) {
23
  $(function() {
25
  });
26
  })(jQuery);
27
  </script>
28
+ <?php endif; ?>
29
  <div id="wf-diagnostics">
30
  <?php if (!$sendingDiagnosticEmail): ?>
31
  <div class="wf-diagnostics-wrapper">
lib/wfAPI.php CHANGED
@@ -27,7 +27,7 @@ class wfAPI {
27
  //Sanity check. Developer should call wfAPI::SSLEnabled() to check if SSL is enabled before forcing SSL and return a user friendly msg if it's not.
28
  if ($forceSSL && (!preg_match('/^https:/i', $apiURL))) {
29
  //User's should never see this message unless we aren't calling SSLEnabled() to check if SSL is enabled before using call() with forceSSL
30
- throw new Exception("SSL is not supported by your web server and is required to use this function. Please ask your hosting provider or site admin to install cURL with openSSL to use this feature.");
31
  }
32
  $json = $this->getURL(rtrim($apiURL, '/') . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&' . self::buildQuery(
33
  array_merge(
@@ -35,7 +35,7 @@ class wfAPI {
35
  $getParams
36
  )), $postParams, $timeout);
37
  if (!$json) {
38
- throw new Exception("We received an empty data response from the Wordfence scanning servers when calling the '$action' function.");
39
  }
40
 
41
  $dat = json_decode($json, true);
@@ -84,10 +84,10 @@ class wfAPI {
84
  }
85
 
86
  if (!is_array($dat)) {
87
- throw new Exception("We received a data structure that is not the expected array when contacting the Wordfence scanning servers and calling the '$action' function.");
88
  }
89
  if (is_array($dat) && isset($dat['errorMsg'])) {
90
- throw new Exception($dat['errorMsg']);
91
  }
92
  return $dat;
93
  }
@@ -118,7 +118,7 @@ class wfAPI {
118
 
119
  if (is_wp_error($response)) {
120
  $error_message = $response->get_error_message();
121
- throw new Exception("There was an " . ($error_message ? '' : 'unknown ') . "error connecting to the Wordfence scanning servers" . ($error_message ? ": $error_message" : '.'));
122
  }
123
 
124
  $dateHeader = @$response['headers']['date'];
@@ -140,7 +140,7 @@ class wfAPI {
140
  }
141
 
142
  if (200 != $this->lastHTTPStatus) {
143
- throw new Exception("The Wordfence scanning servers are currently unavailable. This may be for maintenance or a temporary outage. If this still occurs in an hour, please contact support. [$this->lastHTTPStatus]");
144
  }
145
 
146
  $content = wp_remote_retrieve_body($response);
@@ -193,3 +193,15 @@ class wfAPI {
193
  return wp_http_supports(array('ssl'));
194
  }
195
  }
 
 
 
 
 
 
 
 
 
 
 
 
27
  //Sanity check. Developer should call wfAPI::SSLEnabled() to check if SSL is enabled before forcing SSL and return a user friendly msg if it's not.
28
  if ($forceSSL && (!preg_match('/^https:/i', $apiURL))) {
29
  //User's should never see this message unless we aren't calling SSLEnabled() to check if SSL is enabled before using call() with forceSSL
30
+ throw new wfAPICallSSLUnavailableException("SSL is not supported by your web server and is required to use this function. Please ask your hosting provider or site admin to install cURL with openSSL to use this feature.");
31
  }
32
  $json = $this->getURL(rtrim($apiURL, '/') . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&' . self::buildQuery(
33
  array_merge(
35
  $getParams
36
  )), $postParams, $timeout);
37
  if (!$json) {
38
+ throw new wfAPICallInvalidResponseException("We received an empty data response from the Wordfence scanning servers when calling the '$action' function.");
39
  }
40
 
41
  $dat = json_decode($json, true);
84
  }
85
 
86
  if (!is_array($dat)) {
87
+ throw new wfAPICallInvalidResponseException("We received a data structure that is not the expected array when contacting the Wordfence scanning servers and calling the '$action' function.");
88
  }
89
  if (is_array($dat) && isset($dat['errorMsg'])) {
90
+ throw new wfAPICallErrorResponseException($dat['errorMsg']);
91
  }
92
  return $dat;
93
  }
118
 
119
  if (is_wp_error($response)) {
120
  $error_message = $response->get_error_message();
121
+ throw new wfAPICallFailedException("There was an " . ($error_message ? '' : 'unknown ') . "error connecting to the Wordfence scanning servers" . ($error_message ? ": $error_message" : '.'));
122
  }
123
 
124
  $dateHeader = @$response['headers']['date'];
140
  }
141
 
142
  if (200 != $this->lastHTTPStatus) {
143
+ throw new wfAPICallFailedException("The Wordfence scanning servers are currently unavailable. This may be for maintenance or a temporary outage. If this still occurs in an hour, please contact support. [$this->lastHTTPStatus]");
144
  }
145
 
146
  $content = wp_remote_retrieve_body($response);
193
  return wp_http_supports(array('ssl'));
194
  }
195
  }
196
+
197
+ class wfAPICallSSLUnavailableException extends Exception {
198
+ }
199
+
200
+ class wfAPICallFailedException extends Exception {
201
+ }
202
+
203
+ class wfAPICallInvalidResponseException extends Exception {
204
+ }
205
+
206
+ class wfAPICallErrorResponseException extends Exception {
207
+ }
lib/wfConfig.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
  class wfConfig {
 
 
3
  const AUTOLOAD = 'yes';
4
  const DONT_AUTOLOAD = 'no';
5
 
@@ -290,14 +292,28 @@ class wfConfig {
290
 
291
  return $options;
292
  }
293
- public static function updateTableExists() {
294
- global $wpdb;
295
- self::$tableExists = $wpdb->get_col($wpdb->prepare(<<<SQL
296
- SELECT TABLE_NAME FROM information_schema.TABLES
297
- WHERE TABLE_SCHEMA=DATABASE()
298
- AND TABLE_NAME=%s
299
- SQL
300
- , self::table()));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  }
302
  private static function updateCachedOption($name, $val) {
303
  $options = self::loadAllOptions();
1
  <?php
2
  class wfConfig {
3
+ const TABLE_EXISTS_OPTION = 'wordfence_installed';
4
+
5
  const AUTOLOAD = 'yes';
6
  const DONT_AUTOLOAD = 'no';
7
 
292
 
293
  return $options;
294
  }
295
+
296
+ /**
297
+ * Bases the table's existence on the option specified by wfConfig::TABLE_EXISTS_OPTION for performance. We only
298
+ * set that option just prior to deletion in the uninstall handler and after table creation in the install handler.
299
+ */
300
+ public static function updateTableExists($change = null) {
301
+ if ($change !== null) {
302
+ self::$tableExists = !!$change;
303
+ update_option(wfConfig::TABLE_EXISTS_OPTION, self::$tableExists);
304
+ return;
305
+ }
306
+
307
+ self::$tableExists = true;
308
+ $optionValue = get_option(wfConfig::TABLE_EXISTS_OPTION, null);
309
+ if ($optionValue === null) { //No value, set an initial one
310
+ global $wpdb;
311
+ self::updateTableExists(!!$wpdb->get_col($wpdb->prepare('SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s', self::table())));
312
+ return;
313
+ }
314
+ if (!$optionValue) {
315
+ self::$tableExists = false;
316
+ }
317
  }
318
  private static function updateCachedOption($name, $val) {
319
  $options = self::loadAllOptions();
lib/wfCredentialsController.php CHANGED
@@ -95,6 +95,13 @@ class wfCredentialsController {
95
  delete_transient($key);
96
  }
97
 
 
 
 
 
 
 
 
98
  public static function hasPreviousLoginFromIP($user, $ip) {
99
  global $wpdb;
100
  $table_wfLogins = wfDB::networkTable('wfLogins');
@@ -110,8 +117,19 @@ class wfCredentialsController {
110
  }
111
 
112
  $lastAdminLogin = wfConfig::get_ser('lastAdminLogin');
113
- if (is_array($lastAdminLogin) && isset($lastAdminLogin['userID']) && $lastAdminLogin['userID'] == $id && isset($lastAdminLogin['IP']) && wfUtils::inet_pton($lastAdminLogin['IP']) == wfUtils::inet_pton($ip)) {
114
- return true;
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
 
117
  return false;
95
  delete_transient($key);
96
  }
97
 
98
+ /**
99
+ * Returns whether or not we've seen a successful login from $ip for the given user.
100
+ *
101
+ * @param WP_User $user
102
+ * @param string $ip
103
+ * @return bool
104
+ */
105
  public static function hasPreviousLoginFromIP($user, $ip) {
106
  global $wpdb;
107
  $table_wfLogins = wfDB::networkTable('wfLogins');
117
  }
118
 
119
  $lastAdminLogin = wfConfig::get_ser('lastAdminLogin');
120
+ if (is_array($lastAdminLogin) && isset($lastAdminLogin['userID']) && isset($lastAdminLogin['IP'])) {
121
+ if ($lastAdminLogin['userID'] == $id && wfUtils::inet_pton($lastAdminLogin['IP']) == wfUtils::inet_pton($ip)) {
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+
127
+ //Final check -- if the IP recorded at plugin activation matches, let it through. This is __only__ checked when we don't have any other record of an admin login.
128
+ $activatingIP = wfConfig::get('activatingIP');
129
+ if (wfUtils::isValidIP($activatingIP)) {
130
+ if (wfUtils::inet_pton($activatingIP) == wfUtils::inet_pton($ip)) {
131
+ return true;
132
+ }
133
  }
134
 
135
  return false;
lib/wfDiagnostic.php CHANGED
@@ -377,9 +377,9 @@ class wfDiagnostic
377
  if (!function_exists('openssl_verify') || !defined('OPENSSL_VERSION_NUMBER') || !defined('OPENSSL_VERSION_TEXT')) {
378
  return false;
379
  }
380
- $compare = wfUtils::openssl_version_compare('1.0.1');
381
  return array(
382
- 'test' => $compare < 0,
383
  'message' => OPENSSL_VERSION_TEXT,
384
  );
385
  }
377
  if (!function_exists('openssl_verify') || !defined('OPENSSL_VERSION_NUMBER') || !defined('OPENSSL_VERSION_TEXT')) {
378
  return false;
379
  }
380
+ $compare = wfVersionCheckController::shared()->checkOpenSSLVersion();
381
  return array(
382
+ 'test' => $compare == wfVersionCheckController::VERSION_COMPATIBLE,
383
  'message' => OPENSSL_VERSION_TEXT,
384
  );
385
  }
lib/wfIssues.php CHANGED
@@ -28,6 +28,13 @@ class wfIssues {
28
  const SCAN_FAILED_DURATION_REACHED = 'duration';
29
  const SCAN_FAILED_VERSION_CHANGE = 'versionchange';
30
  const SCAN_FAILED_FORK_FAILED = 'forkfailed';
 
 
 
 
 
 
 
31
 
32
  private $db = false;
33
 
@@ -42,6 +49,10 @@ class wfIssues {
42
  public $totalWarningIssues = 0;
43
  public $totalIgnoredIssues = 0;
44
 
 
 
 
 
45
  public static function statusPrep(){
46
  wfConfig::set_ser('wfStatusStartMsgs', array());
47
  wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
@@ -125,12 +136,22 @@ class wfIssues {
125
  }
126
  }
127
 
 
 
 
 
 
128
  $recordedFailure = wfConfig::get('lastScanFailureType');
129
  switch ($recordedFailure) {
130
  case self::SCAN_FAILED_GENERAL:
131
  case self::SCAN_FAILED_DURATION_REACHED:
132
  case self::SCAN_FAILED_VERSION_CHANGE:
133
  case self::SCAN_FAILED_FORK_FAILED:
 
 
 
 
 
134
  return $recordedFailure;
135
  }
136
 
@@ -437,6 +458,9 @@ class wfIssues {
437
  }
438
  }
439
  $issueList[$i]['issueIDX'] = $i;
 
 
 
440
  }
441
  }
442
  return $ret; //array of lists of issues by status
28
  const SCAN_FAILED_DURATION_REACHED = 'duration';
29
  const SCAN_FAILED_VERSION_CHANGE = 'versionchange';
30
  const SCAN_FAILED_FORK_FAILED = 'forkfailed';
31
+ const SCAN_FAILED_CALLBACK_TEST_FAILED = 'callbackfailed';
32
+ const SCAN_FAILED_START_TIMEOUT = 'starttimeout';
33
+
34
+ const SCAN_FAILED_API_SSL_UNAVAILABLE = 'sslunavailable';
35
+ const SCAN_FAILED_API_CALL_FAILED = 'apifailed';
36
+ const SCAN_FAILED_API_INVALID_RESPONSE = 'apiinvalid';
37
+ const SCAN_FAILED_API_ERROR_RESPONSE = 'apierror';
38
 
39
  private $db = false;
40
 
49
  public $totalWarningIssues = 0;
50
  public $totalIgnoredIssues = 0;
51
 
52
+ public static function validIssueTypes() {
53
+ return array('checkHowGetIPs', 'checkSpamIP', 'commentBadURL', 'configReadable', 'coreUnknown', 'database', 'diskSpace', 'dnsChange', 'easyPassword', 'file', 'geoipSupport', 'knownfile', 'optionBadURL', 'postBadTitle', 'postBadURL', 'publiclyAccessible', 'spamvertizeCheck', 'suspiciousAdminUsers', 'timelimit', 'wfPluginAbandoned', 'wfPluginRemoved', 'wfPluginUpgrade', 'wfPluginVulnerable', 'wfThemeUpgrade', 'wfUpgrade', 'wpscan_directoryList', 'wpscan_fullPathDiscl');
54
+ }
55
+
56
  public static function statusPrep(){
57
  wfConfig::set_ser('wfStatusStartMsgs', array());
58
  wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
136
  }
137
  }
138
 
139
+ $scanStartAttempt = wfConfig::get('scanStartAttempt', 0);
140
+ if ($scanStartAttempt && time() - $scanStartAttempt > WORDFENCE_SCAN_START_FAILURE_THRESHOLD) {
141
+ return self::SCAN_FAILED_START_TIMEOUT;
142
+ }
143
+
144
  $recordedFailure = wfConfig::get('lastScanFailureType');
145
  switch ($recordedFailure) {
146
  case self::SCAN_FAILED_GENERAL:
147
  case self::SCAN_FAILED_DURATION_REACHED:
148
  case self::SCAN_FAILED_VERSION_CHANGE:
149
  case self::SCAN_FAILED_FORK_FAILED:
150
+ case self::SCAN_FAILED_CALLBACK_TEST_FAILED:
151
+ case self::SCAN_FAILED_API_SSL_UNAVAILABLE:
152
+ case self::SCAN_FAILED_API_CALL_FAILED:
153
+ case self::SCAN_FAILED_API_INVALID_RESPONSE:
154
+ case self::SCAN_FAILED_API_ERROR_RESPONSE:
155
  return $recordedFailure;
156
  }
157
 
458
  }
459
  }
460
  $issueList[$i]['issueIDX'] = $i;
461
+ if (isset($issueList[$i]['data']['cType'])) {
462
+ $issueList[$i]['data']['ucType'] = ucwords($issueList[$i]['data']['cType']);
463
+ }
464
  }
465
  }
466
  return $ret; //array of lists of issues by status
lib/wfLog.php CHANGED
@@ -686,16 +686,21 @@ class wfLog {
686
  $this->tagRequestForBlock($reason);
687
 
688
  if (wfConfig::get('alertOn_block')) {
689
- wordfence::alert("Blocking IP {$IP}", "Wordfence has blocked IP address {$IP}.\nThe reason is: \"{$reason}\".", $IP);
 
 
 
 
 
690
  }
691
- wordfence::status(2, 'info', "Blocking IP {$IP}. {$reason}");
692
  }
693
  else if ($action == 'throttle') { //Rate limited - throttle
694
  $secsToGo = wfBlock::rateLimitThrottleDuration();
695
  wfBlock::createRateThrottle($reason, $IP, $secsToGo);
696
  wfActivityReport::logBlockedIP($IP, null, 'throttle');
697
 
698
- wordfence::status(2, 'info', "Throttling IP {$IP}. {$reason}");
699
  wfConfig::inc('totalIPsThrottled');
700
  }
701
  $this->do503($secsToGo, $reason);
686
  $this->tagRequestForBlock($reason);
687
 
688
  if (wfConfig::get('alertOn_block')) {
689
+ $message = sprintf(__('Wordfence has blocked IP address %s.', 'wordfence'), $IP) . "\n";
690
+ $message .= sprintf(__('The reason is: "%s".', 'wordfence'), $reason);
691
+ if ($secsToGo > 0) {
692
+ $message .= "\n" . sprintf(__('The duration of the block is %s.', 'wordfence'), wfUtils::makeDuration($secsToGo, true));
693
+ }
694
+ wordfence::alert(sprintf(__('Blocking IP %s', 'wordfence'), $IP), $message, $IP);
695
  }
696
+ wordfence::status(2, 'info', sprintf(__('Blocking IP %s. %s', 'wordfence'), $IP, $reason));
697
  }
698
  else if ($action == 'throttle') { //Rate limited - throttle
699
  $secsToGo = wfBlock::rateLimitThrottleDuration();
700
  wfBlock::createRateThrottle($reason, $IP, $secsToGo);
701
  wfActivityReport::logBlockedIP($IP, null, 'throttle');
702
 
703
+ wordfence::status(2, 'info', sprintf(__('Throttling IP %s. %s', 'wordfence'), $IP, $reason));
704
  wfConfig::inc('totalIPsThrottled');
705
  }
706
  $this->do503($secsToGo, $reason);
lib/wfScan.php CHANGED
@@ -54,6 +54,7 @@ class wfScan {
54
  }
55
  $scanController = new wfScanner($scanMode);
56
 
 
57
  $isFork = ($_GET['isFork'] == '1' ? true : false);
58
 
59
  if(! $isFork){
@@ -82,85 +83,88 @@ class wfScan {
82
  @error_reporting(E_ALL);
83
  wfUtils::iniSet('display_errors','On');
84
  self::status(4, 'info', "Setting up scanRunning and starting scan");
85
- if($isFork){
86
- $scan = wfConfig::get_ser('wfsd_engine', false, false);
87
- if($scan){
88
- self::status(4, 'info', "Got a true deserialized value back from 'wfsd_engine' with type: " . gettype($scan));
89
- wfConfig::set('wfsd_engine', '', wfConfig::DONT_AUTOLOAD);
90
- } else {
91
- self::status(2, 'error', "Scan can't continue - stored data not found after a fork. Got type: " . gettype($scan));
92
- wfConfig::set('wfsd_engine', '', wfConfig::DONT_AUTOLOAD);
93
- wfConfig::set('lastScanCompleted', __('Scan can\'t continue - stored data not found after a fork.', 'wordfence'));
94
- wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_FORK_FAILED);
95
- wfUtils::clearScanLock();
96
- self::status(2, 'error', "Scan terminated with error: " . __('Scan can\'t continue - stored data not found after a fork.', 'wordfence'));
97
- self::status(10, 'info', "SUM_KILLED:Previous scan terminated with an error. See below.");
98
- exit();
99
- }
100
- } else {
101
- $delay = -1;
102
- $isScheduled = false;
103
- $originalScanStart = wfConfig::get('originalScheduledScanStart', 0);
104
- $lastScanStart = wfConfig::get('lastScheduledScanStart', 0);
105
- $minimumFrequency = ($scanController->schedulingMode() == wfScanner::SCAN_SCHEDULING_MODE_MANUAL ? 1800 : 43200);
106
- if ($lastScanStart && (time() - $lastScanStart) < $minimumFrequency) {
107
- $isScheduled = true;
108
-
109
- if ($originalScanStart > 0) {
110
- $delay = max($lastScanStart - $originalScanStart, 0);
111
  }
112
  }
113
-
114
- wfIssues::statusPrep(); //Re-initializes all status counters
115
- $scanController->resetStages();
116
- $scanController->resetSummaryItems();
117
-
118
- if ($scanMode != wfScanner::SCAN_TYPE_QUICK) {
119
- wordfence::status(1, 'info', "Contacting Wordfence to initiate scan");
120
- $wp_version = wfUtils::getWPVersion();
121
- $apiKey = wfConfig::get('apiKey');
122
- $api = new wfAPI($apiKey, $wp_version);
123
- $response = $api->call('log_scan', array(), array('delay' => $delay, 'scheduled' => (int) $isScheduled, 'mode' => wfConfig::get('schedMode')/*, 'forcedefer' => 1*/));
124
-
125
- if ($scanController->schedulingMode() == wfScanner::SCAN_SCHEDULING_MODE_AUTOMATIC && $isScheduled) {
126
- if (isset($response['defer'])) {
127
- $defer = (int) $response['defer'];
128
- wordfence::status(2, 'info', "Deferring scheduled scan by " . wfUtils::makeDuration($defer));
129
- wfConfig::set('lastScheduledScanStart', 0);
130
- wfConfig::set('lastScanCompleted', 'ok');
131
- wfConfig::set('lastScanFailureType', false);
132
- wfConfig::set_ser('wfStatusStartMsgs', array());
133
- $scanController->recordLastScanTime();
134
- $i = new wfIssues();
135
- wfScanEngine::refreshScanNotification($i);
136
- wfScanner::shared()->scheduleSingleScan(time() + $defer, $originalScanStart);
137
- wfUtils::clearScanLock();
138
- exit();
139
  }
140
  }
141
 
142
- $malwarePrefixesHash = (isset($response['malwarePrefixes']) ? $response['malwarePrefixes'] : '');
143
- $coreHashesHash = (isset($response['coreHashes']) ? $response['coreHashes'] : '');
 
144
 
145
- $scan = new wfScanEngine($malwarePrefixesHash, $coreHashesHash, $scanMode);
146
- $scan->deleteNewIssues();
147
- }
148
- else {
149
- wordfence::status(1, 'info', "Initiating quick scan");
150
- $scan = new wfScanEngine('', '', $scanMode);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
152
- }
153
- try {
154
  $scan->go();
155
  }
156
- catch (wfScanEngineDurationLimitException $e) {
157
  wfUtils::clearScanLock();
158
  $peakMemory = self::logPeakMemory();
159
  self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
160
  self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
161
  exit();
162
  }
163
- catch (wfScanEngineCoreVersionChangeException $e) {
164
  wfUtils::clearScanLock();
165
  $peakMemory = self::logPeakMemory();
166
  self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
@@ -175,7 +179,47 @@ class wfScan {
175
 
176
  exit();
177
  }
178
- catch (Exception $e){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  wfUtils::clearScanLock();
180
  self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
181
  self::status(10, 'info', "SUM_KILLED:Previous scan terminated with an error. See below.");
54
  }
55
  $scanController = new wfScanner($scanMode);
56
 
57
+ wfConfig::remove('scanStartAttempt');
58
  $isFork = ($_GET['isFork'] == '1' ? true : false);
59
 
60
  if(! $isFork){
83
  @error_reporting(E_ALL);
84
  wfUtils::iniSet('display_errors','On');
85
  self::status(4, 'info', "Setting up scanRunning and starting scan");
86
+ try {
87
+ if ($isFork) {
88
+ $scan = wfConfig::get_ser('wfsd_engine', false, false);
89
+ if ($scan) {
90
+ self::status(4, 'info', "Got a true deserialized value back from 'wfsd_engine' with type: " . gettype($scan));
91
+ wfConfig::set('wfsd_engine', '', wfConfig::DONT_AUTOLOAD);
92
+ }
93
+ else {
94
+ self::status(2, 'error', "Scan can't continue - stored data not found after a fork. Got type: " . gettype($scan));
95
+ wfConfig::set('wfsd_engine', '', wfConfig::DONT_AUTOLOAD);
96
+ wfConfig::set('lastScanCompleted', __('Scan can\'t continue - stored data not found after a fork.', 'wordfence'));
97
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_FORK_FAILED);
98
+ wfUtils::clearScanLock();
99
+ self::status(2, 'error', "Scan terminated with error: " . __('Scan can\'t continue - stored data not found after a fork.', 'wordfence'));
100
+ self::status(10, 'info', "SUM_KILLED:Previous scan terminated with an error. See below.");
101
+ exit();
 
 
 
 
 
 
 
 
 
 
102
  }
103
  }
104
+ else {
105
+ $delay = -1;
106
+ $isScheduled = false;
107
+ $originalScanStart = wfConfig::get('originalScheduledScanStart', 0);
108
+ $lastScanStart = wfConfig::get('lastScheduledScanStart', 0);
109
+ $minimumFrequency = ($scanController->schedulingMode() == wfScanner::SCAN_SCHEDULING_MODE_MANUAL ? 1800 : 43200);
110
+ if ($lastScanStart && (time() - $lastScanStart) < $minimumFrequency) {
111
+ $isScheduled = true;
112
+
113
+ if ($originalScanStart > 0) {
114
+ $delay = max($lastScanStart - $originalScanStart, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
  }
117
 
118
+ wfIssues::statusPrep(); //Re-initializes all status counters
119
+ $scanController->resetStages();
120
+ $scanController->resetSummaryItems();
121
 
122
+ if ($scanMode != wfScanner::SCAN_TYPE_QUICK) {
123
+ wordfence::status(1, 'info', "Contacting Wordfence to initiate scan");
124
+ $wp_version = wfUtils::getWPVersion();
125
+ $apiKey = wfConfig::get('apiKey');
126
+ $api = new wfAPI($apiKey, $wp_version);
127
+ $response = $api->call('log_scan', array(), array('delay' => $delay, 'scheduled' => (int) $isScheduled, 'mode' => wfConfig::get('schedMode')/*, 'forcedefer' => 1*/));
128
+
129
+ if ($scanController->schedulingMode() == wfScanner::SCAN_SCHEDULING_MODE_AUTOMATIC && $isScheduled) {
130
+ if (isset($response['defer'])) {
131
+ $defer = (int) $response['defer'];
132
+ wordfence::status(2, 'info', "Deferring scheduled scan by " . wfUtils::makeDuration($defer));
133
+ wfConfig::set('lastScheduledScanStart', 0);
134
+ wfConfig::set('lastScanCompleted', 'ok');
135
+ wfConfig::set('lastScanFailureType', false);
136
+ wfConfig::set_ser('wfStatusStartMsgs', array());
137
+ $scanController->recordLastScanTime();
138
+ $i = new wfIssues();
139
+ wfScanEngine::refreshScanNotification($i);
140
+ wfScanner::shared()->scheduleSingleScan(time() + $defer, $originalScanStart);
141
+ wfUtils::clearScanLock();
142
+ exit();
143
+ }
144
+ }
145
+
146
+ $malwarePrefixesHash = (isset($response['malwarePrefixes']) ? $response['malwarePrefixes'] : '');
147
+ $coreHashesHash = (isset($response['coreHashes']) ? $response['coreHashes'] : '');
148
+
149
+ $scan = new wfScanEngine($malwarePrefixesHash, $coreHashesHash, $scanMode);
150
+ $scan->deleteNewIssues();
151
+ }
152
+ else {
153
+ wordfence::status(1, 'info', "Initiating quick scan");
154
+ $scan = new wfScanEngine('', '', $scanMode);
155
+ }
156
  }
157
+
 
158
  $scan->go();
159
  }
160
+ catch (wfScanEngineDurationLimitException $e) { //User error set in wfScanEngine
161
  wfUtils::clearScanLock();
162
  $peakMemory = self::logPeakMemory();
163
  self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
164
  self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
165
  exit();
166
  }
167
+ catch (wfScanEngineCoreVersionChangeException $e) { //User error set in wfScanEngine
168
  wfUtils::clearScanLock();
169
  $peakMemory = self::logPeakMemory();
170
  self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
179
 
180
  exit();
181
  }
182
+ catch (wfAPICallSSLUnavailableException $e) {
183
+ wfConfig::set('lastScanCompleted', $e->getMessage());
184
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_API_SSL_UNAVAILABLE);
185
+
186
+ wfUtils::clearScanLock();
187
+ $peakMemory = self::logPeakMemory();
188
+ self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
189
+ self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
190
+ exit();
191
+ }
192
+ catch (wfAPICallFailedException $e) {
193
+ wfConfig::set('lastScanCompleted', $e->getMessage());
194
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_API_CALL_FAILED);
195
+
196
+ wfUtils::clearScanLock();
197
+ $peakMemory = self::logPeakMemory();
198
+ self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
199
+ self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
200
+ exit();
201
+ }
202
+ catch (wfAPICallInvalidResponseException $e) {
203
+ wfConfig::set('lastScanCompleted', $e->getMessage());
204
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_API_INVALID_RESPONSE);
205
+
206
+ wfUtils::clearScanLock();
207
+ $peakMemory = self::logPeakMemory();
208
+ self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
209
+ self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
210
+ exit();
211
+ }
212
+ catch (wfAPICallErrorResponseException $e) {
213
+ wfConfig::set('lastScanCompleted', $e->getMessage());
214
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_API_ERROR_RESPONSE);
215
+
216
+ wfUtils::clearScanLock();
217
+ $peakMemory = self::logPeakMemory();
218
+ self::status(2, 'info', "Wordfence used " . wfUtils::formatBytes($peakMemory - self::$peakMemAtStart) . " of memory for scan. Server peak memory usage was: " . wfUtils::formatBytes($peakMemory));
219
+ self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
220
+ exit();
221
+ }
222
+ catch (Exception $e) {
223
  wfUtils::clearScanLock();
224
  self::status(2, 'error', "Scan terminated with error: " . $e->getMessage());
225
  self::status(10, 'info', "SUM_KILLED:Previous scan terminated with an error. See below.");
lib/wfScanEngine.php CHANGED
@@ -230,6 +230,19 @@ class wfScanEngine {
230
  wfScanEngine::refreshScanNotification($this->i);
231
  throw $e;
232
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  catch (Exception $e) {
234
  if ($e->getCode() != wfScanEngine::SCAN_MANUALLY_KILLED) {
235
  wfConfig::set('lastScanCompleted', $e->getMessage());
@@ -1950,43 +1963,82 @@ class wfScanEngine {
1950
  }
1951
  $timeout = self::getMaxExecutionTime() - 2; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied
1952
  $testURL = admin_url('admin-ajax.php?action=wordfence_testAjax');
1953
- if(! wfConfig::get('startScansRemotely', false)){
1954
- $testResult = wp_remote_post($testURL, array(
1955
- 'timeout' => $timeout,
1956
- 'blocking' => true,
1957
- 'sslverify' => false,
1958
- 'headers' => array()
 
1959
  ));
 
 
 
 
 
1960
  wordfence::status(4, 'info', "Test result of scan start URL fetch: " . var_export($testResult, true));
1961
  }
 
1962
  $cronKey = wfUtils::bigRandomHex();
1963
  wfConfig::set('currentCronKey', time() . ',' . $cronKey);
1964
- if( (! wfConfig::get('startScansRemotely', false)) && (! is_wp_error($testResult)) && (is_array($testResult) || $testResult instanceof ArrayAccess) && strstr($testResult['body'], 'WFSCANTESTOK') !== false){
1965
  //ajax requests can be sent by the server to itself
1966
  $cronURL = 'admin-ajax.php?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&scanMode=' . $scanMode . '&cronKey=' . $cronKey;
1967
  $cronURL = admin_url($cronURL);
1968
  $headers = array('Referer' => false/*, 'Cookie' => 'XDEBUG_SESSION=1'*/);
1969
  wordfence::status(4, 'info', "Starting cron with normal ajax at URL $cronURL");
1970
- wp_remote_get( $cronURL, array(
1971
- 'timeout' => 0.01,
1972
- 'blocking' => false,
1973
- 'sslverify' => false,
1974
- 'headers' => $headers
1975
- ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1976
  wordfence::status(4, 'info', "Scan process ended after forking.");
1977
- } else {
 
1978
  $cronURL = admin_url('admin-ajax.php');
1979
  $cronURL = preg_replace('/^(https?:\/\/)/i', '$1noc1.wordfence.com/scanp/', $cronURL);
1980
  $cronURL .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&scanMode=' . $scanMode . '&cronKey=' . $cronKey;
1981
  $headers = array();
1982
  wordfence::status(4, 'info', "Starting cron via proxy at URL $cronURL");
1983
-
1984
- wp_remote_get( $cronURL, array(
1985
- 'timeout' => 0.01,
1986
- 'blocking' => false,
1987
- 'sslverify' => false,
1988
- 'headers' => $headers
1989
- ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1990
  wordfence::status(4, 'info', "Scan process ended after forking.");
1991
  }
1992
  return false; //No error
@@ -2442,3 +2494,5 @@ class wfScanEngineDurationLimitException extends Exception {
2442
 
2443
  class wfScanEngineCoreVersionChangeException extends Exception {
2444
  }
 
 
230
  wfScanEngine::refreshScanNotification($this->i);
231
  throw $e;
232
  }
233
+ catch (wfScanEngineTestCallbackFailedException $e) {
234
+ wfConfig::set('lastScanCompleted', $e->getMessage());
235
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
236
+ $this->scanController->recordLastScanTime();
237
+
238
+ $this->recordMetric('scan', 'duration', (time() - $this->startTime));
239
+ $this->recordMetric('scan', 'memory', wfConfig::get('wfPeakMemory', 0, false));
240
+ $this->recordMetric('scan', 'failure', $e->getMessage());
241
+ $this->submitMetrics();
242
+
243
+ wfScanEngine::refreshScanNotification($this->i);
244
+ throw $e;
245
+ }
246
  catch (Exception $e) {
247
  if ($e->getCode() != wfScanEngine::SCAN_MANUALLY_KILLED) {
248
  wfConfig::set('lastScanCompleted', $e->getMessage());
1963
  }
1964
  $timeout = self::getMaxExecutionTime() - 2; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied
1965
  $testURL = admin_url('admin-ajax.php?action=wordfence_testAjax');
1966
+ if (!wfConfig::get('startScansRemotely', false)) {
1967
+ try {
1968
+ $testResult = wp_remote_post($testURL, array(
1969
+ 'timeout' => $timeout,
1970
+ 'blocking' => true,
1971
+ 'sslverify' => false,
1972
+ 'headers' => array()
1973
  ));
1974
+ }
1975
+ catch (Exception $e) {
1976
+ //Fall through to the remote start test below
1977
+ }
1978
+
1979
  wordfence::status(4, 'info', "Test result of scan start URL fetch: " . var_export($testResult, true));
1980
  }
1981
+
1982
  $cronKey = wfUtils::bigRandomHex();
1983
  wfConfig::set('currentCronKey', time() . ',' . $cronKey);
1984
+ if ((!wfConfig::get('startScansRemotely', false)) && (!is_wp_error($testResult)) && (is_array($testResult) || $testResult instanceof ArrayAccess) && strstr($testResult['body'], 'WFSCANTESTOK') !== false) {
1985
  //ajax requests can be sent by the server to itself
1986
  $cronURL = 'admin-ajax.php?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&scanMode=' . $scanMode . '&cronKey=' . $cronKey;
1987
  $cronURL = admin_url($cronURL);
1988
  $headers = array('Referer' => false/*, 'Cookie' => 'XDEBUG_SESSION=1'*/);
1989
  wordfence::status(4, 'info', "Starting cron with normal ajax at URL $cronURL");
1990
+
1991
+ try {
1992
+ wfConfig::set('scanStartAttempt', time());
1993
+ $response = wp_remote_get($cronURL, array(
1994
+ 'timeout' => 0.01,
1995
+ 'blocking' => false,
1996
+ 'sslverify' => false,
1997
+ 'headers' => $headers
1998
+ ));
1999
+ }
2000
+ catch (Exception $e) {
2001
+ wfConfig::set('lastScanCompleted', $e->getMessage());
2002
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
2003
+ return false;
2004
+ }
2005
+
2006
+ if (is_wp_error($response)) {
2007
+ $error_message = $response->get_error_message();
2008
+ wfConfig::set('lastScanCompleted', "There was an " . ($error_message ? '' : 'unknown ') . "error starting the scan" . ($error_message ? ": $error_message" : '.'));
2009
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
2010
+ }
2011
+
2012
  wordfence::status(4, 'info', "Scan process ended after forking.");
2013
+ }
2014
+ else {
2015
  $cronURL = admin_url('admin-ajax.php');
2016
  $cronURL = preg_replace('/^(https?:\/\/)/i', '$1noc1.wordfence.com/scanp/', $cronURL);
2017
  $cronURL .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&scanMode=' . $scanMode . '&cronKey=' . $cronKey;
2018
  $headers = array();
2019
  wordfence::status(4, 'info', "Starting cron via proxy at URL $cronURL");
2020
+
2021
+ try {
2022
+ wfConfig::set('scanStartAttempt', time());
2023
+ $response = wp_remote_get($cronURL, array(
2024
+ 'timeout' => 0.01,
2025
+ 'blocking' => false,
2026
+ 'sslverify' => false,
2027
+ 'headers' => $headers
2028
+ ));
2029
+ }
2030
+ catch (Exception $e) {
2031
+ wfConfig::set('lastScanCompleted', $e->getMessage());
2032
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
2033
+ return false;
2034
+ }
2035
+
2036
+ if (is_wp_error($response)) {
2037
+ $error_message = $response->get_error_message();
2038
+ wfConfig::set('lastScanCompleted', "There was an " . ($error_message ? '' : 'unknown ') . "error starting the scan" . ($error_message ? ": $error_message" : '.'));
2039
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
2040
+ }
2041
+
2042
  wordfence::status(4, 'info', "Scan process ended after forking.");
2043
  }
2044
  return false; //No error
2494
 
2495
  class wfScanEngineCoreVersionChangeException extends Exception {
2496
  }
2497
+ class wfScanEngineTestCallbackFailedException extends Exception {
2498
+ }
lib/wfSupportController.php CHANGED
@@ -17,6 +17,10 @@ class wfSupportController {
17
 
18
  const ITEM_WIDGET_LOCAL_ATTACKS = 'widget-local-attacks';
19
 
 
 
 
 
20
  const ITEM_DASHBOARD = 'dashboard';
21
  const ITEM_DASHBOARD_STATUS_FIREWALL = 'dashboard-status-firewall';
22
  const ITEM_DASHBOARD_STATUS_SCAN = 'dashboard-status-scan';
@@ -125,6 +129,7 @@ class wfSupportController {
125
  const ITEM_SCAN_OPTION_CUSTOM_MALWARE_SIGNATURES = 'scan-option-custom-malware-signatures';
126
  const ITEM_SCAN_TIME_LIMIT = 'scan-time-limit';
127
  const ITEM_SCAN_FAILS = 'scan-fails';
 
128
  const ITEM_SCAN_BULK_DELETE_WARNING = 'scan-bulk-delete-warning';
129
  const ITEM_SCAN_SCHEDULING = 'scan-scheduling';
130
  const ITEM_SCAN_RESULT_PUBLIC_CONFIG = 'scan-result-public-config';
@@ -177,6 +182,10 @@ class wfSupportController {
177
 
178
  case self::ITEM_WIDGET_LOCAL_ATTACKS:
179
 
 
 
 
 
180
  case self::ITEM_DASHBOARD:
181
  case self::ITEM_DASHBOARD_STATUS_FIREWALL:
182
  case self::ITEM_DASHBOARD_STATUS_SCAN:
@@ -254,6 +263,7 @@ class wfSupportController {
254
  case self::ITEM_SCAN_STATUS_REPUTATION:
255
  case self::ITEM_SCAN_TIME_LIMIT:
256
  case self::ITEM_SCAN_FAILS:
 
257
  case self::ITEM_SCAN_BULK_DELETE_WARNING:
258
  case self::ITEM_SCAN_SCHEDULING:
259
  case self::ITEM_SCAN_OPTION_CHECK_SITE_BLACKLISTED:
17
 
18
  const ITEM_WIDGET_LOCAL_ATTACKS = 'widget-local-attacks';
19
 
20
+ const ITEM_VERSION_WORDPRESS = 'version-wordpress';
21
+ const ITEM_VERSION_PHP = 'version-php';
22
+ const ITEM_VERSION_OPENSSL = 'version-ssl';
23
+
24
  const ITEM_DASHBOARD = 'dashboard';
25
  const ITEM_DASHBOARD_STATUS_FIREWALL = 'dashboard-status-firewall';
26
  const ITEM_DASHBOARD_STATUS_SCAN = 'dashboard-status-scan';
129
  const ITEM_SCAN_OPTION_CUSTOM_MALWARE_SIGNATURES = 'scan-option-custom-malware-signatures';
130
  const ITEM_SCAN_TIME_LIMIT = 'scan-time-limit';
131
  const ITEM_SCAN_FAILS = 'scan-fails';
132
+ const ITEM_SCAN_FAILED_START = 'scan-failed-start';
133
  const ITEM_SCAN_BULK_DELETE_WARNING = 'scan-bulk-delete-warning';
134
  const ITEM_SCAN_SCHEDULING = 'scan-scheduling';
135
  const ITEM_SCAN_RESULT_PUBLIC_CONFIG = 'scan-result-public-config';
182
 
183
  case self::ITEM_WIDGET_LOCAL_ATTACKS:
184
 
185
+ case self::ITEM_VERSION_WORDPRESS:
186
+ case self::ITEM_VERSION_PHP:
187
+ case self::ITEM_VERSION_OPENSSL:
188
+
189
  case self::ITEM_DASHBOARD:
190
  case self::ITEM_DASHBOARD_STATUS_FIREWALL:
191
  case self::ITEM_DASHBOARD_STATUS_SCAN:
263
  case self::ITEM_SCAN_STATUS_REPUTATION:
264
  case self::ITEM_SCAN_TIME_LIMIT:
265
  case self::ITEM_SCAN_FAILS:
266
+ case self::ITEM_SCAN_FAILED_START:
267
  case self::ITEM_SCAN_BULK_DELETE_WARNING:
268
  case self::ITEM_SCAN_SCHEDULING:
269
  case self::ITEM_SCAN_OPTION_CHECK_SITE_BLACKLISTED:
lib/wfUtils.php CHANGED
@@ -2411,45 +2411,6 @@ class wfUtils {
2411
  }
2412
  return new DateTime($timestring);
2413
  }
2414
-
2415
- /**
2416
- * Returns whether or not the OpenSSL version is before, after, or equal to the equivalent text version string.
2417
- *
2418
- * @param string $compareVersion
2419
- * @param int $openSSLVersion A version number in the format OpenSSL uses.
2420
- * @return bool|int Returns -1 if $compareVersion is earlier, 0 if equal, 1 if later, and false if not a valid version string.
2421
- */
2422
- public static function openssl_version_compare($compareVersion, $openSSLVersion = OPENSSL_VERSION_NUMBER) {
2423
- if (preg_match('/^(\d+)\.(\d+)\.(\d+)([a-z]?)/i', $compareVersion, $matches)) {
2424
- $primary = 0; $major = 0; $minor = 0; $fixLetterIndex = 0;
2425
- if (isset($matches[1])) { $primary = (int) $matches[1]; }
2426
- if (isset($matches[2])) { $major = (int) $matches[2]; }
2427
- if (isset($matches[3])) { $minor = (int) $matches[3]; }
2428
- if (isset($matches[4])) { $fixLetterIndex = strpos('abcdefghijklmnopqrstuvwxyz', strtolower($matches[1])) + 1; }
2429
-
2430
- $compareOpenSSLVersion = self::openssl_make_version($primary, $major, $minor, $fixLetterIndex, 0);
2431
- if ($compareOpenSSLVersion < $openSSLVersion) { return -1; }
2432
- else if ($compareOpenSSLVersion == $openSSLVersion) { return 0; }
2433
- return 1;
2434
- }
2435
-
2436
- return false;
2437
- }
2438
-
2439
- /**
2440
- * Builds a number that can be compared to OPENSSL_VERSION_NUMBER from the parameters given. This is a modified
2441
- * version of the macro in the OpenSSL source.
2442
- *
2443
- * @param int $primary The '1' in 1.0.2g.
2444
- * @param int $major The '0' in 1.0.2g.
2445
- * @param int $minor The '2' in 1.0.2g.
2446
- * @param int $fixLetterIndex The 'g' in 1.0.2g.
2447
- * @param int $patch
2448
- * @return int
2449
- */
2450
- public static function openssl_make_version($primary, $major, $minor, $fixLetterIndex = 0, $patch = 0) {
2451
- return ((($primary & 0xff) << 28) + (($major & 0xff) << 20) + (($minor & 0xff) << 12) + (($fixLetterIndex & 0xff) << 4) + $patch);
2452
- }
2453
  }
2454
 
2455
  // GeoIP lib uses these as well
2411
  }
2412
  return new DateTime($timestring);
2413
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2414
  }
2415
 
2416
  // GeoIP lib uses these as well
lib/wfVersionCheckController.php ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class wfVersionCheckController {
4
+ const VERSION_COMPATIBLE = 'compatible';
5
+ const VERSION_DEPRECATED = 'deprecated';
6
+ const VERSION_UNSUPPORTED = 'unsupported';
7
+
8
+ const PHP_DEPRECATING = '5.3.0'; //When greater than PHP_MINIMUM, will issue a discontinuing warning the first time we check it and find a version less than this (also applies to the other similar constant pairs)
9
+ const PHP_MINIMUM = '5.2.0'; //The currently supported minimum
10
+
11
+ const OPENSSL_DEPRECATING = '1.0.1';
12
+ const OPENSSL_MINIMUM = '1.0.1';
13
+
14
+ const WORDPRESS_DEPRECATING = '3.9.0';
15
+ const WORDPRESS_MINIMUM = '3.9.0';
16
+
17
+ public static function shared() {
18
+ static $_shared = false;
19
+ if ($_shared === false) {
20
+ $_shared = new wfVersionCheckController();
21
+ }
22
+ return $_shared;
23
+ }
24
+
25
+ /**
26
+ * Returns whether or not all version checks are successful. If any check returns a value other than VERSION_COMPATIBLE, this returns false.
27
+ *
28
+ * @return bool
29
+ */
30
+ public function checkVersions() {
31
+ return ($this->checkPHPVersion() == self::VERSION_COMPATIBLE) && ($this->checkOpenSSLVersion() == self::VERSION_COMPATIBLE) && ($this->checkWordPressVersion() == self::VERSION_COMPATIBLE);
32
+ }
33
+
34
+ /**
35
+ * Does the same thing as checkVersions but also triggers display of the corresponding warnings.
36
+ *
37
+ * @return bool
38
+ */
39
+ public function checkVersionsAndWarn() {
40
+ //PHP
41
+ $php = $this->checkPHPVersion();
42
+ if ($php == self::VERSION_DEPRECATED) {
43
+ $this->_alertEmail(
44
+ 'phpVersionCheckDeprecationEmail_' . self::PHP_DEPRECATING,
45
+ __('PHP version too old', 'wordfence'),
46
+ sprintf(__('Your site is using a PHP version (%s) that will no longer be supported by Wordfence in an upcoming release and needs to be updated. We recommend using the newest version of PHP 7.x or 5.6 but will currently support PHP versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), phpversion(), self::PHP_DEPRECATING) . ' ' . sprintf(__('Learn More: %s', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_PHP))
47
+ );
48
+
49
+ $this->_adminNotice(
50
+ 'phpVersionCheckDeprecationNotice_' . self::PHP_DEPRECATING,
51
+ 'phpVersionCheck',
52
+ sprintf(__('<strong>WARNING: </strong> Your site is using a PHP version (%s) that will no longer be supported by Wordfence in an upcoming release and needs to be updated. We recommend using the newest version of PHP 7.x or 5.6 but will currently support PHP versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), phpversion(), self::PHP_DEPRECATING) . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_PHP) . '" target="_blank" rel="noopener noreferrer">' . __('Learn More', 'wordfence') . '</a>'
53
+ );
54
+
55
+ return false;
56
+ }
57
+ else if ($php == self::VERSION_UNSUPPORTED) {
58
+ $this->_alertEmail(
59
+ 'phpVersionCheckUnsupportedEmail_' . self::PHP_MINIMUM,
60
+ __('PHP version too old', 'wordfence'),
61
+ sprintf(__('Your site is using a PHP version (%s) that is no longer supported by Wordfence and needs to be updated. We recommend using the newest version of PHP 7.x or 5.6 but will currently support PHP versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), phpversion(), self::PHP_DEPRECATING) . ' ' . sprintf(__('Learn More: %s', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_PHP))
62
+ );
63
+
64
+ $this->_adminNotice(
65
+ 'phpVersionCheckUnsupportedNotice_' . self::PHP_MINIMUM,
66
+ 'phpVersionCheck',
67
+ sprintf(__('<strong>WARNING: </strong> Your site is using a PHP version (%s) that is no longer supported by Wordfence and needs to be updated. We recommend using the newest version of PHP 7.x or 5.6 but will currently support PHP versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), phpversion(), self::PHP_DEPRECATING) . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_PHP) . '" target="_blank" rel="noopener noreferrer">' . __('Learn More', 'wordfence') . '</a>'
68
+ );
69
+
70
+ return false;
71
+ }
72
+ else {
73
+ wfAdminNoticeQueue::removeAdminNotice(false, 'phpVersionCheck');
74
+ }
75
+
76
+ //OpenSSL
77
+ $openssl = $this->checkOpenSSLVersion();
78
+ if ($openssl == self::VERSION_DEPRECATED) {
79
+ $this->_alertEmail(
80
+ 'opensslVersionCheckDeprecationEmail_' . self::OPENSSL_DEPRECATING,
81
+ __('OpenSSL version too old', 'wordfence'),
82
+ sprintf(__('Your site is using an OpenSSL version (%s) that will no longer be supported by Wordfence in an upcoming release and needs to be updated. We recommend using the newest version of OpenSSL but will currently support OpenSSL versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), self::openssl_make_text_version(), self::OPENSSL_DEPRECATING) . ' ' . sprintf(__('Learn More: %s', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_OPENSSL))
83
+ );
84
+
85
+ $this->_adminNotice(
86
+ 'opensslVersionCheckDeprecationNotice_' . self::OPENSSL_DEPRECATING,
87
+ 'opensslVersionCheck',
88
+ sprintf(__('<strong>WARNING: </strong> Your site is using an OpenSSL version (%s) that will no longer be supported by Wordfence in an upcoming release and needs to be updated. We recommend using the newest version of OpenSSL but will currently support OpenSSL versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), self::openssl_make_text_version(), self::OPENSSL_DEPRECATING) . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_OPENSSL) . '" target="_blank" rel="noopener noreferrer">' . __('Learn More', 'wordfence') . '</a>'
89
+ );
90
+
91
+ return false;
92
+ }
93
+ else if ($openssl == self::VERSION_UNSUPPORTED) {
94
+ $this->_alertEmail(
95
+ 'opensslVersionCheckUnsupportedEmail_' . self::PHP_MINIMUM,
96
+ __('OpenSSL version too old', 'wordfence'),
97
+ sprintf(__('Your site is using an OpenSSL version (%s) that is no longer supported by Wordfence and needs to be updated. We recommend using the newest version of OpenSSL but will currently support OpenSSL versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), self::openssl_make_text_version(), self::OPENSSL_DEPRECATING) . ' ' . sprintf(__('Learn More: %s', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_OPENSSL))
98
+ );
99
+
100
+ $this->_adminNotice(
101
+ 'opensslVersionCheckUnsupportedNotice_' . self::PHP_MINIMUM,
102
+ 'opensslVersionCheck',
103
+ sprintf(__('<strong>WARNING: </strong> Your site is using an OpenSSL version (%s) that is no longer supported by Wordfence and needs to be updated. We recommend using the newest version of OpenSSL but will currently support OpenSSL versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), self::openssl_make_text_version(), self::OPENSSL_DEPRECATING) . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_OPENSSL) . '" target="_blank" rel="noopener noreferrer">' . __('Learn More', 'wordfence') . '</a>'
104
+ );
105
+
106
+ return false;
107
+ }
108
+ else {
109
+ wfAdminNoticeQueue::removeAdminNotice(false, 'opensslVersionCheck');
110
+ }
111
+
112
+ //WordPress
113
+ $wordpress = $this->checkWordPressVersion();
114
+ if ($wordpress == self::VERSION_DEPRECATED) {
115
+ require(ABSPATH . 'wp-includes/version.php'); /** @var string $wp_version */
116
+
117
+ $this->_alertEmail(
118
+ 'wordpressVersionCheckDeprecationEmail_' . self::WORDPRESS_DEPRECATING,
119
+ __('WordPress version too old', 'wordfence'),
120
+ sprintf(__('Your site is using a WordPress version (%s) that will no longer be supported by Wordfence in an upcoming release and needs to be updated. We recommend using the newest version of WordPress but will currently support WordPress versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), $wp_version, self::WORDPRESS_DEPRECATING) . ' ' . sprintf(__('Learn More: %s', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_WORDPRESS))
121
+ );
122
+
123
+ $this->_adminNotice(
124
+ 'wordpressVersionCheckDeprecationNotice_' . self::WORDPRESS_DEPRECATING,
125
+ 'wordpressVersionCheck',
126
+ sprintf(__('<strong>WARNING: </strong> Your site is using a WordPress version (%s) that will no longer be supported by Wordfence in an upcoming release and needs to be updated. We recommend using the newest version of WordPress but will currently support WordPress versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), $wp_version, self::WORDPRESS_DEPRECATING) . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_WORDPRESS) . '" target="_blank" rel="noopener noreferrer">' . __('Learn More', 'wordfence') . '</a>'
127
+ );
128
+
129
+ return false;
130
+ }
131
+ else if ($wordpress == self::VERSION_UNSUPPORTED) {
132
+ require(ABSPATH . 'wp-includes/version.php'); /** @var string $wp_version */
133
+
134
+ $this->_alertEmail(
135
+ 'wordpressVersionCheckUnsupportedEmail_' . self::WORDPRESS_MINIMUM,
136
+ __('WordPress version too old', 'wordfence'),
137
+ sprintf(__('Your site is using a WordPress version (%s) that is no longer supported by Wordfence and needs to be updated. We recommend using the newest version of WordPress but will currently support WordPress versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), $wp_version, self::WORDPRESS_DEPRECATING) . ' ' . sprintf(__('Learn More: %s', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_WORDPRESS))
138
+ );
139
+
140
+ $this->_adminNotice(
141
+ 'wordpressVersionCheckUnsupportedNotice_' . self::WORDPRESS_MINIMUM,
142
+ 'wordpressVersionCheck',
143
+ sprintf(__('<strong>WARNING: </strong> Your site is using a WordPress version (%s) that is no longer supported by Wordfence and needs to be updated. We recommend using the newest version of WordPress but will currently support WordPress versions as old as %s. Version checks are run regularly, so if you have successfully updated, you can dismiss this notice or check that the update has taken effect later.', 'wordfence'), $wp_version, self::WORDPRESS_DEPRECATING) . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_VERSION_WORDPRESS) . '" target="_blank" rel="noopener noreferrer">' . __('Learn More', 'wordfence') . '</a>'
144
+ );
145
+
146
+ return false;
147
+ }
148
+ else {
149
+ wfAdminNoticeQueue::removeAdminNotice(false, 'wordpressVersionCheck');
150
+ }
151
+
152
+ return true;
153
+ }
154
+
155
+ private function _alertEmail($checkKey, $title, $body) {
156
+ if (!wfConfig::get($checkKey)) {
157
+ wordfence::alert($title, $body, wfUtils::getIP());
158
+ wfConfig::set($checkKey, true);
159
+ }
160
+ }
161
+
162
+ private function _adminNotice($checkKey, $noticeKey, $message) {
163
+ if (!wfConfig::get($checkKey)) {
164
+ wfAdminNoticeQueue::addAdminNotice(wfAdminNotice::SEVERITY_CRITICAL, $message, $noticeKey);
165
+ wfConfig::set($checkKey, true);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Returns whether or not the PHP version meets our minimum requirement or is a version being deprecated.
171
+ *
172
+ * @return string One of the VERSION_ constants.
173
+ */
174
+ public function checkPHPVersion() {
175
+ if (version_compare(phpversion(), self::PHP_DEPRECATING, '>=')) {
176
+ return self::VERSION_COMPATIBLE;
177
+ }
178
+
179
+ if (self::PHP_DEPRECATING != self::PHP_MINIMUM && version_compare(phpversion(), self::PHP_MINIMUM, '>=')) {
180
+ return self::VERSION_DEPRECATED;
181
+ }
182
+
183
+ return self::VERSION_UNSUPPORTED;
184
+ }
185
+
186
+ /**
187
+ * Returns whether or not the OpenSSL version meets our minimum requirement or is a version being deprecated.
188
+ *
189
+ * @return string One of the VERSION_ constants.
190
+ */
191
+ public function checkOpenSSLVersion() {
192
+ if (self::openssl_version_compare(self::OPENSSL_DEPRECATING) <= 0) {
193
+ return self::VERSION_COMPATIBLE;
194
+ }
195
+
196
+ if (self::OPENSSL_DEPRECATING != self::OPENSSL_MINIMUM && self::openssl_version_compare(self::OPENSSL_MINIMUM) <= 0) {
197
+ return self::VERSION_DEPRECATED;
198
+ }
199
+
200
+ return self::VERSION_UNSUPPORTED;
201
+ }
202
+
203
+ /**
204
+ * Returns whether or not the WordPress version meets our minimum requirement or is a version being deprecated.
205
+ *
206
+ * @return string One of the VERSION_ constants.
207
+ */
208
+ public function checkWordPressVersion() {
209
+ require(ABSPATH . 'wp-includes/version.php'); /** @var string $wp_version */
210
+
211
+ if (version_compare($wp_version, self::WORDPRESS_DEPRECATING, '>=')) {
212
+ return self::VERSION_COMPATIBLE;
213
+ }
214
+
215
+ if (self::WORDPRESS_DEPRECATING != self::WORDPRESS_MINIMUM && version_compare($wp_version, self::WORDPRESS_MINIMUM, '>=')) {
216
+ return self::VERSION_DEPRECATED;
217
+ }
218
+
219
+ return self::VERSION_UNSUPPORTED;
220
+ }
221
+
222
+ /**
223
+ * Utility Functions
224
+ */
225
+
226
+ /**
227
+ * Returns whether or not the OpenSSL version is before, after, or equal to the equivalent text version string.
228
+ *
229
+ * @param string $compareVersion
230
+ * @param int $openSSLVersion A version number in the format OpenSSL uses.
231
+ * @return bool|int Returns -1 if $compareVersion is earlier, 0 if equal, 1 if later, and false if not a valid version string.
232
+ */
233
+ public static function openssl_version_compare($compareVersion, $openSSLVersion = OPENSSL_VERSION_NUMBER) {
234
+ if (preg_match('/^(\d+)\.(\d+)\.(\d+)([a-z]?)/i', $compareVersion, $matches)) {
235
+ $primary = 0; $major = 0; $minor = 0; $fixLetterIndex = 0;
236
+ if (isset($matches[1])) { $primary = (int) $matches[1]; }
237
+ if (isset($matches[2])) { $major = (int) $matches[2]; }
238
+ if (isset($matches[3])) { $minor = (int) $matches[3]; }
239
+ if (isset($matches[4])) { $fixLetterIndex = strpos('abcdefghijklmnopqrstuvwxyz', strtolower($matches[1])) + 1; }
240
+
241
+ $compareOpenSSLVersion = self::openssl_make_number_version($primary, $major, $minor, $fixLetterIndex, 0);
242
+ if ($compareOpenSSLVersion < $openSSLVersion) { return -1; }
243
+ else if ($compareOpenSSLVersion == $openSSLVersion) { return 0; }
244
+ return 1;
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+ /**
251
+ * Builds a number that can be compared to OPENSSL_VERSION_NUMBER from the parameters given. This is a modified
252
+ * version of the macro in the OpenSSL source.
253
+ *
254
+ * @param int $primary The '1' in 1.0.2g.
255
+ * @param int $major The '0' in 1.0.2g.
256
+ * @param int $minor The '2' in 1.0.2g.
257
+ * @param int $fixLetterIndex The 'g' in 1.0.2g.
258
+ * @param int $patch
259
+ * @return int
260
+ */
261
+ public static function openssl_make_number_version($primary, $major, $minor, $fixLetterIndex = 0, $patch = 0) {
262
+ return ((($primary & 0xff) << 28) + (($major & 0xff) << 20) + (($minor & 0xff) << 12) + (($fixLetterIndex & 0xff) << 4) + $patch);
263
+ }
264
+
265
+ /**
266
+ * Builds a text version of the OpenSSL version from a number-formatted one.
267
+ *
268
+ * @param int $number
269
+ * @return string
270
+ */
271
+ public static function openssl_make_text_version($number = OPENSSL_VERSION_NUMBER) {
272
+ $primary = (($number >> 28) & 0xff);
273
+ $major = (($number >> 20) & 0xff);
274
+ $minor = (($number >> 12) & 0xff);
275
+ $fix = (($number >> 4) & 0xff);
276
+ $patch = ($number & 0xf); //Not currently handled -- would be values like alpha, beta, rc1
277
+
278
+ $alphabet = str_split('abcdefghijklmnopqrstuvwxyz');
279
+ $fixLetters = '';
280
+ while ($fix > 26) {
281
+ $fixLetters .= 'z';
282
+ $fix -= 26;
283
+ }
284
+ if (array_key_exists($fix - 1, $alphabet)) {
285
+ $fixLetters .= $alphabet[$fix - 1];
286
+ }
287
+
288
+ $version = "{$primary}.{$major}.{$minor}{$fixLetters}";
289
+ return $version;
290
+ }
291
+ }
lib/wordfenceClass.php CHANGED
@@ -36,6 +36,7 @@ require_once(dirname(__FILE__) . '/wfImportExportController.php');
36
  require_once(dirname(__FILE__) . '/wfOnboardingController.php');
37
  require_once(dirname(__FILE__) . '/wfSupportController.php');
38
  require_once(dirname(__FILE__) . '/wfCredentialsController.php');
 
39
  require_once(dirname(__FILE__) . '/wfDateLocalization.php');
40
  require_once(dirname(__FILE__) . '/wfAdminNoticeQueue.php');
41
 
@@ -65,6 +66,11 @@ class wordfence {
65
  public static $commentSpamItems = array();
66
  public static function installPlugin(){
67
  self::runInstall();
 
 
 
 
 
68
  //Used by MU code below
69
  update_option('wordfenceActivated', 1);
70
  }
@@ -102,9 +108,9 @@ class wordfence {
102
  }
103
 
104
  if(wfConfig::get('deleteTablesOnDeact')){
 
105
  $schema = new wfSchema();
106
  $schema->dropAll();
107
- wfConfig::updateTableExists();
108
  foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
109
  if (is_multisite() && function_exists('delete_network_option')) {
110
  delete_network_option(null, $opt);
@@ -171,6 +177,8 @@ class wordfence {
171
  }
172
  }
173
  }
 
 
174
  }
175
  private static function keyAlert($msg){
176
  self::alert($msg, $msg . " To ensure uninterrupted Premium Wordfence protection on your site,\nplease renew your API key by visiting http://www.wordfence.com/ Sign in, go to your dashboard,\nselect the key about to expire and click the button to renew that API key.", false);
@@ -187,6 +195,7 @@ class wordfence {
187
  try {
188
  $keyType = wfAPI::KEY_TYPE_FREE;
189
  $keyData = $api->call('ping_api_key', array(), array('supportHash' => wfConfig::get('supportHash', '')));
 
190
  if (isset($keyData['_isPaidKey'])) {
191
  $keyType = wfConfig::get('keyType');
192
  }
@@ -248,6 +257,7 @@ class wordfence {
248
  }
249
  catch(Exception $e){
250
  wordfence::status(4, 'error', "Could not verify Wordfence API Key: " . $e->getMessage());
 
251
  }
252
 
253
  $wfdb = new wfDB();
@@ -403,6 +413,8 @@ class wordfence {
403
  public static function _completeCoreUpdateNotification() {
404
  //This approach is here because WP Core updates run in a different sequence than plugin/theme updates, so we have to defer the running of the notification update sequence by an extra page load
405
  delete_site_transient('wordfence_updating_notifications');
 
 
406
  }
407
  public static function runInstall(){
408
  if(self::$runInstallCalled){ return; }
@@ -428,6 +440,7 @@ class wordfence {
428
 
429
  $schema = new wfSchema();
430
  $schema->createAll(); //if not exists
 
431
 
432
  /** @var wpdb $wpdb */
433
  global $wpdb;
@@ -1014,6 +1027,11 @@ SQL
1014
 
1015
  //Install new schedule. If schedule config is blank it will install the default 'auto' schedule.
1016
  wfScanner::shared()->scheduleScans();
 
 
 
 
 
1017
 
1018
  //Must be the final line
1019
  }
@@ -1104,6 +1122,7 @@ SQL
1104
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
1105
  add_action('wordfence_daily_autoUpdate', 'wfConfig::autoUpdate');
1106
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
 
1107
  add_action('plugins_loaded', 'wordfence::veryFirstAction');
1108
  add_action('init', 'wordfence::initAction');
1109
  //add_action('admin_bar_menu', 'wordfence::admin_bar_menu', 99);
@@ -1485,7 +1504,11 @@ SQL
1485
  wfBlock::createLockout($reason, $IP, wfBlock::lockoutDuration(), time(), time(), 1);
1486
  self::getLog()->tagRequestForLockout($reason);
1487
  if (wfConfig::get('alertOn_loginLockout')) {
1488
- wordfence::alert("User locked out from signing in", "A user with IP address {$IP} has been locked out from signing in or using the password recovery form for the following reason: {$reason}", $IP);
 
 
 
 
1489
  }
1490
  }
1491
 
@@ -1936,7 +1959,7 @@ SQL
1936
  if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) { //User authenticated with name and password, 2FA code ready to check
1937
  $userID = $userDat->ID;
1938
 
1939
- if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) {
1940
  //Do nothing. This is the code path the old method of including the code in the password field will take -- since we already have a valid $authUser, skip the nonce verification portion
1941
  }
1942
  else if (isset($_POST['wordfence_twoFactorNonce'])) {
@@ -1976,7 +1999,7 @@ SQL
1976
  //Everything's good, let the sign in continue
1977
  }
1978
  else {
1979
- if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) { //Using the old method of appending the code to the password
1980
  if ($mode == 'authenticator') {
1981
  remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
1982
  self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code from your authenticator app to the end of your password (e.g., <code>wf123456</code>).'));
@@ -2081,7 +2104,7 @@ SQL
2081
  delete_user_meta($userDat->ID, '_wf_twoFactorNonceTime');
2082
  $authUser = $userDat; //Log in as the user we saved in the wp_authenticate action
2083
  }
2084
- else if (get_class($authUser) == 'WP_User') { //User authenticated with name and password, prompt for the 2FA code
2085
  //Verify at least one administrator has 2FA enabled
2086
  $requireAdminTwoFactor = $hasActivatedTwoFactorUser && wfConfig::get('loginSec_requireAdminTwoFactor');
2087
 
@@ -2346,7 +2369,7 @@ SQL
2346
  require('wfLockedOut.php');
2347
  }
2348
  set_transient($tKey, $tries, wfConfig::get('loginSec_countFailMins') * 60);
2349
- } else if(get_class($authUser) == 'WP_User'){
2350
  delete_transient($tKey); //reset counter on success
2351
  }
2352
  }
@@ -2404,7 +2427,7 @@ SQL
2404
  }
2405
 
2406
  try {
2407
- $response = wp_remote_post(WORDFENCE_HACKATTEMPT_URL . 'multipleHackAttempts/?k=' . rawurlencode(wfConfig::get('apiKey')) . '&t=brute', array(
2408
  'timeout' => 1,
2409
  'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
2410
  'body' => 'IPs=' . rawurlencode(json_encode($toSend)),
@@ -2476,7 +2499,7 @@ SQL
2476
  $toSend = array_values($toSend);
2477
 
2478
  try {
2479
- $response = wp_remote_post(WORDFENCE_HACKATTEMPT_URL . 'multipleHackAttempts/?k=' . rawurlencode(wfConfig::get('apiKey')) . '&t=brute', array(
2480
  'timeout' => 1,
2481
  'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
2482
  'body' => 'IPs=' . rawurlencode(json_encode($toSend)),
@@ -2532,7 +2555,7 @@ SQL
2532
  }
2533
 
2534
  try {
2535
- $result = wp_remote_get(WORDFENCE_HACKATTEMPT_URL . 'hackAttempt/?k=' . rawurlencode(wfConfig::get('apiKey')) .
2536
  '&IP=' . rawurlencode(filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? wfUtils::inet_aton($IP) : wfUtils::inet_pton($IP)) .
2537
  '&t=' . rawurlencode($hitType) .
2538
  '&type=' . $endpointType,
@@ -3007,7 +3030,20 @@ SQL
3007
  //This ensures that if the scan crashes for some reason, the schedule will hold.
3008
  wfScanner::shared()->scheduleScans();
3009
 
3010
- wfScanEngine::startScan();
 
 
 
 
 
 
 
 
 
 
 
 
 
3011
  }
3012
  public static function ajax_saveCountryBlocking_callback(){
3013
  if(! wfConfig::get('isPaid')){
@@ -3037,6 +3073,67 @@ SQL
3037
  $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
3038
  }
3039
  $content .= "\n\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3040
 
3041
  ob_start();
3042
  phpinfo();
@@ -3782,6 +3879,7 @@ HTACCESS;
3782
  wordfence::status(10, 'info', "SUM_KILLED:A request was received to stop the previous scan.");
3783
  wfUtils::clearScanLock(); //Clear the lock now because there may not be a scan running to pick up the kill request and clear the lock
3784
  wfScanEngine::requestKill();
 
3785
  wfConfig::set('lastScanFailureType', false);
3786
  return array(
3787
  'ok' => 1,
@@ -3957,6 +4055,32 @@ HTACCESS;
3957
  'buttonTitle' => __('Close', 'wordfence'),
3958
  ))->render();
3959
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3960
  }
3961
 
3962
  wfUtils::doNotCache();
@@ -3967,7 +4091,7 @@ HTACCESS;
3967
  'currentScanID' => wfScanner::shared()->lastScanTime(),
3968
  'signatureUpdateTime' => wfConfig::get('signatureUpdateTime'),
3969
  'scanFailedHTML' => $scanFailedHTML,
3970
- 'scanStalled' => ($scanFailed == wfIssues::SCAN_FAILED_TIMEOUT ? 1 : 0),
3971
  'scanRunning' => wfScanner::shared()->isRunning() ? 1 : 0,
3972
  'scanStages' => $stages,
3973
  'scanStats' => $stats,
36
  require_once(dirname(__FILE__) . '/wfOnboardingController.php');
37
  require_once(dirname(__FILE__) . '/wfSupportController.php');
38
  require_once(dirname(__FILE__) . '/wfCredentialsController.php');
39
+ require_once(dirname(__FILE__) . '/wfVersionCheckController.php');
40
  require_once(dirname(__FILE__) . '/wfDateLocalization.php');
41
  require_once(dirname(__FILE__) . '/wfAdminNoticeQueue.php');
42
 
66
  public static $commentSpamItems = array();
67
  public static function installPlugin(){
68
  self::runInstall();
69
+
70
+ if (get_current_user_id() > 0) {
71
+ wfConfig::set('activatingIP', wfUtils::getIP());
72
+ }
73
+
74
  //Used by MU code below
75
  update_option('wordfenceActivated', 1);
76
  }
108
  }
109
 
110
  if(wfConfig::get('deleteTablesOnDeact')){
111
+ wfConfig::updateTableExists(false);
112
  $schema = new wfSchema();
113
  $schema->dropAll();
 
114
  foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
115
  if (is_multisite() && function_exists('delete_network_option')) {
116
  delete_network_option(null, $opt);
177
  }
178
  }
179
  }
180
+
181
+ wfVersionCheckController::shared()->checkVersionsAndWarn();
182
  }
183
  private static function keyAlert($msg){
184
  self::alert($msg, $msg . " To ensure uninterrupted Premium Wordfence protection on your site,\nplease renew your API key by visiting http://www.wordfence.com/ Sign in, go to your dashboard,\nselect the key about to expire and click the button to renew that API key.", false);
195
  try {
196
  $keyType = wfAPI::KEY_TYPE_FREE;
197
  $keyData = $api->call('ping_api_key', array(), array('supportHash' => wfConfig::get('supportHash', '')));
198
+ wfConfig::set('useNoc3Secure', isset($keyData['n3']) ? wfUtils::truthyToBoolean($keyData['n3']) : false);
199
  if (isset($keyData['_isPaidKey'])) {
200
  $keyType = wfConfig::get('keyType');
201
  }
257
  }
258
  catch(Exception $e){
259
  wordfence::status(4, 'error', "Could not verify Wordfence API Key: " . $e->getMessage());
260
+ wfConfig::set('useNoc3Secure', false);
261
  }
262
 
263
  $wfdb = new wfDB();
413
  public static function _completeCoreUpdateNotification() {
414
  //This approach is here because WP Core updates run in a different sequence than plugin/theme updates, so we have to defer the running of the notification update sequence by an extra page load
415
  delete_site_transient('wordfence_updating_notifications');
416
+
417
+ wfVersionCheckController::shared()->checkVersionsAndWarn();
418
  }
419
  public static function runInstall(){
420
  if(self::$runInstallCalled){ return; }
440
 
441
  $schema = new wfSchema();
442
  $schema->createAll(); //if not exists
443
+ wfConfig::updateTableExists(true);
444
 
445
  /** @var wpdb $wpdb */
446
  global $wpdb;
1027
 
1028
  //Install new schedule. If schedule config is blank it will install the default 'auto' schedule.
1029
  wfScanner::shared()->scheduleScans();
1030
+
1031
+ //Check our minimum versions and generate the necessary warnings
1032
+ if (!wp_next_scheduled('wordfence_version_check')) {
1033
+ wp_schedule_single_event(time(), 'wordfence_version_check');
1034
+ }
1035
 
1036
  //Must be the final line
1037
  }
1122
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
1123
  add_action('wordfence_daily_autoUpdate', 'wfConfig::autoUpdate');
1124
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
1125
+ add_action('wordfence_version_check', array(wfVersionCheckController::shared(), 'checkVersionsAndWarn'));
1126
  add_action('plugins_loaded', 'wordfence::veryFirstAction');
1127
  add_action('init', 'wordfence::initAction');
1128
  //add_action('admin_bar_menu', 'wordfence::admin_bar_menu', 99);
1504
  wfBlock::createLockout($reason, $IP, wfBlock::lockoutDuration(), time(), time(), 1);
1505
  self::getLog()->tagRequestForLockout($reason);
1506
  if (wfConfig::get('alertOn_loginLockout')) {
1507
+ $message = sprintf(__('A user with IP addr %s has been locked out from signing in or using the password recovery form for the following reason: %s.', 'wordfence'), $IP, $reason);
1508
+ if (wfBlock::lockoutDuration() > 0) {
1509
+ $message .= "\n" . sprintf(__('The duration of the lockout is %s.', 'wordfence'), wfUtils::makeDuration(wfBlock::lockoutDuration(), true));
1510
+ }
1511
+ wordfence::alert(__('User locked out from signing in', 'wordfence'), $message, $IP);
1512
  }
1513
  }
1514
 
1959
  if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) { //User authenticated with name and password, 2FA code ready to check
1960
  $userID = $userDat->ID;
1961
 
1962
+ if (is_object($authUser) && get_class($authUser) == 'WP_User' && $authUser->ID == $userID) {
1963
  //Do nothing. This is the code path the old method of including the code in the password field will take -- since we already have a valid $authUser, skip the nonce verification portion
1964
  }
1965
  else if (isset($_POST['wordfence_twoFactorNonce'])) {
1999
  //Everything's good, let the sign in continue
2000
  }
2001
  else {
2002
+ if (is_object($authUser) && get_class($authUser) == 'WP_User' && $authUser->ID == $userID) { //Using the old method of appending the code to the password
2003
  if ($mode == 'authenticator') {
2004
  remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
2005
  self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code from your authenticator app to the end of your password (e.g., <code>wf123456</code>).'));
2104
  delete_user_meta($userDat->ID, '_wf_twoFactorNonceTime');
2105
  $authUser = $userDat; //Log in as the user we saved in the wp_authenticate action
2106
  }
2107
+ else if (is_object($authUser) && get_class($authUser) == 'WP_User') { //User authenticated with name and password, prompt for the 2FA code
2108
  //Verify at least one administrator has 2FA enabled
2109
  $requireAdminTwoFactor = $hasActivatedTwoFactorUser && wfConfig::get('loginSec_requireAdminTwoFactor');
2110
 
2369
  require('wfLockedOut.php');
2370
  }
2371
  set_transient($tKey, $tries, wfConfig::get('loginSec_countFailMins') * 60);
2372
+ } else if(is_object($authUser) && get_class($authUser) == 'WP_User'){
2373
  delete_transient($tKey); //reset counter on success
2374
  }
2375
  }
2427
  }
2428
 
2429
  try {
2430
+ $response = wp_remote_post((wfConfig::get('useNoc3Secure') ? WORDFENCE_HACKATTEMPT_URL_SEC : WORDFENCE_HACKATTEMPT_URL) . 'multipleHackAttempts/?k=' . rawurlencode(wfConfig::get('apiKey')) . '&t=brute', array(
2431
  'timeout' => 1,
2432
  'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
2433
  'body' => 'IPs=' . rawurlencode(json_encode($toSend)),
2499
  $toSend = array_values($toSend);
2500
 
2501
  try {
2502
+ $response = wp_remote_post((wfConfig::get('useNoc3Secure') ? WORDFENCE_HACKATTEMPT_URL_SEC : WORDFENCE_HACKATTEMPT_URL) . 'multipleHackAttempts/?k=' . rawurlencode(wfConfig::get('apiKey')) . '&t=brute', array(
2503
  'timeout' => 1,
2504
  'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
2505
  'body' => 'IPs=' . rawurlencode(json_encode($toSend)),
2555
  }
2556
 
2557
  try {
2558
+ $result = wp_remote_get((wfConfig::get('useNoc3Secure') ? WORDFENCE_HACKATTEMPT_URL_SEC : WORDFENCE_HACKATTEMPT_URL) . 'hackAttempt/?k=' . rawurlencode(wfConfig::get('apiKey')) .
2559
  '&IP=' . rawurlencode(filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? wfUtils::inet_aton($IP) : wfUtils::inet_pton($IP)) .
2560
  '&t=' . rawurlencode($hitType) .
2561
  '&type=' . $endpointType,
3030
  //This ensures that if the scan crashes for some reason, the schedule will hold.
3031
  wfScanner::shared()->scheduleScans();
3032
 
3033
+ try {
3034
+ wfScanEngine::startScan();
3035
+ }
3036
+ catch (wfScanEngineTestCallbackFailedException $e) {
3037
+ wfConfig::set('lastScanCompleted', $e->getMessage());
3038
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
3039
+ wfUtils::clearScanLock();
3040
+ }
3041
+ catch (Exception $e) {
3042
+ if ($e->getCode() != wfScanEngine::SCAN_MANUALLY_KILLED) {
3043
+ wfConfig::set('lastScanCompleted', $e->getMessage());
3044
+ wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_GENERAL);
3045
+ }
3046
+ }
3047
  }
3048
  public static function ajax_saveCountryBlocking_callback(){
3049
  if(! wfConfig::get('isPaid')){
3073
  $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
3074
  }
3075
  $content .= "\n\n";
3076
+ $content .= str_repeat('-', 80);
3077
+ $content .= "\n\n";
3078
+
3079
+ $content .= __('# Scan Issues', 'wordfence') . "\n\n";
3080
+ $issues = wfIssues::shared()->getIssues(0, 50, 0, 50);
3081
+ $issueCounts = array_merge(array('new' => 0, 'ignoreP' => 0, 'ignoreC' => 0), wfIssues::shared()->getIssueCounts());
3082
+ $issueTypes = wfIssues::validIssueTypes();
3083
+
3084
+ $content .= sprintf(__('## New Issues (%d total)', 'wordfence'), $issueCounts['new']) . "\n\n";
3085
+ if (isset($issues['new']) && count($issues['new'])) {
3086
+ foreach ($issues['new'] as $i) {
3087
+ if (!in_array($i['type'], $issueTypes)) {
3088
+ continue;
3089
+ }
3090
+
3091
+ $viewContent = '';
3092
+ try {
3093
+ $viewContent = wfView::create('scanner/issue-' . $i['type'], array('textOutput' => $i))->render();
3094
+ }
3095
+ catch (wfViewNotFoundException $e) {
3096
+ //Ignore -- should never happen since we validate the type
3097
+ }
3098
+
3099
+ if (!empty($viewContent)) {
3100
+ $content .= $viewContent . "\n\n";
3101
+ }
3102
+ }
3103
+ }
3104
+ else {
3105
+ $content .= __('No New Issues', 'wordfence') . "\n\n";
3106
+ }
3107
+
3108
+ $content .= str_repeat('-', 10);
3109
+ $content .= "\n\n";
3110
+
3111
+ $content .= sprintf(__('## Ignored Issues (%d total)', 'wordfence'), $issueCounts['ignoreP'] + $issueCounts['ignoreC']) . "\n\n";
3112
+ if (isset($issues['new']) && count($issues['new'])) {
3113
+ foreach ($issues['ignored'] as $i) {
3114
+ if (!in_array($i['type'], $issueTypes)) {
3115
+ continue;
3116
+ }
3117
+
3118
+ $viewContent = '';
3119
+ try {
3120
+ $viewContent = wfView::create('scanner/issue-' . $i['type'], array('textOutput' => $i))->render();
3121
+ }
3122
+ catch (wfViewNotFoundException $e) {
3123
+ //Ignore -- should never happen since we validate the type
3124
+ }
3125
+
3126
+ if (!empty($viewContent)) {
3127
+ $content .= $viewContent . "\n\n";
3128
+ }
3129
+ }
3130
+ }
3131
+ else {
3132
+ $content .= __('No Ignored Issues', 'wordfence') . "\n\n";
3133
+ }
3134
+
3135
+ $content .= str_repeat('-', 80);
3136
+ $content .= "\n\n";
3137
 
3138
  ob_start();
3139
  phpinfo();
3879
  wordfence::status(10, 'info', "SUM_KILLED:A request was received to stop the previous scan.");
3880
  wfUtils::clearScanLock(); //Clear the lock now because there may not be a scan running to pick up the kill request and clear the lock
3881
  wfScanEngine::requestKill();
3882
+ wfConfig::remove('scanStartAttempt');
3883
  wfConfig::set('lastScanFailureType', false);
3884
  return array(
3885
  'ok' => 1,
4055
  'buttonTitle' => __('Close', 'wordfence'),
4056
  ))->render();
4057
  break;
4058
+ case wfIssues::SCAN_FAILED_START_TIMEOUT:
4059
+ case wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED:
4060
+ $scanFailedHTML = wfView::create('scanner/scan-failed', array(
4061
+ 'messageHTML' => __('The scan has failed to start. This is often because the site either cannot make outbound requests or is blocked from connecting to itself.', 'wordfence') . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_SCAN_FAILED_START) . '" target="_blank" rel="noopener noreferrer">' . __('Click here for steps you can try.', 'wordfence') . '</a>',
4062
+ 'buttonTitle' => __('Close', 'wordfence'),
4063
+ ))->render();
4064
+ break;
4065
+ case wfIssues::SCAN_FAILED_API_SSL_UNAVAILABLE:
4066
+ $scanFailedHTML = wfView::create('scanner/scan-failed', array(
4067
+ 'messageHTML' => __('Scans are not functional because SSL is unavailable.', 'wordfence'),
4068
+ 'buttonTitle' => __('Close', 'wordfence'),
4069
+ ))->render();
4070
+ break;
4071
+ case wfIssues::SCAN_FAILED_API_CALL_FAILED:
4072
+ $scanFailedHTML = wfView::create('scanner/scan-failed', array(
4073
+ 'messageHTML' => __('The scan has failed because we were unable to contact the Wordfence servers. Some sites may need adjustments to run scans reliably.', 'wordfence') . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_SCAN_FAILS) . '" target="_blank" rel="noopener noreferrer">' . __('Click here for steps you can try.', 'wordfence') . '</a>',
4074
+ 'buttonTitle' => __('Close', 'wordfence'),
4075
+ ))->render();
4076
+ break;
4077
+ case wfIssues::SCAN_FAILED_API_INVALID_RESPONSE:
4078
+ case wfIssues::SCAN_FAILED_API_ERROR_RESPONSE:
4079
+ $scanFailedHTML = wfView::create('scanner/scan-failed', array(
4080
+ 'messageHTML' => __('The scan has failed because we received an unexpected response from the Wordfence servers. This may be a temporary error, though some sites may need adjustments to run scans reliably.', 'wordfence') . ' <a href="' . wfSupportController::esc_supportURL(wfSupportController::ITEM_SCAN_FAILS) . '" target="_blank" rel="noopener noreferrer">' . __('Click here for steps you can try.', 'wordfence') . '</a>',
4081
+ 'buttonTitle' => __('Close', 'wordfence'),
4082
+ ))->render();
4083
+ break;
4084
  }
4085
 
4086
  wfUtils::doNotCache();
4091
  'currentScanID' => wfScanner::shared()->lastScanTime(),
4092
  'signatureUpdateTime' => wfConfig::get('signatureUpdateTime'),
4093
  'scanFailedHTML' => $scanFailedHTML,
4094
+ 'scanStalled' => ($scanFailed == wfIssues::SCAN_FAILED_TIMEOUT || $scanFailed == wfIssues::SCAN_FAILED_START_TIMEOUT ? 1 : 0),
4095
  'scanRunning' => wfScanner::shared()->isRunning() ? 1 : 0,
4096
  'scanStages' => $stages,
4097
  'scanStats' => $stats,
lib/wordfenceConstants.php CHANGED
@@ -7,6 +7,7 @@ define('WORDFENCE_API_URL_BASE_NONSEC', WORDFENCE_API_URL_NONSEC . 'v' . WORDFEN
7
  define('WORDFENCE_BREACH_URL_BASE_SEC', WORDFENCE_API_URL_SEC . 'passwords/');
8
  define('WORDFENCE_BREACH_URL_BASE_NONSEC', WORDFENCE_API_URL_NONSEC . 'passwords/');
9
  define('WORDFENCE_HACKATTEMPT_URL', 'http://noc3.wordfence.com/');
 
10
  define('WORDFENCE_MAX_SCAN_LOCK_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
11
  define('WORDFENCE_DEFAULT_MAX_SCAN_TIME', 10800);
12
  define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
@@ -21,5 +22,6 @@ if (!defined('WORDFENCE_SCAN_ISSUES_PER_PAGE')) { define('WORDFENCE_SCAN_ISSUES_
21
  if (!defined('WORDFENCE_BLOCKED_IPS_PER_PAGE')) { define('WORDFENCE_BLOCKED_IPS_PER_PAGE', 100); }
22
  if (!defined('WORDFENCE_DISABLE_FILE_VIEWER')) { define('WORDFENCE_DISABLE_FILE_VIEWER', false); }
23
  if (!defined('WORDFENCE_SCAN_FAILURE_THRESHOLD')) { define('WORDFENCE_SCAN_FAILURE_THRESHOLD', 300); }
 
24
  if (!defined('WORDFENCE_PREFER_WP_HOME_FOR_WPML')) { define('WORDFENCE_PREFER_WP_HOME_FOR_WPML', false); } //When determining the unfiltered `home` and `siteurl` with WPML installed, use WP_HOME and WP_SITEURL if set instead of the database values
25
  if (!defined('WORDFENCE_SCAN_MIN_EXECUTION_TIME')) { define('WORDFENCE_SCAN_MIN_EXECUTION_TIME', 8); }
7
  define('WORDFENCE_BREACH_URL_BASE_SEC', WORDFENCE_API_URL_SEC . 'passwords/');
8
  define('WORDFENCE_BREACH_URL_BASE_NONSEC', WORDFENCE_API_URL_NONSEC . 'passwords/');
9
  define('WORDFENCE_HACKATTEMPT_URL', 'http://noc3.wordfence.com/');
10
+ define('WORDFENCE_HACKATTEMPT_URL_SEC', 'https://noc3.wordfence.com/');
11
  define('WORDFENCE_MAX_SCAN_LOCK_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
12
  define('WORDFENCE_DEFAULT_MAX_SCAN_TIME', 10800);
13
  define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
22
  if (!defined('WORDFENCE_BLOCKED_IPS_PER_PAGE')) { define('WORDFENCE_BLOCKED_IPS_PER_PAGE', 100); }
23
  if (!defined('WORDFENCE_DISABLE_FILE_VIEWER')) { define('WORDFENCE_DISABLE_FILE_VIEWER', false); }
24
  if (!defined('WORDFENCE_SCAN_FAILURE_THRESHOLD')) { define('WORDFENCE_SCAN_FAILURE_THRESHOLD', 300); }
25
+ if (!defined('WORDFENCE_SCAN_START_FAILURE_THRESHOLD')) { define('WORDFENCE_SCAN_START_FAILURE_THRESHOLD', 15); }
26
  if (!defined('WORDFENCE_PREFER_WP_HOME_FOR_WPML')) { define('WORDFENCE_PREFER_WP_HOME_FOR_WPML', false); } //When determining the unfiltered `home` and `siteurl` with WPML installed, use WP_HOME and WP_SITEURL if set instead of the database values
27
  if (!defined('WORDFENCE_SCAN_MIN_EXECUTION_TIME')) { define('WORDFENCE_SCAN_MIN_EXECUTION_TIME', 8); }
readme.txt CHANGED
@@ -2,8 +2,9 @@
2
  Contributors: mmaunder
3
  Tags: security, firewall, malware scanner, web application firewall, antivirus, block hackers, country blocking, clean hacked site, blacklist, waf, login security
4
  Requires at least: 3.9
 
5
  Tested up to: 4.9.5
6
- Stable tag: 7.1.2
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
@@ -167,6 +168,20 @@ Secure your website with Wordfence.
167
 
168
  == Changelog ==
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  = 7.1.2 =
171
  * Improvement: Added support for filtering the blocks list.
172
  * Improvement: Added a flow for generating the WAF autoprepend file and retrieving the path for manual installations.
2
  Contributors: mmaunder
3
  Tags: security, firewall, malware scanner, web application firewall, antivirus, block hackers, country blocking, clean hacked site, blacklist, waf, login security
4
  Requires at least: 3.9
5
+ Requires PHP: 5.2
6
  Tested up to: 4.9.5
7
+ Stable tag: 7.1.3
8
 
9
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
10
 
168
 
169
  == Changelog ==
170
 
171
+ = 7.1.3 =
172
+ * Improvement: Improved the performance of our config table status check.
173
+ * Improvement: The IP address of the user activating Wordfence is now used by the breached password check until an admin successfully logs in.
174
+ * Improvement: Added several new error displays for scan failures to help diagnose and fix issues.
175
+ * Improvement: Added the block duration to alerts generated when an IP is blocked.
176
+ * Improvement: A text version of scan results is now included in the activity log email.
177
+ * Improvement: The WAF install/uninstall process no longer asks to backup files that do not exist.
178
+ * Change: Began a phased rollout of moving brute force queries to be https-only.
179
+ * Change: Added the initial deprecation notice for PHP 5.2.
180
+ * Change: Suppressed a script tag on the diagnostics page from being output in the email version.
181
+ * Fix: Addressed an issue where plugins that return a null user during authentication would cause a PHP notice to be logged.
182
+ * Fix: Fixed an issue where plugins that use non-standard version formatting could end up with a inaccurate vulnerability status.
183
+ * Fix: Added a workaround for web email clients that erroneously encode some URL characters (e.g., #).
184
+
185
  = 7.1.2 =
186
  * Improvement: Added support for filtering the blocks list.
187
  * Improvement: Added a flow for generating the WAF autoprepend file and retrieving the path for manual installations.
views/blocking/block-list.php CHANGED
@@ -8,7 +8,7 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
8
  <div class="wf-row">
9
  <div class="wf-col-xs-12">
10
  <div class="wf-flex-horizontal wf-flex-full-width wf-add-bottom-small">
11
- <h3 class="wf-no-top wf-no-bottom"><?php printf(__('All blocks<span class="wf-hidden-xs"> for %s</span>', 'wordfence'), preg_replace('/^https?:\/\//i', '', wfUtils::wpSiteURL())); ?></h3>
12
  <div class="wf-right">
13
  <div class="wf-inline-block">
14
  <ul class="wf-option wf-option-toggled-boolean-switch wf-option-no-spacing" data-option="displayAutomaticBlocks" data-enabled-value="1" data-disabled-value="0" data-original-value="<?php echo wfConfig::get('displayAutomaticBlocks') ? 1 : 0; ?>">
8
  <div class="wf-row">
9
  <div class="wf-col-xs-12">
10
  <div class="wf-flex-horizontal wf-flex-full-width wf-add-bottom-small">
11
+ <h3 class="wf-no-top wf-no-bottom"><?php printf(__('Current blocks<span class="wf-hidden-xs"> for %s</span>', 'wordfence'), preg_replace('/^https?:\/\//i', '', wfUtils::wpSiteURL())); ?></h3>
12
  <div class="wf-right">
13
  <div class="wf-inline-block">
14
  <ul class="wf-option wf-option-toggled-boolean-switch wf-option-no-spacing" data-option="displayAutomaticBlocks" data-enabled-value="1" data-disabled-value="0" data-original-value="<?php echo wfConfig::get('displayAutomaticBlocks') ? 1 : 0; ?>">
views/scanner/issue-base.php CHANGED
@@ -11,7 +11,11 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
11
  * @var array $summaryControls An array of summary controls for the issue type.
12
  * @var array $detailPairs An array of label/value pairs for the issue's detail data. If the entry should only be conditionally shown, the value may be an array of the format array(conditional, displayValue).
13
  * @var array $detailControls An array of detail controls for the issue type.
 
 
14
  */
 
 
15
  ?>
16
  <script type="text/x-jquery-template" id="issueTmpl_<?php echo $internalType; ?>">
17
  <ul class="wf-issue wf-issue-<?php echo $internalType; ?> {{if severity == '1'}}wf-issue-severity-critical{{else}}wf-issue-severity-warning{{/if}}" data-issue-id="${id}" data-issue-type="<?php echo $internalType; ?>" data-issue-severity="${severity}" data-high-sensitivity="{{if (data.highSense == '1')}}1{{else}}0{{/if}}" data-beta-signatures="{{if (data.betaSigs == '1')}}1{{else}}0{{/if}}">
@@ -61,3 +65,102 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
61
  </li>
62
  </ul>
63
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  * @var array $summaryControls An array of summary controls for the issue type.
12
  * @var array $detailPairs An array of label/value pairs for the issue's detail data. If the entry should only be conditionally shown, the value may be an array of the format array(conditional, displayValue).
13
  * @var array $detailControls An array of detail controls for the issue type.
14
+ * @var array $textOutput If provided, used the content of the array to output plain text rather than the HTML template.
15
+ * @var array $textOutputDetailPairs An array of label/value pairs for the issue's detail data if outputting via text. If the entry should only be conditionally shown, the value may be an array of the format array(conditional, displayValue) where conditional is one or more keypaths that must all be truthy. It is preprocessed lightly for output: all values prefixed with $ will be treated as keypaths in the $textOutput array. If that is prefixed with ! for the conditional, its value will be inverted.
16
  */
17
+
18
+ if (!isset($textOutput) || !is_array($textOutput)):
19
  ?>
20
  <script type="text/x-jquery-template" id="issueTmpl_<?php echo $internalType; ?>">
21
  <ul class="wf-issue wf-issue-<?php echo $internalType; ?> {{if severity == '1'}}wf-issue-severity-critical{{else}}wf-issue-severity-warning{{/if}}" data-issue-id="${id}" data-issue-type="<?php echo $internalType; ?>" data-issue-severity="${severity}" data-high-sensitivity="{{if (data.highSense == '1')}}1{{else}}0{{/if}}" data-beta-signatures="{{if (data.betaSigs == '1')}}1{{else}}0{{/if}}">
65
  </li>
66
  </ul>
67
  </script>
68
+ <?php else: ?>
69
+ <?php
70
+ echo '[' . $displayType . ($textOutput['status'] == 'ignoreP' || $textOutput['status'] == 'ignoreP' ? ', ' . __('Ignored', 'wordfence') : '') . ']' . "\n";
71
+ echo $textOutput['shortMsg'] . "\n";
72
+ echo sprintf(__('Issue Found: %s', 'wordfence'), $textOutput['displayTime']) . "\n";
73
+ echo sprintf(__('Severity: %s', 'wordfence'), $textOutput['severity'] == 1 ? __('Critical', 'wordfence') : __('Warning', 'wordfence')) . "\n";
74
+ foreach ($textOutputDetailPairs as $label => $value) {
75
+ if ($value === null) {
76
+ echo "\n";
77
+ continue;
78
+ }
79
+
80
+ unset($conditional);
81
+ if (is_array($value)) {
82
+ $conditional = $value[0];
83
+ if (!is_array($conditional)) {
84
+ $conditional = array($conditional);
85
+ }
86
+ $value = $value[1];
87
+ }
88
+
89
+ $allow = true;
90
+ if (isset($conditional)) {
91
+ foreach ($conditional as $test) {
92
+ if (!$allow) {
93
+ break;
94
+ }
95
+
96
+ if (preg_match('/^!?\$(\S+)/', $test, $matches)) {
97
+ $invert = (strpos($test, '!') === 0);
98
+ $components = explode('.', $matches[1]);
99
+ $tier = $textOutput;
100
+ foreach ($components as $index => $c) {
101
+ if (is_array($tier) && !isset($tier[$c])) {
102
+ if (!$invert) {
103
+ $allow = false;
104
+ }
105
+ break;
106
+ }
107
+
108
+ if ($index == count($components) - 1 && is_array($tier)) {
109
+ if ((!$tier[$c] && !$invert) || ($tier[$c] && $invert)) {
110
+ $allow = false;
111
+ }
112
+ break;
113
+ }
114
+ else if (!is_array($tier)) {
115
+ $allow = false;
116
+ break;
117
+ }
118
+
119
+ $tier = $tier[$c];
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ if (!$allow) {
126
+ continue;
127
+ }
128
+
129
+ if (preg_match_all('/(?<=^|\s)\$(\S+)(?=$|\s)/', $value, $matches, PREG_OFFSET_CAPTURE)) {
130
+ array_shift($matches);
131
+ $matches = $matches[0];
132
+ $matches = array_reverse($matches);
133
+ foreach ($matches as $m) {
134
+ $resolvedKeyPath = '';
135
+ $components = explode('.', $m[0]);
136
+ $tier = $textOutput;
137
+ foreach ($components as $index => $c) {
138
+ if (is_array($tier) && !isset($tier[$c])) {
139
+ $allow = false;
140
+ break 2;
141
+ }
142
+
143
+ if ($index == count($components) - 1 && is_array($tier)) {
144
+ $resolvedKeyPath = (string) $tier[$c];
145
+ break;
146
+ }
147
+ else if (!is_array($tier)) {
148
+ $allow = false;
149
+ break 2;
150
+ }
151
+
152
+ $tier = $tier[$c];
153
+ }
154
+
155
+ $value = substr($value, 0, $m[1] - 1) . strip_tags($resolvedKeyPath) . substr($value, $m[1] + strlen($m[0]));
156
+ }
157
+ }
158
+
159
+ if (!$allow) {
160
+ continue;
161
+ }
162
+
163
+ echo $label . ': ' . $value . "\n";
164
+ }
165
+ ?>
166
+ <?php endif; ?>
views/scanner/issue-checkGSB.php CHANGED
@@ -16,4 +16,10 @@ echo wfView::create('scanner/issue-base', array(
16
  'detailControls' => array(
17
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
18
  ),
 
 
 
 
 
 
19
  ))->render();
16
  'detailControls' => array(
17
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
18
  ),
19
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
20
+ 'textOutputDetailPairs' => array(
21
+ __('Bad URL', 'wordfence') => '$data.badURL',
22
+ null,
23
+ __('Details', 'wordfence') => '$longMsg',
24
+ ),
25
  ))->render();
views/scanner/issue-checkHowGetIPs.php CHANGED
@@ -15,4 +15,8 @@ echo wfView::create('scanner/issue-base', array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.useRecommendedHowGetIPs(\'${id}\'); return false;">' . __('Use Recommended Value', 'wordfence') . '</a>',
16
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
17
  ),
 
 
 
 
18
  ))->render();
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.useRecommendedHowGetIPs(\'${id}\'); return false;">' . __('Use Recommended Value', 'wordfence') . '</a>',
16
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
17
  ),
18
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
19
+ 'textOutputDetailPairs' => array(
20
+ __('Details', 'wordfence') => '$longMsg',
21
+ ),
22
  ))->render();
views/scanner/issue-checkSpamIP.php CHANGED
@@ -14,4 +14,8 @@ echo wfView::create('scanner/issue-base', array(
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
  ),
 
 
 
 
17
  ))->render();
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
  ),
17
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
18
+ 'textOutputDetailPairs' => array(
19
+ __('Details', 'wordfence') => '$longMsg',
20
+ ),
21
  ))->render();
views/scanner/issue-commentBadURL.php CHANGED
@@ -21,5 +21,17 @@ echo wfView::create('scanner/issue-base', array(
21
  ),
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
- )
 
 
 
 
 
 
 
 
 
 
 
 
25
  ))->render();
21
  ),
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
+ ),
25
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
26
+ 'textOutputDetailPairs' => array(
27
+ __('Author', 'wordfence') => '$data.author',
28
+ __('Bad URL', 'wordfence') => '$data.badURL',
29
+ __('Posted on', 'wordfence') => '$data.commentDate',
30
+ null,
31
+ __('Details', 'wordfence') => '$longMsg',
32
+ null,
33
+ __('Multisite Blog ID', 'wordfence') => array('$data.isMultisite', '$data.blog_id'),
34
+ __('Multisite Blog Domain', 'wordfence') => array('$data.isMultisite', '$data.domain'),
35
+ __('Multisite Blog Path', 'wordfence') => array('$data.isMultisite', '$data.path'),
36
+ ),
37
  ))->render();
views/scanner/issue-configReadable.php CHANGED
@@ -17,5 +17,11 @@ echo wfView::create('scanner/issue-base', array(
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
- )
 
 
 
 
 
 
21
  ))->render();
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
+ ),
21
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
22
+ 'textOutputDetailPairs' => array(
23
+ __('URL', 'wordfence') => '$data.url',
24
+ null,
25
+ __('Details', 'wordfence') => '$longMsg',
26
+ ),
27
  ))->render();
views/scanner/issue-coreUnknown.php CHANGED
@@ -13,5 +13,9 @@ echo wfView::create('scanner/issue-base', array(
13
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
14
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
- )
 
 
 
 
17
  ))->render();
13
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
14
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
+ ),
17
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
18
+ 'textOutputDetailPairs' => array(
19
+ __('Details', 'wordfence') => '$longMsg',
20
+ ),
21
  ))->render();
views/scanner/issue-database.php CHANGED
@@ -19,4 +19,11 @@ echo wfView::create('scanner/issue-base', array(
19
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteDatabaseOption(\'${id}\'); return false;">' . __('Delete Option', 'wordfence') . '</a>{{/if}}',
20
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
21
  ),
 
 
 
 
 
 
 
22
  ))->render();
19
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteDatabaseOption(\'${id}\'); return false;">' . __('Delete Option', 'wordfence') . '</a>{{/if}}',
20
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
21
  ),
22
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
23
+ 'textOutputDetailPairs' => array(
24
+ __('Option Name', 'wordfence') => '$data.option_name',
25
+ __('Bad URL', 'wordfence') => '$data.badURL',
26
+ null,
27
+ __('Details', 'wordfence') => '$longMsg',
28
+ ),
29
  ))->render();
views/scanner/issue-diskSpace.php CHANGED
@@ -15,5 +15,11 @@ echo wfView::create('scanner/issue-base', array(
15
  ),
16
  'detailControls' => array(
17
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
18
- )
 
 
 
 
 
 
19
  ))->render();
15
  ),
16
  'detailControls' => array(
17
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
18
+ ),
19
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
20
+ 'textOutputDetailPairs' => array(
21
+ __('Space Remaining', 'wordfence') => '$data.spaceLeft',
22
+ null,
23
+ __('Details', 'wordfence') => '$longMsg',
24
+ ),
25
  ))->render();
views/scanner/issue-dnsChange.php CHANGED
@@ -16,5 +16,12 @@ echo wfView::create('scanner/issue-base', array(
16
  ),
17
  'detailControls' => array(
18
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
19
- )
 
 
 
 
 
 
 
20
  ))->render();
16
  ),
17
  'detailControls' => array(
18
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
19
+ ),
20
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
21
+ 'textOutputDetailPairs' => array(
22
+ __('Old DNS Records', 'wordfence') => '$data.oldDNS',
23
+ __('New DNS Records', 'wordfence') => '$data.newDNS',
24
+ null,
25
+ __('Details', 'wordfence') => '$longMsg',
26
+ ),
27
  ))->render();
views/scanner/issue-easyPassword.php CHANGED
@@ -17,5 +17,13 @@ echo wfView::create('scanner/issue-base', array(
17
  ),
18
  'detailControls' => array(
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
- )
 
 
 
 
 
 
 
 
21
  ))->render();
17
  ),
18
  'detailControls' => array(
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
+ ),
21
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
22
+ 'textOutputDetailPairs' => array(
23
+ __('Login Name', 'wordfence') => '$data.user_login',
24
+ __('User Email', 'wordfence') => '$data.user_email',
25
+ __('Full Name', 'wordfence') => '$data.first_name $data.last_name',
26
+ null,
27
+ __('Details', 'wordfence') => '$longMsg',
28
+ ),
29
  ))->render();
views/scanner/issue-file.php CHANGED
@@ -20,5 +20,15 @@ echo wfView::create('scanner/issue-base', array(
20
  '{{if data.canDiff}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeDiffLink(data)}">' . __('View Differences', 'wordfence') . '</a>{{/if}}',
21
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
- )
 
 
 
 
 
 
 
 
 
 
24
  ))->render();
20
  '{{if data.canDiff}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeDiffLink(data)}">' . __('View Differences', 'wordfence') . '</a>{{/if}}',
21
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
+ ),
24
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
25
+ 'textOutputDetailPairs' => array(
26
+ __('Filename', 'wordfence') => '$data.file',
27
+ __('File Type', 'wordfence') => '$data.ucType',
28
+ __('File Type', 'wordfence') => '$data.wpconfig',
29
+ __('File Type', 'wordfence') => array(array('!$data.ucType', '!$data.wpconfig'), 'Not a core, theme, or plugin file from wordpress.org'),
30
+ __('Bad URL', 'wordfence') => '$data.badURL',
31
+ null,
32
+ __('Details', 'wordfence') => '$longMsg',
33
+ ),
34
  ))->render();
views/scanner/issue-geoipSupport.php CHANGED
@@ -13,5 +13,9 @@ echo wfView::create('scanner/issue-base', array(
13
  ),
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
- )
 
 
 
 
17
  ))->render();
13
  ),
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
+ ),
17
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
18
+ 'textOutputDetailPairs' => array(
19
+ __('Details', 'wordfence') => '$longMsg',
20
+ ),
21
  ))->render();
views/scanner/issue-knownfile.php CHANGED
@@ -20,5 +20,14 @@ echo wfView::create('scanner/issue-base', array(
20
  '{{if data.canDiff}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeDiffLink(data)}">' . __('View Differences', 'wordfence') . '</a>{{/if}}',
21
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
- )
 
 
 
 
 
 
 
 
 
24
  ))->render();
20
  '{{if data.canDiff}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeDiffLink(data)}">' . __('View Differences', 'wordfence') . '</a>{{/if}}',
21
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
+ ),
24
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
25
+ 'textOutputDetailPairs' => array(
26
+ __('Filename', 'wordfence') => '$data.file',
27
+ __('File Type', 'wordfence') => '$data.ucType',
28
+ __('File Type', 'wordfence') => array('!$data.ucType', 'Not a core, theme, or plugin file from wordpress.org'),
29
+ __('Bad URL', 'wordfence') => '$data.badURL',
30
+ null,
31
+ __('Details', 'wordfence') => '$longMsg',
32
+ ),
33
  ))->render();
views/scanner/issue-optionBadURL.php CHANGED
@@ -21,4 +21,15 @@ echo wfView::create('scanner/issue-base', array(
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
  ),
 
 
 
 
 
 
 
 
 
 
 
24
  ))->render();
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
  ),
24
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
25
+ 'textOutputDetailPairs' => array(
26
+ __('Option Name', 'wordfence') => '$data.optionKey',
27
+ __('Bad URL', 'wordfence') => '$data.badURL',
28
+ null,
29
+ __('Details', 'wordfence') => '$longMsg',
30
+ null,
31
+ __('Multisite Blog ID', 'wordfence') => array('$data.isMultisite', '$data.blog_id'),
32
+ __('Multisite Blog Domain', 'wordfence') => array('$data.isMultisite', '$data.domain'),
33
+ __('Multisite Blog Path', 'wordfence') => array('$data.isMultisite', '$data.path'),
34
+ ),
35
  ))->render();
views/scanner/issue-postBadTitle.php CHANGED
@@ -20,5 +20,16 @@ echo wfView::create('scanner/issue-base', array(
20
  ),
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
- )
 
 
 
 
 
 
 
 
 
 
 
24
  ))->render();
20
  ),
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
+ ),
24
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
25
+ 'textOutputDetailPairs' => array(
26
+ __('Title', 'wordfence') => '$data.postTitle',
27
+ __('Posted on', 'wordfence') => '$data.postDate',
28
+ null,
29
+ __('Details', 'wordfence') => '$longMsg',
30
+ null,
31
+ __('Multisite Blog ID', 'wordfence') => array('$data.isMultisite', '$data.blog_id'),
32
+ __('Multisite Blog Domain', 'wordfence') => array('$data.isMultisite', '$data.domain'),
33
+ __('Multisite Blog Path', 'wordfence') => array('$data.isMultisite', '$data.path'),
34
+ ),
35
  ))->render();
views/scanner/issue-postBadURL.php CHANGED
@@ -21,5 +21,17 @@ echo wfView::create('scanner/issue-base', array(
21
  ),
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
- )
 
 
 
 
 
 
 
 
 
 
 
 
25
  ))->render();
21
  ),
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
+ ),
25
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
26
+ 'textOutputDetailPairs' => array(
27
+ __('Title', 'wordfence') => '$data.postTitle',
28
+ __('Bad URL', 'wordfence') => '$data.badURL',
29
+ __('Posted on', 'wordfence') => '$data.postDate',
30
+ null,
31
+ __('Details', 'wordfence') => '$longMsg',
32
+ null,
33
+ __('Multisite Blog ID', 'wordfence') => array('$data.isMultisite', '$data.blog_id'),
34
+ __('Multisite Blog Domain', 'wordfence') => array('$data.isMultisite', '$data.domain'),
35
+ __('Multisite Blog Path', 'wordfence') => array('$data.isMultisite', '$data.path'),
36
+ ),
37
  ))->render();
views/scanner/issue-publiclyAccessible.php CHANGED
@@ -17,5 +17,11 @@ echo wfView::create('scanner/issue-base', array(
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
- )
 
 
 
 
 
 
21
  ))->render();
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-delete-file">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
+ ),
21
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
22
+ 'textOutputDetailPairs' => array(
23
+ __('URL', 'wordfence') => '$data.url',
24
+ null,
25
+ __('Details', 'wordfence') => '$longMsg',
26
+ ),
27
  ))->render();
views/scanner/issue-spamvertizeCheck.php CHANGED
@@ -14,4 +14,8 @@ echo wfView::create('scanner/issue-base', array(
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
  ),
 
 
 
 
17
  ))->render();
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
  ),
17
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
18
+ 'textOutputDetailPairs' => array(
19
+ __('Details', 'wordfence') => '$longMsg',
20
+ ),
21
  ))->render();
views/scanner/issue-suspiciousAdminUsers.php CHANGED
@@ -15,5 +15,9 @@ echo wfView::create('scanner/issue-base', array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteAdminUser(\'${id}\'); return false;">' . __('Delete User', 'wordfence') . '</a>',
16
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.revokeAdminUser(\'${id}\'); return false;">' . __('Revoke Capabilities', 'wordfence') . '</a>',
17
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
18
- )
 
 
 
 
19
  ))->render();
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteAdminUser(\'${id}\'); return false;">' . __('Delete User', 'wordfence') . '</a>',
16
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.revokeAdminUser(\'${id}\'); return false;">' . __('Revoke Capabilities', 'wordfence') . '</a>',
17
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
18
+ ),
19
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
20
+ 'textOutputDetailPairs' => array(
21
+ __('Details', 'wordfence') => '$longMsg',
22
+ ),
23
  ))->render();
views/scanner/issue-timelimit.php CHANGED
@@ -13,5 +13,9 @@ echo wfView::create('scanner/issue-base', array(
13
  ),
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
- )
 
 
 
 
17
  ))->render();
13
  ),
14
  'detailControls' => array(
15
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
16
+ ),
17
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
18
+ 'textOutputDetailPairs' => array(
19
+ __('Details', 'wordfence') => '$longMsg',
20
+ ),
21
  ))->render();
views/scanner/issue-wfPluginAbandoned.php CHANGED
@@ -22,5 +22,18 @@ echo wfView::create('scanner/issue-base', array(
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
  '<a href="' . esc_url(network_admin_url('plugins.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-manage-plugins">' . __('Manage Plugins', 'wordfence') . '</a>',
25
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  ))->render();
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
  '<a href="' . esc_url(network_admin_url('plugins.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-manage-plugins">' . __('Manage Plugins', 'wordfence') . '</a>',
25
+ ),
26
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
27
+ 'textOutputDetailPairs' => array(
28
+ __('Plugin Name', 'wordfence') => '$data.name',
29
+ __('Current Plugin Version', 'wordfence') => '$data.version',
30
+ __('Last Updated', 'wordfence') => '$data.dateUpdated',
31
+ null,
32
+ __('Details', 'wordfence') => '$longMsg',
33
+ __('Vulnerability Status', 'wordfence') => array('$data.vulnerable', __('Plugin has unpatched security issues.', 'wordfence')),
34
+ null,
35
+ __('Plugin URL', 'wordfence') => '$data.homepage',
36
+ __('Repository URL', 'wordfence') => '$data.wpURL',
37
+ __('Vulnerability Information', 'wordfence') => '$data.vulnerabilityLink',
38
+ ),
39
  ))->render();
views/scanner/issue-wfPluginRemoved.php CHANGED
@@ -20,6 +20,16 @@ echo wfView::create('scanner/issue-base', array(
20
  'detailControls' => array(
21
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
22
  '<a href="' . esc_url(network_admin_url('plugins.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-manage-plugins">' . __('Manage Plugins', 'wordfence') . '</a>',
23
- )
 
 
 
 
 
 
 
 
 
 
24
  ))->render();
25
 
20
  'detailControls' => array(
21
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
22
  '<a href="' . esc_url(network_admin_url('plugins.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-manage-plugins">' . __('Manage Plugins', 'wordfence') . '</a>',
23
+ ),
24
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
25
+ 'textOutputDetailPairs' => array(
26
+ __('Plugin Name', 'wordfence') => '$data.Name',
27
+ __('Current Plugin Version', 'wordfence') => '$data.Version',
28
+ null,
29
+ __('Details', 'wordfence') => '$longMsg',
30
+ null,
31
+ __('Plugin URL', 'wordfence') => '$data.PluginURI',
32
+ __('Vulnerability Information', 'wordfence') => '$data.vulnerabilityLink',
33
+ ),
34
  ))->render();
35
 
views/scanner/issue-wfPluginUpgrade.php CHANGED
@@ -22,5 +22,18 @@ echo wfView::create('scanner/issue-base', array(
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
  '<a href="' . esc_url(network_admin_url('update-core.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-view-updates">' . __('View Updates', 'wordfence') . '</a>',
25
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  ))->render();
22
  'detailControls' => array(
23
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
24
  '<a href="' . esc_url(network_admin_url('update-core.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-view-updates">' . __('View Updates', 'wordfence') . '</a>',
25
+ ),
26
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
27
+ 'textOutputDetailPairs' => array(
28
+ __('Plugin Name', 'wordfence') => '$data.Name',
29
+ __('Current Plugin Version', 'wordfence') => '$data.Version',
30
+ __('New Plugin Version', 'wordfence') => '$data.newVersion',
31
+ null,
32
+ __('Details', 'wordfence') => '$longMsg',
33
+ __('Vulnerability Status', 'wordfence') => array('$data.vulnerable', __('Update includes security-related fixes.', 'wordfence')),
34
+ null,
35
+ __('Plugin URL', 'wordfence') => '$data.PluginURI',
36
+ __('Repository URL', 'wordfence') => '$data.wpURL',
37
+ __('Vulnerability Information', 'wordfence') => '$data.vulnerabilityLink',
38
+ ),
39
  ))->render();
views/scanner/issue-wfPluginVulnerable.php CHANGED
@@ -21,5 +21,16 @@ echo wfView::create('scanner/issue-base', array(
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
  '<a href="' . esc_url(network_admin_url('plugins.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-manage-plugins">' . __('Manage Plugins', 'wordfence') . '</a>',
24
- )
 
 
 
 
 
 
 
 
 
 
 
25
  ))->render();
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
  '<a href="' . esc_url(network_admin_url('plugins.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-manage-plugins">' . __('Manage Plugins', 'wordfence') . '</a>',
24
+ ),
25
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
26
+ 'textOutputDetailPairs' => array(
27
+ __('Plugin Name', 'wordfence') => '$data.Name',
28
+ __('Current Plugin Version', 'wordfence') => '$data.Version',
29
+ null,
30
+ __('Details', 'wordfence') => '$longMsg',
31
+ null,
32
+ __('Plugin URL', 'wordfence') => '$data.PluginURI',
33
+ __('Repository URL', 'wordfence') => '$data.wpURL',
34
+ __('Vulnerability Information', 'wordfence') => '$data.vulnerabilityLink',
35
+ ),
36
  ))->render();
views/scanner/issue-wfThemeUpgrade.php CHANGED
@@ -21,5 +21,17 @@ echo wfView::create('scanner/issue-base', array(
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
  '<a href="' . esc_url(network_admin_url('update-core.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-view-updates">' . __('View Updates', 'wordfence') . '</a>',
24
- )
 
 
 
 
 
 
 
 
 
 
 
 
25
  ))->render();
21
  'detailControls' => array(
22
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
23
  '<a href="' . esc_url(network_admin_url('update-core.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-view-updates">' . __('View Updates', 'wordfence') . '</a>',
24
+ ),
25
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
26
+ 'textOutputDetailPairs' => array(
27
+ __('Theme Name', 'wordfence') => '$data.name',
28
+ __('Current Theme Version', 'wordfence') => '$data.version',
29
+ __('New Theme Version', 'wordfence') => '$data.newVersion',
30
+ null,
31
+ __('Details', 'wordfence') => '$longMsg',
32
+ __('Vulnerability Status', 'wordfence') => array('$data.vulnerable', __('Update includes security-related fixes.', 'wordfence')),
33
+ null,
34
+ __('Theme URL', 'wordfence') => '$data.URL',
35
+ __('Vulnerability Information', 'wordfence') => '$data.vulnerabilityLink',
36
+ ),
37
  ))->render();
views/scanner/issue-wfUpgrade.php CHANGED
@@ -19,5 +19,15 @@ echo wfView::create('scanner/issue-base', array(
19
  'detailControls' => array(
20
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
21
  '<a href="' . esc_url(network_admin_url('update-core.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-view-updates">' . __('View Updates', 'wordfence') . '</a>',
22
- )
 
 
 
 
 
 
 
 
 
 
23
  ))->render();
19
  'detailControls' => array(
20
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-mark-fixed">' . __('Mark as Fixed', 'wordfence') . '</a>',
21
  '<a href="' . esc_url(network_admin_url('update-core.php')) . '" class="wf-btn wf-btn-default wf-btn-callout-subtle wf-issue-control-view-updates">' . __('View Updates', 'wordfence') . '</a>',
22
+ ),
23
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
24
+ 'textOutputDetailPairs' => array(
25
+ __('Current WordPress Version', 'wordfence') => '$data.currentVersion',
26
+ __('New WordPress Version', 'wordfence') => '$data.newVersion',
27
+ null,
28
+ __('Details', 'wordfence') => '$longMsg',
29
+ __('Vulnerability Status', 'wordfence') => array('$data.vulnerable', __('Update includes security-related fixes.', 'wordfence')),
30
+ null,
31
+ __('Vulnerability Information', 'wordfence') => '$data.vulnerabilityLink',
32
+ ),
33
  ))->render();
views/scanner/issue-wpscan_directoryList.php CHANGED
@@ -17,5 +17,11 @@ echo wfView::create('scanner/issue-base', array(
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteFile(\'${id}\'); return false;">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.updateIssueStatus(\'${id}\', \'delete\'); return false;">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
- )
 
 
 
 
 
 
21
  ))->render();
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteFile(\'${id}\'); return false;">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.updateIssueStatus(\'${id}\', \'delete\'); return false;">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
+ ),
21
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
22
+ 'textOutputDetailPairs' => array(
23
+ __('URL', 'wordfence') => '$data.url',
24
+ null,
25
+ __('Details', 'wordfence') => '$longMsg',
26
+ ),
27
  ))->render();
views/scanner/issue-wpscan_fullPathDiscl.php CHANGED
@@ -17,5 +17,11 @@ echo wfView::create('scanner/issue-base', array(
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteFile(\'${id}\'); return false;">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.updateIssueStatus(\'${id}\', \'delete\'); return false;">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
- )
 
 
 
 
 
 
21
  ))->render();
17
  '{{if data.fileExists}}<a target="_blank" class="wf-btn wf-btn-default wf-btn-callout-subtle" rel="noopener noreferrer" href="${WFAD.makeViewFileLink(data.file)}">' . __('View File', 'wordfence') . '</a>{{/if}}',
18
  '{{if data.canDelete}}<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.deleteFile(\'${id}\'); return false;">' . __('Delete File', 'wordfence') . '</a>{{/if}}',
19
  '<a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.updateIssueStatus(\'${id}\', \'delete\'); return false;">' . __('Mark as Fixed', 'wordfence') . '</a>',
20
+ ),
21
+ 'textOutput' => (isset($textOutput) ? $textOutput : null),
22
+ 'textOutputDetailPairs' => array(
23
+ __('URL', 'wordfence') => '$data.url',
24
+ null,
25
+ __('Details', 'wordfence') => '$longMsg',
26
+ ),
27
  ))->render();
views/scanner/scan-failed.php CHANGED
@@ -18,7 +18,7 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
18
  <h4><?php _e('Scan Failed', 'wordfence'); ?></h4>
19
  <p><?php echo $messageHTML; ?></p>
20
  </li>
21
- <li class="wf-right">
22
  <a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.killScan(); return false;"><?php echo $buttonTitle; ?></a>
23
  </li>
24
  </ul>
18
  <h4><?php _e('Scan Failed', 'wordfence'); ?></h4>
19
  <p><?php echo $messageHTML; ?></p>
20
  </li>
21
+ <li class="wf-right wf-padding-add-left">
22
  <a href="#" class="wf-btn wf-btn-default wf-btn-callout-subtle" onclick="WFAD.killScan(); return false;"><?php echo $buttonTitle; ?></a>
23
  </li>
24
  </ul>
views/waf/options-group-basic-firewall.php CHANGED
@@ -333,8 +333,15 @@ if (!isset($collapseable)) {
333
  }
334
  }
335
 
 
336
  $('.wf-waf-backups').hide();
337
- $('.wf-waf-backups-' + el.val().replace(/[^a-z0-9\-]/i, '')).show();
 
 
 
 
 
 
338
 
339
  if (nginxNotice.length) { //Install only
340
  if (el.val() == 'nginx') {
@@ -405,7 +412,7 @@ if (!isset($collapseable)) {
405
  });
406
 
407
  if (window.location.hash) {
408
- var hashes = window.location.hash.split('#');
409
  for (var i = 0; i < hashes.length; i++) {
410
  if (hashes[i] == 'configureAutoPrepend') {
411
  $('#wf-waf-install').trigger('click');
@@ -419,7 +426,7 @@ if (!isset($collapseable)) {
419
  }
420
 
421
  $(window).on('hashchange', function () {
422
- var hashes = window.location.hash.split('#');
423
  for (var i = 0; i < hashes.length; i++) {
424
  if (hashes[i] == 'configureAutoPrepend') {
425
  $('#wf-waf-install').trigger('click');
333
  }
334
  }
335
 
336
+ var identifier = '.wf-waf-backups-' + el.val().replace(/[^a-z0-9\-]/i, '');
337
  $('.wf-waf-backups').hide();
338
+ $(identifier).show();
339
+ if ($(identifier).find('.wf-waf-backup-file-list').children().length > 0) {
340
+ $('.wf-waf-download-instructions').show();
341
+ }
342
+ else {
343
+ $('.wf-waf-download-instructions').hide();
344
+ }
345
 
346
  if (nginxNotice.length) { //Install only
347
  if (el.val() == 'nginx') {
412
  });
413
 
414
  if (window.location.hash) {
415
+ var hashes = WFAD.parseHashes();
416
  for (var i = 0; i < hashes.length; i++) {
417
  if (hashes[i] == 'configureAutoPrepend') {
418
  $('#wf-waf-install').trigger('click');
426
  }
427
 
428
  $(window).on('hashchange', function () {
429
+ var hashes = WFAD.parseHashes();
430
  for (var i = 0; i < hashes.length; i++) {
431
  if (hashes[i] == 'configureAutoPrepend') {
432
  $('#wf-waf-install').trigger('click');
views/waf/waf-install.php CHANGED
@@ -71,13 +71,21 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
71
  $class = preg_replace('/[^a-z0-9\-]/i', '', $optionValue);
72
  $helper = new wfWAFAutoPrependHelper($optionValue, null);
73
  $backups = $helper->getFilesNeededForBackup();
74
- $jsonBackups = json_encode(array_map('basename', $backups));
 
 
 
 
 
 
 
 
75
  ?>
76
  <div class="wf-waf-backups wf-waf-backups-<?php echo $class; ?>" style="display: none;" data-backups="<?php echo esc_attr($jsonBackups); ?>">
77
- <?php if (count($backups)): ?><p><?php _e('Please download a backup of the following files before we make the necessary changes:', 'wordfence'); ?></p><?php endif; ?>
78
  <ul class="wf-waf-backup-file-list">
79
  <?php
80
- foreach ($backups as $index => $backup) {
81
  echo '<li><a class="wf-btn wf-btn-default wf-waf-backup-download" data-backup-index="' . $index . '" href="' .
82
  esc_url(add_query_arg(array(
83
  'downloadBackup' => 1,
@@ -93,7 +101,7 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
93
  </div>
94
  <div class="wf-modal-footer">
95
  <ul class="wf-flex-horizontal wf-flex-full-width">
96
- <li><?php _e('Once you have downloaded the files, click Continue to complete the setup.', 'wordfence'); ?></li>
97
  <li class="wf-right"><a href="#" class="wf-btn wf-btn-primary wf-btn-callout-subtle wf-disabled" id="wf-waf-install-continue"><?php _e('Continue', 'wordfence'); ?></a></li>
98
  </ul>
99
  </div>
71
  $class = preg_replace('/[^a-z0-9\-]/i', '', $optionValue);
72
  $helper = new wfWAFAutoPrependHelper($optionValue, null);
73
  $backups = $helper->getFilesNeededForBackup();
74
+ $filteredBackups = array();
75
+ foreach ($backups as $index => $backup) {
76
+ if (!file_exists($backup)) {
77
+ continue;
78
+ }
79
+
80
+ $filteredBackups[$index] = $backup;
81
+ }
82
+ $jsonBackups = json_encode(array_map('basename', $filteredBackups));
83
  ?>
84
  <div class="wf-waf-backups wf-waf-backups-<?php echo $class; ?>" style="display: none;" data-backups="<?php echo esc_attr($jsonBackups); ?>">
85
+ <?php if (count($filteredBackups)): ?><p><?php _e('Please download a backup of the following files before we make the necessary changes:', 'wordfence'); ?></p><?php endif; ?>
86
  <ul class="wf-waf-backup-file-list">
87
  <?php
88
+ foreach ($filteredBackups as $index => $backup) {
89
  echo '<li><a class="wf-btn wf-btn-default wf-waf-backup-download" data-backup-index="' . $index . '" href="' .
90
  esc_url(add_query_arg(array(
91
  'downloadBackup' => 1,
101
  </div>
102
  <div class="wf-modal-footer">
103
  <ul class="wf-flex-horizontal wf-flex-full-width">
104
+ <li class="wf-waf-download-instructions"><?php _e('Once you have downloaded the files, click Continue to complete the setup.', 'wordfence'); ?></li>
105
  <li class="wf-right"><a href="#" class="wf-btn wf-btn-primary wf-btn-callout-subtle wf-disabled" id="wf-waf-install-continue"><?php _e('Continue', 'wordfence'); ?></a></li>
106
  </ul>
107
  </div>
views/waf/waf-uninstall.php CHANGED
@@ -58,7 +58,6 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
58
  <select name='serverConfiguration' id='wf-waf-server-config'>
59
  <?php echo $wafPrependOptions; ?>
60
  </select>
61
- <p><?php _e('Please download a backup of the following files before we make the necessary changes:', 'wordfence'); ?></p>
62
  <?php
63
  $adminURL = network_admin_url('admin.php?page=WordfenceWAF&subpage=waf_options&action=removeAutoPrepend');
64
  $wfnonce = wp_create_nonce('wfWAFRemoveAutoPrepend');
@@ -67,12 +66,21 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
67
  $class = preg_replace('/[^a-z0-9\-]/i', '', $optionValue);
68
  $helper = new wfWAFAutoPrependHelper($optionValue, null);
69
  $backups = $helper->getFilesNeededForBackup();
70
- $jsonBackups = json_encode(array_map('basename', $backups));
 
 
 
 
 
 
 
 
71
  ?>
72
  <div class="wf-waf-backups wf-waf-backups-<?php echo $class; ?>" style="display: none;" data-backups="<?php echo esc_attr($jsonBackups); ?>">
 
73
  <ul class="wf-waf-backup-file-list">
74
  <?php
75
- foreach ($backups as $index => $backup) {
76
  echo '<li><a class="wf-btn wf-btn-default wf-waf-backup-download" data-backup-index="' . $index . '" href="' .
77
  esc_url(add_query_arg(array(
78
  'downloadBackup' => 1,
@@ -89,7 +97,7 @@ if (!defined('WORDFENCE_VERSION')) { exit; }
89
  </div>
90
  <div class="wf-modal-footer">
91
  <ul class="wf-flex-horizontal wf-flex-full-width">
92
- <li><?php _e('Once you have downloaded the files, click Continue to complete uninstallation.', 'wordfence'); ?></li>
93
  <li class="wf-right"><a href="#" class="wf-btn wf-btn-primary wf-btn-callout-subtle wf-disabled" id="wf-waf-uninstall-continue"><?php _e('Continue', 'wordfence'); ?></a></li>
94
  </ul>
95
  </div>
58
  <select name='serverConfiguration' id='wf-waf-server-config'>
59
  <?php echo $wafPrependOptions; ?>
60
  </select>
 
61
  <?php
62
  $adminURL = network_admin_url('admin.php?page=WordfenceWAF&subpage=waf_options&action=removeAutoPrepend');
63
  $wfnonce = wp_create_nonce('wfWAFRemoveAutoPrepend');
66
  $class = preg_replace('/[^a-z0-9\-]/i', '', $optionValue);
67
  $helper = new wfWAFAutoPrependHelper($optionValue, null);
68
  $backups = $helper->getFilesNeededForBackup();
69
+ $filteredBackups = array();
70
+ foreach ($backups as $index => $backup) {
71
+ if (!file_exists($backup)) {
72
+ continue;
73
+ }
74
+
75
+ $filteredBackups[$index] = $backup;
76
+ }
77
+ $jsonBackups = json_encode(array_map('basename', $filteredBackups));
78
  ?>
79
  <div class="wf-waf-backups wf-waf-backups-<?php echo $class; ?>" style="display: none;" data-backups="<?php echo esc_attr($jsonBackups); ?>">
80
+ <?php if (count($filteredBackups)): ?><p><?php _e('Please download a backup of the following files before we make the necessary changes:', 'wordfence'); ?></p><?php endif; ?>
81
  <ul class="wf-waf-backup-file-list">
82
  <?php
83
+ foreach ($filteredBackups as $index => $backup) {
84
  echo '<li><a class="wf-btn wf-btn-default wf-waf-backup-download" data-backup-index="' . $index . '" href="' .
85
  esc_url(add_query_arg(array(
86
  'downloadBackup' => 1,
97
  </div>
98
  <div class="wf-modal-footer">
99
  <ul class="wf-flex-horizontal wf-flex-full-width">
100
+ <li class="wf-waf-download-instructions"><?php _e('Once you have downloaded the files, click Continue to complete uninstallation.', 'wordfence'); ?></li>
101
  <li class="wf-right"><a href="#" class="wf-btn wf-btn-primary wf-btn-callout-subtle wf-disabled" id="wf-waf-uninstall-continue"><?php _e('Continue', 'wordfence'); ?></a></li>
102
  </ul>
103
  </div>
wordfence.php CHANGED
@@ -4,15 +4,15 @@ 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.1.2
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '7.1.2');
15
- define('WORDFENCE_BUILD_NUMBER', '1522855379');
16
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
17
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
18
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
+ Version: 7.1.3
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '7.1.3');
15
+ define('WORDFENCE_BUILD_NUMBER', '1523983877');
16
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
17
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
18